From e0043e17dfc52fe1702746543127cb4a87232bcd Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Sun, 27 Dec 2015 16:30:02 +0000 Subject: [PATCH] Fix linux personality syscall wrapper 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: Likewise. * sysdeps/unix/sysv/linux/tst-personality.c: Likewise. * 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. --- ChangeLog | 25 ++++++++++ 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 + .../unix/sysv/linux/microblaze/syscalls.list | 1 + .../sysv/linux/mips/mips64/n32/syscalls.list | 2 + sysdeps/unix/sysv/linux/personality.c | 49 +++++++++++++++++++ .../sysv/linux/s390/s390-32/syscalls.list | 1 + sysdeps/unix/sysv/linux/sh/syscalls.list | 2 + .../sysv/linux/sparc/sparc64/personality.c | 3 ++ sysdeps/unix/sysv/linux/syscalls.list | 1 - sysdeps/unix/sysv/linux/tst-personality.c | 45 +++++++++++++++++ .../unix/sysv/linux/wordsize-64/syscalls.list | 1 + .../unix/sysv/linux/x86_64/x32/syscalls.list | 1 + 16 files changed, 139 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/ChangeLog b/ChangeLog index 85d39d6107..51a055c825 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +2015-12-30 Dmitry V. Levin + + [BZ #19408] + * sysdeps/unix/sysv/linux/personality.c: New file. + * sysdeps/unix/sysv/linux/sparc/sparc64/personality.c: Likewise. + * sysdeps/unix/sysv/linux/tst-personality.c: Likewise. + * 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. + 2015-12-30 Aurelien Jarno * sysdeps/unix/sysv/linux/arm/ioperm.c: Do not include . diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile index f6269ea3d8..9999600633 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 911ed7d2fa..c06954f824 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 2cb8d027c3..d29c35866d 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 1cebd6a50f..6282ff8e2f 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 ad4ca46b50..4260f3e55f 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 86fd40bd15..da0fd4ec98 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 7ad55231f6..584ad1fcba 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 0000000000..6724f1b2a6 --- /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 82baf9cf36..141b16523d 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 2f4ac6548d..169d40f074 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 0000000000..250e501d91 --- /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 caa6ccfbb1..afaf0337bd 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 0000000000..1bfa5029df --- /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 51ee8d8239..19cc6d9833 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 2cc58af2df..c98ac74d46 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