Fix linux personality syscall wrapper
authorDmitry V. Levin <ldv@altlinux.org>
Sun, 27 Dec 2015 16:30:02 +0000 (16:30 +0000)
committerDmitry V. Levin <ldv@altlinux.org>
Thu, 31 Dec 2015 00:17:48 +0000 (00:17 +0000)
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.

16 files changed:
ChangeLog
sysdeps/unix/sysv/linux/Makefile
sysdeps/unix/sysv/linux/arm/syscalls.list
sysdeps/unix/sysv/linux/hppa/syscalls.list
sysdeps/unix/sysv/linux/i386/syscalls.list
sysdeps/unix/sysv/linux/m68k/syscalls.list
sysdeps/unix/sysv/linux/microblaze/syscalls.list
sysdeps/unix/sysv/linux/mips/mips64/n32/syscalls.list
sysdeps/unix/sysv/linux/personality.c [new file with mode: 0644]
sysdeps/unix/sysv/linux/s390/s390-32/syscalls.list
sysdeps/unix/sysv/linux/sh/syscalls.list
sysdeps/unix/sysv/linux/sparc/sparc64/personality.c [new file with mode: 0644]
sysdeps/unix/sysv/linux/syscalls.list
sysdeps/unix/sysv/linux/tst-personality.c [new file with mode: 0644]
sysdeps/unix/sysv/linux/wordsize-64/syscalls.list
sysdeps/unix/sysv/linux/x86_64/x32/syscalls.list

index 85d39d6..51a055c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2015-12-30  Dmitry V. Levin  <ldv@altlinux.org>
+
+       [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  <aurelien@aurel32.net>
 
        * sysdeps/unix/sysv/linux/arm/ioperm.c: Do not include <string.h>.
index f6269ea..9999600 100644 (file)
@@ -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).
 
index 911ed7d..c06954f 100644 (file)
@@ -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
index 2cb8d02..d29c358 100644 (file)
@@ -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
index 1cebd6a..6282ff8 100644 (file)
@@ -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
index ad4ca46..4260f3e 100644 (file)
@@ -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
index 86fd40b..da0fd4e 100644 (file)
@@ -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).
index 7ad5523..584ad1f 100644 (file)
@@ -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 (file)
index 0000000..6724f1b
--- /dev/null
@@ -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
+   <http://www.gnu.org/licenses/>.  */
+
+#include <sys/personality.h>
+#include <sysdep.h>
+
+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)
index 82baf9c..141b165 100644 (file)
@@ -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
index 2f4ac65..169d40f 100644 (file)
@@ -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 (file)
index 0000000..250e501
--- /dev/null
@@ -0,0 +1,3 @@
+/* Work around sign extension bug in the kernel.  */
+#define PERSONALITY_TRUNCATE_ARGUMENT
+#include <sysdeps/unix/sysv/linux/personality.c>
index caa6ccf..afaf033 100644 (file)
@@ -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 (file)
index 0000000..1bfa502
--- /dev/null
@@ -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
+   <http://www.gnu.org/licenses/>.  */
+
+#include <errno.h>
+#include <sys/personality.h>
+
+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"
index 51ee8d8..19cc6d9 100644 (file)
@@ -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
index 2cc58af..c98ac74 100644 (file)
@@ -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