From patchwork Wed Dec 30 11:36:14 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Dmitry V. Levin" X-Patchwork-Id: 10179 X-Patchwork-Delegate: ldv@altlinux.org Received: (qmail 30738 invoked by alias); 30 Dec 2015 11:36:21 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 30706 invoked by uid 89); 30 Dec 2015 11:36:20 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=0.5 required=5.0 tests=AWL, BAYES_99, BAYES_999, KAM_LAZY_DOMAIN_SECURITY, RCVD_IN_DNSWL_LOW, RP_MATCHES_RCVD autolearn=no version=3.3.2 spammy=204, 65, pause, 427 X-HELO: pegasus3.altlinux.org Date: Wed, 30 Dec 2015 14:36:14 +0300 From: "Dmitry V. Levin" To: libc-alpha@sourceware.org Subject: [PATCH v2] Fix linux personality syscall wrapper Message-ID: <20151230113614.GA11943@altlinux.org> Mail-Followup-To: libc-alpha@sourceware.org References: <20151228010929.GA1819@altlinux.org> <20151229214218.GB25803@vapier.lan> <20151229232721.GA3737@altlinux.org> <20151229235007.GQ25803@vapier.lan> <20151230002622.GC3737@altlinux.org> <20151230030419.GT25803@vapier.lan> Mime-Version: 1.0 Content-Disposition: inline In-Reply-To: <20151230030419.GT25803@vapier.lan> The personality system call, starting with linux kernel commit v2.6.29-6609-g11d06b2a1e5658f448a308aa3beb97bacd64a940, always successfully changes the personality if requested. The syscall wrapper, however, still can return an error in the following cases: - the value returned by the system call looks like an error due to architecture limitations of 32-bit kernels; - a personality greater than 0xffffffff is passed to the system call, and the 64-bit kernel does not have commit v2.6.35-rc1-372-g485d527686850d68a0e9006dd9904f19f122485e that would truncate this value to unsigned int; - on sparc64, the value returned by the system call looks like an error due to sparc64 kernel sign extension bug. The solution is three-fold: - move generic syscalls.list personality entry to generic 64-bit syscalls.list file; - for each 32-bit architecture that use negated errno semantics, add a NOERRNO personality entry to their syscalls.list file; - for sparc64 and 32-bit architectures that use dedicated registers to flag syscall errors, add a wrapper around personality syscall; if the system call return value is flagged as an error, this wrapper returns the negated "would be errno" value, otherwise it returns the system call return value; on sparc64, it also truncates the personality argument to unsigned int before passing it to the kernel. [BZ #19408] * sysdeps/unix/sysv/linux/personality.c: New file. * sysdeps/unix/sysv/linux/sparc/sparc64/personality.c: New file. * sysdeps/unix/sysv/linux/tst-personality.c: New file. * sysdeps/unix/sysv/linux/Makefile [$(subdir) == misc] (sysdep_routines): Add personality. (tests): Add tst-personality. * sysdeps/unix/sysv/linux/syscalls.list (personality): Move ... * sysdeps/unix/sysv/linux/wordsize-64/syscalls.list: ... here. * sysdeps/unix/sysv/linux/arm/syscalls.list (personality): New entry. * sysdeps/unix/sysv/linux/hppa/syscalls.list (personality): Likewise. * sysdeps/unix/sysv/linux/i386/syscalls.list (personality): Likewise. * sysdeps/unix/sysv/linux/m68k/syscalls.list (personality): Likewise. * sysdeps/unix/sysv/linux/microblaze/syscalls.list (personality): Likewise. * sysdeps/unix/sysv/linux/mips/mips64/n32/syscalls.list (personality): Likewise. * sysdeps/unix/sysv/linux/s390/s390-32/syscalls.list (personality): Likewise. * sysdeps/unix/sysv/linux/sh/syscalls.list (personality): Likewise. * sysdeps/unix/sysv/linux/x86_64/x32/syscalls.list (personality): Likewise. --- v2: - tst-personality.c: test that errno is unaffected by personality calls; - personality.c: clarify comment, by Mike Frysinger; sysdeps/unix/sysv/linux/Makefile | 5 ++- sysdeps/unix/sysv/linux/arm/syscalls.list | 2 + sysdeps/unix/sysv/linux/hppa/syscalls.list | 1 + sysdeps/unix/sysv/linux/i386/syscalls.list | 2 + sysdeps/unix/sysv/linux/m68k/syscalls.list | 1 + sysdeps/unix/sysv/linux/microblaze/syscalls.list | 1 + .../unix/sysv/linux/mips/mips64/n32/syscalls.list | 2 + sysdeps/unix/sysv/linux/personality.c | 49 ++++++++++++++++++++++ sysdeps/unix/sysv/linux/s390/s390-32/syscalls.list | 1 + sysdeps/unix/sysv/linux/sh/syscalls.list | 2 + .../unix/sysv/linux/sparc/sparc64/personality.c | 3 ++ sysdeps/unix/sysv/linux/syscalls.list | 1 - sysdeps/unix/sysv/linux/tst-personality.c | 45 ++++++++++++++++++++ sysdeps/unix/sysv/linux/wordsize-64/syscalls.list | 1 + sysdeps/unix/sysv/linux/x86_64/x32/syscalls.list | 1 + 15 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 sysdeps/unix/sysv/linux/personality.c create mode 100644 sysdeps/unix/sysv/linux/sparc/sparc64/personality.c create mode 100644 sysdeps/unix/sysv/linux/tst-personality.c diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index f6269ea..9999600 100644 --- a/sysdeps/unix/sysv/linux/Makefile +++ b/sysdeps/unix/sysv/linux/Makefile @@ -16,7 +16,8 @@ include $(firstword $(wildcard $(sysdirs:=/sysctl.mk))) sysdep_routines += clone llseek umount umount2 readahead \ setfsuid setfsgid makedev epoll_pwait signalfd \ - eventfd eventfd_read eventfd_write prlimit + eventfd eventfd_read eventfd_write prlimit \ + personality CFLAGS-gethostid.c = -fexceptions CFLAGS-tst-writev.c += "-DARTIFICIAL_LIMIT=0x80000000-__getpagesize()" @@ -41,7 +42,7 @@ sysdep_headers += sys/mount.h sys/acct.h sys/sysctl.h \ bits/socket_type.h bits/syscall.h bits/sysctl.h \ bits/mman-linux.h -tests += tst-clone tst-fanotify +tests += tst-clone tst-fanotify tst-personality # Generate the list of SYS_* macros for the system calls (__NR_* macros). diff --git a/sysdeps/unix/sysv/linux/arm/syscalls.list b/sysdeps/unix/sysv/linux/arm/syscalls.list index 911ed7d..c06954f 100644 --- a/sysdeps/unix/sysv/linux/arm/syscalls.list +++ b/sysdeps/unix/sysv/linux/arm/syscalls.list @@ -19,6 +19,8 @@ prlimit64 EXTRA prlimit64 i:iipp prlimit64 fanotify_mark EXTRA fanotify_mark i:iiiiis fanotify_mark +personality EXTRA personality Ei:i __personality personality + # Semaphore and shm system calls. msgctl, shmctl, and semctl have C # wrappers (to set __IPC_64). msgget - msgget i:ii __msgget msgget diff --git a/sysdeps/unix/sysv/linux/hppa/syscalls.list b/sysdeps/unix/sysv/linux/hppa/syscalls.list index 2cb8d02..d29c358 100644 --- a/sysdeps/unix/sysv/linux/hppa/syscalls.list +++ b/sysdeps/unix/sysv/linux/hppa/syscalls.list @@ -37,3 +37,4 @@ setrlimit - setrlimit i:ip __setrlimit setrlimit getrlimit - getrlimit i:ip __getrlimit getrlimit prlimit64 EXTRA prlimit64 i:iipp __prlimit64 prlimit64@@GLIBC_2.17 fanotify_mark EXTRA fanotify_mark i:iiiiis __fanotify_mark fanotify_mark@@GLIBC_2.19 +personality EXTRA personality Ei:i __personality personality diff --git a/sysdeps/unix/sysv/linux/i386/syscalls.list b/sysdeps/unix/sysv/linux/i386/syscalls.list index 1cebd6a..6282ff8 100644 --- a/sysdeps/unix/sysv/linux/i386/syscalls.list +++ b/sysdeps/unix/sysv/linux/i386/syscalls.list @@ -25,3 +25,5 @@ waitpid - waitpid Ci:ipi __waitpid waitpid prlimit64 EXTRA prlimit64 i:iipp prlimit64 fanotify_mark EXTRA fanotify_mark i:iiiiis fanotify_mark + +personality EXTRA personality Ei:i __personality personality diff --git a/sysdeps/unix/sysv/linux/m68k/syscalls.list b/sysdeps/unix/sysv/linux/m68k/syscalls.list index ad4ca46..4260f3e 100644 --- a/sysdeps/unix/sysv/linux/m68k/syscalls.list +++ b/sysdeps/unix/sysv/linux/m68k/syscalls.list @@ -19,3 +19,4 @@ setfsuid - setfsuid32 Ei:i setfsuid cacheflush EXTRA cacheflush i:iiii __cacheflush cacheflush prlimit64 EXTRA prlimit64 i:iipp prlimit64 fanotify_mark EXTRA fanotify_mark i:iiiiis fanotify_mark +personality EXTRA personality Ei:i __personality personality diff --git a/sysdeps/unix/sysv/linux/microblaze/syscalls.list b/sysdeps/unix/sysv/linux/microblaze/syscalls.list index 86fd40b..da0fd4e 100644 --- a/sysdeps/unix/sysv/linux/microblaze/syscalls.list +++ b/sysdeps/unix/sysv/linux/microblaze/syscalls.list @@ -4,6 +4,7 @@ cacheflush EXTRA cacheflush i:iiii __cacheflush cacheflush prlimit64 EXTRA prlimit64 i:iipp prlimit64 fanotify_mark EXTRA fanotify_mark i:iiiiis fanotify_mark +personality EXTRA personality Ei:i __personality personality # Semaphore and shm system calls. msgctl, shmctl, and semctl have C # wrappers (to set __IPC_64). diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/syscalls.list b/sysdeps/unix/sysv/linux/mips/mips64/n32/syscalls.list index 7ad5523..584ad1f 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/syscalls.list +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/syscalls.list @@ -6,3 +6,5 @@ sync_file_range - sync_file_range Ci:iiii sync_file_range prlimit64 EXTRA prlimit64 i:iipp prlimit64 fanotify_mark EXTRA fanotify_mark i:iiiis fanotify_mark + +personality EXTRA personality Ei:i __personality personality diff --git a/sysdeps/unix/sysv/linux/personality.c b/sysdeps/unix/sysv/linux/personality.c new file mode 100644 index 0000000..6724f1b --- /dev/null +++ b/sysdeps/unix/sysv/linux/personality.c @@ -0,0 +1,49 @@ +/* Copyright (C) 2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +extern __typeof (personality) __personality; + +int +__personality (unsigned long persona) +{ +#ifdef PERSONALITY_TRUNCATE_ARGUMENT + /* Starting with kernel commit v2.6.21-3117-g97dc32c, the type of + task_struct->pesonality is "unsigned int". + Starting with kernel commit v2.6.35-rc1-372-g485d527, the personality + syscall accepts "unsigned int" instead of "long unsigned int". + Inbetween, a personality argument that does not fit into "unsigned int" + would result to system call returning -EINVAL. + We explicitly truncate the personality argument to "unsigned int" + to eliminate the uncertainty. */ + persona = (unsigned int) persona; +#endif + + INTERNAL_SYSCALL_DECL (err); + long ret = INTERNAL_SYSCALL (personality, err, 1, persona); + + /* Starting with kernel commit v2.6.29-6609-g11d06b2, the personality syscall + never fails. However, 32-bit kernels might flag valid values as errors, so + we need to reverse the error setting. We can't use the raw result as some + arches split the return/error values. */ + if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (ret, err))) + ret = -INTERNAL_SYSCALL_ERRNO (ret, err); + return ret; +} +weak_alias (__personality, personality) diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/syscalls.list b/sysdeps/unix/sysv/linux/s390/s390-32/syscalls.list index 82baf9c..141b165 100644 --- a/sysdeps/unix/sysv/linux/s390/s390-32/syscalls.list +++ b/sysdeps/unix/sysv/linux/s390/s390-32/syscalls.list @@ -20,3 +20,4 @@ setrlimit - setrlimit i:ip __setrlimit setrlimit@GLIBC_2.0 setrlimit@@GLIBC_2.2 prlimit64 EXTRA prlimit64 i:iipp prlimit64 fanotify_mark EXTRA fanotify_mark i:iiiiis fanotify_mark +personality EXTRA personality Ei:i __personality personality diff --git a/sysdeps/unix/sysv/linux/sh/syscalls.list b/sysdeps/unix/sysv/linux/sh/syscalls.list index 2f4ac65..169d40f 100644 --- a/sysdeps/unix/sysv/linux/sh/syscalls.list +++ b/sysdeps/unix/sysv/linux/sh/syscalls.list @@ -20,3 +20,5 @@ waitpid - waitpid Ci:ipi __waitpid waitpid prlimit64 EXTRA prlimit64 i:iipp prlimit64 fanotify_mark EXTRA fanotify_mark i:iiiiis __fanotify_mark fanotify_mark@@GLIBC_2.16 + +personality EXTRA personality Ei:i __personality personality diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/personality.c b/sysdeps/unix/sysv/linux/sparc/sparc64/personality.c new file mode 100644 index 0000000..250e501 --- /dev/null +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/personality.c @@ -0,0 +1,3 @@ +/* Work around sign extension bug in the kernel. */ +#define PERSONALITY_TRUNCATE_ARGUMENT +#include diff --git a/sysdeps/unix/sysv/linux/syscalls.list b/sysdeps/unix/sysv/linux/syscalls.list index caa6ccf..afaf033 100644 --- a/sysdeps/unix/sysv/linux/syscalls.list +++ b/sysdeps/unix/sysv/linux/syscalls.list @@ -46,7 +46,6 @@ munlockall - munlockall i: munlockall nanosleep - nanosleep Ci:pp __nanosleep nanosleep nfsservctl EXTRA nfsservctl i:ipp nfsservctl pause - pause Ci: __libc_pause pause -personality EXTRA personality i:i __personality personality pipe - pipe i:f __pipe pipe pipe2 - pipe2 i:fi __pipe2 pipe2 pivot_root EXTRA pivot_root i:ss pivot_root diff --git a/sysdeps/unix/sysv/linux/tst-personality.c b/sysdeps/unix/sysv/linux/tst-personality.c new file mode 100644 index 0000000..1bfa502 --- /dev/null +++ b/sysdeps/unix/sysv/linux/tst-personality.c @@ -0,0 +1,45 @@ +/* BZ #19408 linux personality syscall wrapper test. + + Copyright (C) 2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include + +static int +do_test (void) +{ + int rc = 0; + unsigned int test_persona = -EINVAL; + unsigned int saved_persona; + + errno = 0xdefaced; + saved_persona = personality (0xffffffff); + + if (personality (test_persona) != saved_persona || + personality (0xffffffff) == -1 || + personality (PER_LINUX) == -1 || + personality (0xffffffff) != PER_LINUX || + 0xdefaced != errno) + rc = 1; + + (void) personality (saved_persona); + return rc; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/sysdeps/unix/sysv/linux/wordsize-64/syscalls.list b/sysdeps/unix/sysv/linux/wordsize-64/syscalls.list index 51ee8d8..19cc6d9 100644 --- a/sysdeps/unix/sysv/linux/wordsize-64/syscalls.list +++ b/sysdeps/unix/sysv/linux/wordsize-64/syscalls.list @@ -20,3 +20,4 @@ open - open Ci:siv __libc_open __open open __open64 open64 prlimit EXTRA prlimit64 i:iipp prlimit prlimit64 fanotify_mark EXTRA fanotify_mark i:iiiis fanotify_mark +personality EXTRA personality i:i __personality personality diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/syscalls.list b/sysdeps/unix/sysv/linux/x86_64/x32/syscalls.list index 2cc58af..c98ac74 100644 --- a/sysdeps/unix/sysv/linux/x86_64/x32/syscalls.list +++ b/sysdeps/unix/sysv/linux/x86_64/x32/syscalls.list @@ -2,6 +2,7 @@ fallocate - fallocate Ci:iiii fallocate fallocate64 gettimeofday - gettimeofday:__vdso_gettimeofday@LINUX_2.6 i:pP __gettimeofday gettimeofday +personality EXTRA personality Ei:i __personality personality posix_fadvise - fadvise64 Vi:iiii posix_fadvise posix_fadvise64 preadv - preadv Ci:ipii preadv preadv64 pwritev - pwritev Ci:ipii pwritev pwritev64