This is my second draft of how we could modify the system call interface in Linux to support user space with 64-bit time_t in addition to the existing 32-bit time_t based interfaces. In order to avoid spamming people too much, I've picked the first half of the patches only now, and left some of the more complex ones for later.
If you are interested in the big picture, see http://git.kernel.org/cgit/linux/kernel/git/arnd/playground.git/log/?h=y2038... with currently 42 patches. That number may go up a bit over time, but only because I plan to split some of the patches into smaller chunks for review purposes, as opposed to adding more system calls.
As part 1 of the series only switches over half the system calls, I've left out the bits that changes the ARM and x86 architectures. There are 24 additional architectures that need to moved over to the new system calls once the basic patches get merged.
The main part I'm not sure about is how the header files should be structured in order to make it easy for every libc implementation to pick out the right definitions of the data structures I had to modify. Hopefully, people on libc-alpha can help me out here.
The overall approach I have taken here is as follows:
* Since 64-bit architectures already support two versions of each system call that passes a time value in order to run 32-bit user space, I'm not adding new system calls for the most part, but instead make 32-bit architectures use the same compat handlers that 64-bit architectures already use.
* The existing syscall numbers are modified to point to the compat_sys_* functions, and new numbers are assigned to point to the existing handlers that now deal with 64-bit time_t based structures
* This series does not touch any ioctl implementation, but should be otherwise complete regarding the system calls. We are still trying to find out exactly which ioctls are affected and have not come up with a complete list at this point. It's probably a good idea though to do at least the essential ioctls before merging the series, so we can have a better understanding of how it will be done in the end.
* Each data structure we need to modify gets a new definition with a __kernel_ prefix, e.g. struct __kernel_timespec to replace struct timespec. This keeps the new structures out of the user libc namespace, but still allows the structure to be integrated into other data structures, e.g. for ioctl.
* There is no #ifdef in the UAPI headers at this point that would check for the kind of user space that is in use. Unfortunately, I believe we will need to add that in order to do some of the particularly tricky ioctls later.
* At first, all system call implementations are modified, but this is done in a way that is not supposed to have any ABI-visible effect on existing architectures.
* After all system calls are converted, we can change one architecture at a time to select ARCH_HAS_COMPAT_TIME, and modify its system call table accordingly. In this version, I do it for ARM32, x86-32, and x86-64 for demonstration purposes.
* A follow-up series changes over all other architectures.
* The last patch in the series changes the CONFIG_COMPAT_TIME Kconfig symbol to be user visible. Disabling this symbol will get you a kernel that intentionally breaks support for old tasks in order to provide an interface that will survive 2038. This is meant mostly as a debugging help for now, to let people build a y2038 safe distro, but at some point in the 2030s, we should remove that option and all the compat handling.
Arnd Bergmann (19): compat: remove compat_printk initramfs: use vfs_stat/lstat directly y2038: introduce linux/compat_time.h header y2038: introduce CONFIG_COMPAT_TIME y2038: make linux/compat_time.h usable on 32-bit y2038: compile compat time code even when CONFIG_COMPAT is not set y2038: add compat_sys_rt_sigtimedwait variants y2038: introduce struct __kernel_timespec y2038: introduce struct __kernel_stat y2038: use __kernel_stat for sys_newstat syscalls y2038: introduce and use struct __kernel_rusage y2038: add compat_{get,put}_timespec64 y2038: add compat handling for sys_semtimedop y2038: use __kernel_timespec for sys_mq_timed{send,receive} y2038: introduce timespec64_to_jiffies y2038: use __kernel_timespec in sys_rt_sigtimedwait y2038: use __kernel_timespec in sys_futex y2038: introduce jiffies_to_timespec64 y2038: use __kernel_timespec in sys_sched_rr_get_interval
arch/Kconfig | 11 ++ arch/alpha/include/uapi/asm/stat.h | 4 + arch/alpha/kernel/osf_sys.c | 4 +- arch/arm/include/uapi/asm/stat.h | 2 + arch/arm64/include/asm/compat.h | 11 -- arch/avr32/include/uapi/asm/stat.h | 2 + arch/blackfin/include/uapi/asm/stat.h | 2 + arch/cris/include/uapi/asm/stat.h | 2 + arch/frv/include/uapi/asm/stat.h | 2 + arch/ia64/include/uapi/asm/stat.h | 4 + arch/m32r/include/uapi/asm/stat.h | 1 + arch/m68k/include/uapi/asm/stat.h | 2 + arch/mips/include/asm/compat.h | 11 -- arch/mips/include/uapi/asm/stat.h | 1 + arch/mn10300/include/uapi/asm/stat.h | 2 + arch/parisc/include/asm/compat.h | 11 -- arch/parisc/include/uapi/asm/stat.h | 1 + arch/powerpc/include/asm/compat.h | 11 -- arch/powerpc/include/uapi/asm/stat.h | 25 +++ arch/s390/include/asm/compat.h | 11 -- arch/s390/include/uapi/asm/stat.h | 24 +++ arch/sh/include/uapi/asm/stat.h | 2 + arch/sparc/include/asm/compat.h | 11 -- arch/sparc/include/uapi/asm/stat.h | 28 ++++ arch/tile/include/asm/compat.h | 11 -- arch/x86/include/asm/compat.h | 12 +- arch/x86/include/asm/ftrace.h | 2 +- arch/x86/include/asm/sys_ia32.h | 2 +- arch/x86/include/uapi/asm/stat.h | 49 ++++-- arch/x86/kernel/cpu/perf_event.c | 2 +- arch/xtensa/include/uapi/asm/stat.h | 2 + fs/Makefile | 1 + fs/compat.c | 26 ++- fs/stat.c | 12 +- include/linux/audit.h | 4 +- include/linux/compat.h | 232 +-------------------------- include/linux/compat_time.h | 285 +++++++++++++++++++++++++++++++++ include/linux/jiffies.h | 23 ++- include/linux/resource.h | 8 +- include/linux/signal.h | 3 +- include/linux/stat.h | 3 + include/linux/syscalls.h | 32 ++-- include/linux/thread_info.h | 2 +- include/linux/time64.h | 17 +- include/uapi/asm-generic/kernel_stat.h | 36 +++++ include/uapi/asm-generic/stat.h | 12 +- include/uapi/linux/resource.h | 32 ++++ include/uapi/linux/time.h | 17 ++ init/do_mounts.h | 22 +-- init/initramfs.c | 12 +- ipc/compat.c | 10 -- ipc/mqueue.c | 16 +- ipc/sem.c | 60 +++++-- ipc/syscall.c | 7 + kernel/Makefile | 1 + kernel/audit.h | 2 +- kernel/auditsc.c | 14 +- kernel/compat.c | 184 ++++++++++++++++++--- kernel/exit.c | 6 +- kernel/futex.c | 10 +- kernel/sched/core.c | 35 +++- kernel/signal.c | 13 +- kernel/sys.c | 23 +-- kernel/sysctl.c | 10 -- kernel/time/time.c | 45 ++++-- 65 files changed, 950 insertions(+), 530 deletions(-) create mode 100644 include/linux/compat_time.h create mode 100644 include/uapi/asm-generic/kernel_stat.h
After 7e8e385aaf6e ("x86/compat: Remove sys32_vm86_warning"), this function has become unused, so we can remove it as well.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- fs/compat.c | 14 -------------- include/linux/compat.h | 1 - kernel/sysctl.c | 10 ---------- 3 files changed, 25 deletions(-)
diff --git a/fs/compat.c b/fs/compat.c index 6fd272d455e4..7d78cde20fc3 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -54,20 +54,6 @@ #include <asm/ioctls.h> #include "internal.h"
-int compat_log = 1; - -int compat_printk(const char *fmt, ...) -{ - va_list ap; - int ret; - if (!compat_log) - return 0; - va_start(ap, fmt); - ret = vprintk(fmt, ap); - va_end(ap); - return ret; -} - /* * Not all architectures have sys_utime, so implement this in terms * of sys_utimes. diff --git a/include/linux/compat.h b/include/linux/compat.h index ab25814690bc..d533d06146a8 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -424,7 +424,6 @@ asmlinkage long compat_sys_settimeofday(struct compat_timeval __user *tv,
asmlinkage long compat_sys_adjtimex(struct compat_timex __user *utp);
-extern int compat_printk(const char *fmt, ...); extern void sigset_from_compat(sigset_t *set, const compat_sigset_t *compat); extern void sigset_to_compat(compat_sigset_t *compat, const sigset_t *set);
diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 2082b1a88fb9..4020ade0ce4d 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -105,7 +105,6 @@ extern unsigned int core_pipe_limit; extern int pid_max; extern int pid_max_min, pid_max_max; extern int percpu_pagelist_fraction; -extern int compat_log; extern int latencytop_enabled; extern int sysctl_nr_open_min, sysctl_nr_open_max; #ifndef CONFIG_MMU @@ -1043,15 +1042,6 @@ static struct ctl_table kern_table[] = { .extra1 = &neg_one, }, #endif -#ifdef CONFIG_COMPAT - { - .procname = "compat-log", - .data = &compat_log, - .maxlen = sizeof (int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, -#endif #ifdef CONFIG_RT_MUTEXES { .procname = "max_lock_depth",
sys_newlstat is a system call implementation that is meant for user space, and that copies kernel-internal data structure to the user format, which is not needed for in-kernel users.
Further, as we rearrange the system call implementation so we can extend it with 64-bit time_t, the prototype for sys_newlstat changes.
This changes the initramfs code to use vfs_lstat directly, to get it out of the way of the time_t changes, and make it slightly more efficient in the process. Along the same lines we also replace sys_stat and sys_stat64 with vfs_stat.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- init/do_mounts.h | 22 ++++------------------ init/initramfs.c | 12 ++++++------ 2 files changed, 10 insertions(+), 24 deletions(-)
diff --git a/init/do_mounts.h b/init/do_mounts.h index f5b978a9bb92..74e75bb20fe3 100644 --- a/init/do_mounts.h +++ b/init/do_mounts.h @@ -19,29 +19,15 @@ static inline int create_dev(char *name, dev_t dev) return sys_mknod(name, S_IFBLK|0600, new_encode_dev(dev)); }
-#if BITS_PER_LONG == 32 static inline u32 bstat(char *name) { - struct stat64 stat; - if (sys_stat64(name, &stat) != 0) + struct kstat stat; + if (vfs_stat(name, &stat) != 0) return 0; - if (!S_ISBLK(stat.st_mode)) + if (!S_ISBLK(stat.mode)) return 0; - if (stat.st_rdev != (u32)stat.st_rdev) - return 0; - return stat.st_rdev; -} -#else -static inline u32 bstat(char *name) -{ - struct stat stat; - if (sys_newstat(name, &stat) != 0) - return 0; - if (!S_ISBLK(stat.st_mode)) - return 0; - return stat.st_rdev; + return stat.rdev; } -#endif
#ifdef CONFIG_BLK_DEV_RAM
diff --git a/init/initramfs.c b/init/initramfs.c index ad1bd7787bbb..10c808e97023 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -311,10 +311,10 @@ static int __init maybe_link(void)
static void __init clean_path(char *path, umode_t fmode) { - struct stat st; + struct kstat st;
- if (!sys_newlstat(path, &st) && (st.st_mode ^ fmode) & S_IFMT) { - if (S_ISDIR(st.st_mode)) + if (!vfs_lstat(path, &st) && (st.mode ^ fmode) & S_IFMT) { + if (S_ISDIR(st.mode)) sys_rmdir(path); else sys_unlink(path); @@ -580,13 +580,13 @@ static void __init clean_rootfs(void) num = sys_getdents64(fd, dirp, BUF_SIZE); while (num > 0) { while (num > 0) { - struct stat st; + struct kstat st; int ret;
- ret = sys_newlstat(dirp->d_name, &st); + ret = vfs_lstat(dirp->d_name, &st); WARN_ON_ONCE(ret); if (!ret) { - if (S_ISDIR(st.st_mode)) + if (S_ISDIR(st.mode)) sys_rmdir(dirp->d_name); else sys_unlink(dirp->d_name);
We want to reuse all the compat syscall handling for native syscalls on 32-bit architectures when dealing with 32-bit time_t types.
This moves all time-related system call declarations from include/linux/compat.h to include/linux/compat_time.h, along with the associated data structures and typedefs.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- arch/arm64/include/asm/compat.h | 11 -- arch/mips/include/asm/compat.h | 11 -- arch/parisc/include/asm/compat.h | 11 -- arch/powerpc/include/asm/compat.h | 11 -- arch/s390/include/asm/compat.h | 11 -- arch/sparc/include/asm/compat.h | 11 -- arch/tile/include/asm/compat.h | 11 -- arch/x86/include/asm/compat.h | 12 +- arch/x86/include/asm/ftrace.h | 2 +- arch/x86/include/asm/sys_ia32.h | 2 +- arch/x86/kernel/cpu/perf_event.c | 2 +- include/linux/compat.h | 225 +-------------------------------- include/linux/compat_time.h | 255 ++++++++++++++++++++++++++++++++++++++ 13 files changed, 262 insertions(+), 313 deletions(-) create mode 100644 include/linux/compat_time.h
diff --git a/arch/arm64/include/asm/compat.h b/arch/arm64/include/asm/compat.h index 7fbed6919b54..4d0786a73a93 100644 --- a/arch/arm64/include/asm/compat.h +++ b/arch/arm64/include/asm/compat.h @@ -34,7 +34,6 @@
typedef u32 compat_size_t; typedef s32 compat_ssize_t; -typedef s32 compat_time_t; typedef s32 compat_clock_t; typedef s32 compat_pid_t; typedef u16 __compat_uid_t; @@ -66,16 +65,6 @@ typedef u32 compat_ulong_t; typedef u64 compat_u64; typedef u32 compat_uptr_t;
-struct compat_timespec { - compat_time_t tv_sec; - s32 tv_nsec; -}; - -struct compat_timeval { - compat_time_t tv_sec; - s32 tv_usec; -}; - struct compat_stat { #ifdef __AARCH64EB__ short st_dev; diff --git a/arch/mips/include/asm/compat.h b/arch/mips/include/asm/compat.h index c4bd54a7f5ce..6495b37a6fbc 100644 --- a/arch/mips/include/asm/compat.h +++ b/arch/mips/include/asm/compat.h @@ -13,7 +13,6 @@
typedef u32 compat_size_t; typedef s32 compat_ssize_t; -typedef s32 compat_time_t; typedef s32 compat_clock_t; typedef s32 compat_suseconds_t;
@@ -45,16 +44,6 @@ typedef u32 compat_ulong_t; typedef u64 compat_u64; typedef u32 compat_uptr_t;
-struct compat_timespec { - compat_time_t tv_sec; - s32 tv_nsec; -}; - -struct compat_timeval { - compat_time_t tv_sec; - s32 tv_usec; -}; - struct compat_stat { compat_dev_t st_dev; s32 st_pad1[3]; diff --git a/arch/parisc/include/asm/compat.h b/arch/parisc/include/asm/compat.h index 94710cfc1ce8..cc20c7b203f4 100644 --- a/arch/parisc/include/asm/compat.h +++ b/arch/parisc/include/asm/compat.h @@ -12,7 +12,6 @@
typedef u32 compat_size_t; typedef s32 compat_ssize_t; -typedef s32 compat_time_t; typedef s32 compat_clock_t; typedef s32 compat_pid_t; typedef u32 __compat_uid_t; @@ -39,16 +38,6 @@ typedef u32 compat_ulong_t; typedef u64 compat_u64; typedef u32 compat_uptr_t;
-struct compat_timespec { - compat_time_t tv_sec; - s32 tv_nsec; -}; - -struct compat_timeval { - compat_time_t tv_sec; - s32 tv_usec; -}; - struct compat_stat { compat_dev_t st_dev; /* dev_t is 32 bits on parisc */ compat_ino_t st_ino; /* 32 bits */ diff --git a/arch/powerpc/include/asm/compat.h b/arch/powerpc/include/asm/compat.h index b142b8e0ed9e..4f8901448eba 100644 --- a/arch/powerpc/include/asm/compat.h +++ b/arch/powerpc/include/asm/compat.h @@ -16,7 +16,6 @@
typedef u32 compat_size_t; typedef s32 compat_ssize_t; -typedef s32 compat_time_t; typedef s32 compat_clock_t; typedef s32 compat_pid_t; typedef u32 __compat_uid_t; @@ -44,16 +43,6 @@ typedef u32 compat_ulong_t; typedef u64 compat_u64; typedef u32 compat_uptr_t;
-struct compat_timespec { - compat_time_t tv_sec; - s32 tv_nsec; -}; - -struct compat_timeval { - compat_time_t tv_sec; - s32 tv_usec; -}; - struct compat_stat { compat_dev_t st_dev; compat_ino_t st_ino; diff --git a/arch/s390/include/asm/compat.h b/arch/s390/include/asm/compat.h index d350ed9d0fbb..91398bb0695c 100644 --- a/arch/s390/include/asm/compat.h +++ b/arch/s390/include/asm/compat.h @@ -50,7 +50,6 @@
typedef u32 compat_size_t; typedef s32 compat_ssize_t; -typedef s32 compat_time_t; typedef s32 compat_clock_t; typedef s32 compat_pid_t; typedef u16 __compat_uid_t; @@ -94,16 +93,6 @@ typedef struct { u32 gprs_high[NUM_GPRS]; } s390_compat_regs_high;
-struct compat_timespec { - compat_time_t tv_sec; - s32 tv_nsec; -}; - -struct compat_timeval { - compat_time_t tv_sec; - s32 tv_usec; -}; - struct compat_stat { compat_dev_t st_dev; u16 __pad1; diff --git a/arch/sparc/include/asm/compat.h b/arch/sparc/include/asm/compat.h index 830502fe62b4..c7e71a6d7232 100644 --- a/arch/sparc/include/asm/compat.h +++ b/arch/sparc/include/asm/compat.h @@ -10,7 +10,6 @@
typedef u32 compat_size_t; typedef s32 compat_ssize_t; -typedef s32 compat_time_t; typedef s32 compat_clock_t; typedef s32 compat_pid_t; typedef u16 __compat_uid_t; @@ -38,16 +37,6 @@ typedef u32 compat_ulong_t; typedef u64 compat_u64; typedef u32 compat_uptr_t;
-struct compat_timespec { - compat_time_t tv_sec; - s32 tv_nsec; -}; - -struct compat_timeval { - compat_time_t tv_sec; - s32 tv_usec; -}; - struct compat_stat { compat_dev_t st_dev; compat_ino_t st_ino; diff --git a/arch/tile/include/asm/compat.h b/arch/tile/include/asm/compat.h index c14e36f008c8..c3a326c9ae75 100644 --- a/arch/tile/include/asm/compat.h +++ b/arch/tile/include/asm/compat.h @@ -29,7 +29,6 @@ typedef u32 compat_ulong_t; typedef u32 compat_size_t; typedef s32 compat_ssize_t; typedef s32 compat_off_t; -typedef s32 compat_time_t; typedef s32 compat_clock_t; typedef u32 compat_ino_t; typedef u32 compat_caddr_t; @@ -59,16 +58,6 @@ typedef unsigned long compat_elf_greg_t; #define COMPAT_ELF_NGREG (sizeof(struct pt_regs) / sizeof(compat_elf_greg_t)) typedef compat_elf_greg_t compat_elf_gregset_t[COMPAT_ELF_NGREG];
-struct compat_timespec { - compat_time_t tv_sec; - s32 tv_nsec; -}; - -struct compat_timeval { - compat_time_t tv_sec; - s32 tv_usec; -}; - #define compat_stat stat #define compat_statfs statfs
diff --git a/arch/x86/include/asm/compat.h b/arch/x86/include/asm/compat.h index acdee09228b3..b8ce4e15b511 100644 --- a/arch/x86/include/asm/compat.h +++ b/arch/x86/include/asm/compat.h @@ -15,7 +15,6 @@
typedef u32 compat_size_t; typedef s32 compat_ssize_t; -typedef s32 compat_time_t; typedef s32 compat_clock_t; typedef s32 compat_pid_t; typedef u16 __compat_uid_t; @@ -43,16 +42,6 @@ typedef u32 compat_ulong_t; typedef u64 __attribute__((aligned(4))) compat_u64; typedef u32 compat_uptr_t;
-struct compat_timespec { - compat_time_t tv_sec; - s32 tv_nsec; -}; - -struct compat_timeval { - compat_time_t tv_sec; - s32 tv_usec; -}; - struct compat_stat { compat_dev_t st_dev; u16 __pad1; @@ -270,6 +259,7 @@ typedef struct user_regs_struct compat_elf_gregset_t; do { *(int *) (((void *) &((S)->pr_reg)) + PR_REG_SIZE(0)) = (V); } \ while (0)
+#undef COMPAT_USE_64BIT_TIME #define COMPAT_USE_64BIT_TIME \ (!!(task_pt_regs(current)->orig_ax & __X32_SYSCALL_BIT)) #else diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h index f45acad3c4b6..2c2a23cb716c 100644 --- a/arch/x86/include/asm/ftrace.h +++ b/arch/x86/include/asm/ftrace.h @@ -45,7 +45,7 @@ int ftrace_int3_handler(struct pt_regs *regs); #if !defined(__ASSEMBLY__) && !defined(COMPILE_OFFSETS)
#if defined(CONFIG_FTRACE_SYSCALLS) && defined(CONFIG_IA32_EMULATION) -#include <asm/compat.h> +#include <linux/compat.h>
/* * Because ia32 syscalls do not map to x86_64 syscall numbers diff --git a/arch/x86/include/asm/sys_ia32.h b/arch/x86/include/asm/sys_ia32.h index 82c34ee25a65..8527b26ad36f 100644 --- a/arch/x86/include/asm/sys_ia32.h +++ b/arch/x86/include/asm/sys_ia32.h @@ -16,7 +16,7 @@ #include <linux/linkage.h> #include <linux/types.h> #include <linux/signal.h> -#include <asm/compat.h> +#include <linux/compat.h> #include <asm/ia32.h>
/* ia32/sys_ia32.c */ diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c index 87848ebe2bb7..6649e07bfe66 100644 --- a/arch/x86/kernel/cpu/perf_event.c +++ b/arch/x86/kernel/cpu/perf_event.c @@ -2134,7 +2134,7 @@ static unsigned long get_segment_base(unsigned int segment)
#ifdef CONFIG_COMPAT
-#include <asm/compat.h> +#include <linux/compat.h>
static inline int perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry *entry) diff --git a/include/linux/compat.h b/include/linux/compat.h index d533d06146a8..41b0dae6203b 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -15,15 +15,12 @@ #include <linux/fs.h> #include <linux/aio_abi.h> /* for aio_context_t */ #include <linux/unistd.h> +#include <linux/compat_time.h>
#include <asm/compat.h> #include <asm/siginfo.h> #include <asm/signal.h>
-#ifndef COMPAT_USE_64BIT_TIME -#define COMPAT_USE_64BIT_TIME 0 -#endif - #ifndef __SC_DELOUSE #define __SC_DELOUSE(t,v) ((t)(unsigned long)(v)) #endif @@ -72,26 +69,9 @@ typedef struct compat_sigaltstack { typedef __compat_uid32_t compat_uid_t; typedef __compat_gid32_t compat_gid_t;
-typedef compat_ulong_t compat_aio_context_t; - struct compat_sel_arg_struct; struct rusage;
-struct compat_itimerspec { - struct compat_timespec it_interval; - struct compat_timespec it_value; -}; - -struct compat_utimbuf { - compat_time_t actime; - compat_time_t modtime; -}; - -struct compat_itimerval { - struct compat_timeval it_interval; - struct compat_timeval it_value; -}; - struct compat_tms { compat_clock_t tms_utime; compat_clock_t tms_stime; @@ -99,39 +79,6 @@ struct compat_tms { compat_clock_t tms_cstime; };
-struct compat_timex { - compat_uint_t modes; - compat_long_t offset; - compat_long_t freq; - compat_long_t maxerror; - compat_long_t esterror; - compat_int_t status; - compat_long_t constant; - compat_long_t precision; - compat_long_t tolerance; - struct compat_timeval time; - compat_long_t tick; - compat_long_t ppsfreq; - compat_long_t jitter; - compat_int_t shift; - compat_long_t stabil; - compat_long_t jitcnt; - compat_long_t calcnt; - compat_long_t errcnt; - compat_long_t stbcnt; - compat_int_t tai; - - compat_int_t:32; compat_int_t:32; compat_int_t:32; compat_int_t:32; - compat_int_t:32; compat_int_t:32; compat_int_t:32; compat_int_t:32; - compat_int_t:32; compat_int_t:32; compat_int_t:32; -}; - -#define _COMPAT_NSIG_WORDS (_COMPAT_NSIG / _COMPAT_NSIG_BPW) - -typedef struct { - compat_sigset_word sig[_COMPAT_NSIG_WORDS]; -} compat_sigset_t; - struct compat_sigaction { #ifndef __ARCH_HAS_IRIX_SIGACTION compat_uptr_t sa_handler; @@ -146,24 +93,6 @@ struct compat_sigaction { compat_sigset_t sa_mask __packed; };
-/* - * These functions operate on 32- or 64-bit specs depending on - * COMPAT_USE_64BIT_TIME, hence the void user pointer arguments. - */ -extern int compat_get_timespec(struct timespec *, const void __user *); -extern int compat_put_timespec(const struct timespec *, void __user *); -extern int compat_get_timeval(struct timeval *, const void __user *); -extern int compat_put_timeval(const struct timeval *, void __user *); - -/* - * This function convert a timespec if necessary and returns a *user - * space* pointer. If no conversion is necessary, it returns the - * initial pointer. NULL is a legitimate argument and will always - * output NULL. - */ -extern int compat_convert_timespec(struct timespec __user **, - const void __user *); - struct compat_iovec { compat_uptr_t iov_base; compat_size_t iov_len; @@ -174,28 +103,6 @@ struct compat_rlimit { compat_ulong_t rlim_max; };
-struct compat_rusage { - struct compat_timeval ru_utime; - struct compat_timeval ru_stime; - compat_long_t ru_maxrss; - compat_long_t ru_ixrss; - compat_long_t ru_idrss; - compat_long_t ru_isrss; - compat_long_t ru_minflt; - compat_long_t ru_majflt; - compat_long_t ru_nswap; - compat_long_t ru_inblock; - compat_long_t ru_oublock; - compat_long_t ru_msgsnd; - compat_long_t ru_msgrcv; - compat_long_t ru_nsignals; - compat_long_t ru_nvcsw; - compat_long_t ru_nivcsw; -}; - -extern int put_compat_rusage(const struct rusage *, - struct compat_rusage __user *); - struct compat_siginfo;
extern asmlinkage long compat_sys_waitid(int, compat_pid_t, @@ -324,8 +231,6 @@ asmlinkage long compat_sys_msgrcv(int msqid, compat_uptr_t msgp, compat_ssize_t msgsz, compat_long_t msgtyp, int msgflg); long compat_sys_msgctl(int first, int second, void __user *uptr); long compat_sys_shmctl(int first, int second, void __user *uptr); -long compat_sys_semtimedop(int semid, struct sembuf __user *tsems, - unsigned nsems, const struct compat_timespec __user *timeout); asmlinkage long compat_sys_keyctl(u32 option, u32 arg2, u32 arg3, u32 arg4, u32 arg5); asmlinkage long compat_sys_ustat(unsigned dev, struct compat_ustat __user *u32); @@ -361,12 +266,6 @@ asmlinkage long compat_sys_execveat(int dfd, const char __user *filename, const compat_uptr_t __user *argv, const compat_uptr_t __user *envp, int flags);
-asmlinkage long compat_sys_select(int n, compat_ulong_t __user *inp, - compat_ulong_t __user *outp, compat_ulong_t __user *exp, - struct compat_timeval __user *tvp); - -asmlinkage long compat_sys_old_select(struct compat_sel_arg_struct __user *arg); - asmlinkage long compat_sys_wait4(compat_pid_t pid, compat_uint_t __user *stat_addr, int options, struct compat_rusage __user *ru); @@ -392,38 +291,6 @@ asmlinkage long compat_sys_sigaction(int sig, struct compat_old_sigaction __user *oact); #endif
-static inline int compat_timeval_compare(struct compat_timeval *lhs, - struct compat_timeval *rhs) -{ - if (lhs->tv_sec < rhs->tv_sec) - return -1; - if (lhs->tv_sec > rhs->tv_sec) - return 1; - return lhs->tv_usec - rhs->tv_usec; -} - -static inline int compat_timespec_compare(struct compat_timespec *lhs, - struct compat_timespec *rhs) -{ - if (lhs->tv_sec < rhs->tv_sec) - return -1; - if (lhs->tv_sec > rhs->tv_sec) - return 1; - return lhs->tv_nsec - rhs->tv_nsec; -} - -extern int get_compat_itimerspec(struct itimerspec *dst, - const struct compat_itimerspec __user *src); -extern int put_compat_itimerspec(struct compat_itimerspec __user *dst, - const struct itimerspec *src); - -asmlinkage long compat_sys_gettimeofday(struct compat_timeval __user *tv, - struct timezone __user *tz); -asmlinkage long compat_sys_settimeofday(struct compat_timeval __user *tv, - struct timezone __user *tz); - -asmlinkage long compat_sys_adjtimex(struct compat_timex __user *utp); - extern void sigset_from_compat(sigset_t *set, const compat_sigset_t *compat); extern void sigset_to_compat(compat_sigset_t *compat, const sigset_t *set);
@@ -451,48 +318,20 @@ asmlinkage long compat_sys_epoll_pwait(int epfd, const compat_sigset_t __user *sigmask, compat_size_t sigsetsize);
-asmlinkage long compat_sys_utime(const char __user *filename, - struct compat_utimbuf __user *t); -asmlinkage long compat_sys_utimensat(unsigned int dfd, - const char __user *filename, - struct compat_timespec __user *t, - int flags); - -asmlinkage long compat_sys_time(compat_time_t __user *tloc); -asmlinkage long compat_sys_stime(compat_time_t __user *tptr); asmlinkage long compat_sys_signalfd(int ufd, const compat_sigset_t __user *sigmask, compat_size_t sigsetsize); -asmlinkage long compat_sys_timerfd_settime(int ufd, int flags, - const struct compat_itimerspec __user *utmr, - struct compat_itimerspec __user *otmr); -asmlinkage long compat_sys_timerfd_gettime(int ufd, - struct compat_itimerspec __user *otmr); - asmlinkage long compat_sys_move_pages(pid_t pid, compat_ulong_t nr_pages, __u32 __user *pages, const int __user *nodes, int __user *status, int flags); -asmlinkage long compat_sys_futimesat(unsigned int dfd, - const char __user *filename, - struct compat_timeval __user *t); -asmlinkage long compat_sys_utimes(const char __user *filename, - struct compat_timeval __user *t); -asmlinkage long compat_sys_newstat(const char __user *filename, - struct compat_stat __user *statbuf); -asmlinkage long compat_sys_newlstat(const char __user *filename, - struct compat_stat __user *statbuf); -asmlinkage long compat_sys_newfstatat(unsigned int dfd, - const char __user *filename, - struct compat_stat __user *statbuf, - int flag); -asmlinkage long compat_sys_newfstat(unsigned int fd, - struct compat_stat __user *statbuf); +struct compat_statfs; asmlinkage long compat_sys_statfs(const char __user *pathname, struct compat_statfs __user *buf); asmlinkage long compat_sys_fstatfs(unsigned int fd, struct compat_statfs __user *buf); +struct compat_statfs64; asmlinkage long compat_sys_statfs64(const char __user *pathname, compat_size_t sz, struct compat_statfs64 __user *buf); @@ -503,11 +342,6 @@ asmlinkage long compat_sys_fcntl64(unsigned int fd, unsigned int cmd, asmlinkage long compat_sys_fcntl(unsigned int fd, unsigned int cmd, compat_ulong_t arg); asmlinkage long compat_sys_io_setup(unsigned nr_reqs, u32 __user *ctx32p); -asmlinkage long compat_sys_io_getevents(compat_aio_context_t ctx_id, - compat_long_t min_nr, - compat_long_t nr, - struct io_event __user *events, - struct compat_timespec __user *timeout); asmlinkage long compat_sys_io_submit(compat_aio_context_t ctx_id, int nr, u32 __user *iocb); asmlinkage long compat_sys_mount(const char __user *dev_name, @@ -536,16 +370,6 @@ asmlinkage long compat_sys_open_by_handle_at(int mountdirfd, int flags); asmlinkage long compat_sys_truncate(const char __user *, compat_off_t); asmlinkage long compat_sys_ftruncate(unsigned int, compat_ulong_t); -asmlinkage long compat_sys_pselect6(int n, compat_ulong_t __user *inp, - compat_ulong_t __user *outp, - compat_ulong_t __user *exp, - struct compat_timespec __user *tsp, - void __user *sig); -asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds, - unsigned int nfds, - struct compat_timespec __user *tsp, - const compat_sigset_t __user *sigmask, - compat_size_t sigsetsize); asmlinkage long compat_sys_signalfd4(int ufd, const compat_sigset_t __user *sigmask, compat_size_t sigsetsize, int flags); @@ -574,16 +398,6 @@ asmlinkage long compat_sys_recv(int fd, void __user *buf, compat_size_t len, asmlinkage long compat_sys_recvfrom(int fd, void __user *buf, compat_size_t len, unsigned flags, struct sockaddr __user *addr, int __user *addrlen); -asmlinkage long compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg, - unsigned vlen, unsigned int flags, - struct compat_timespec __user *timeout); -asmlinkage long compat_sys_nanosleep(struct compat_timespec __user *rqtp, - struct compat_timespec __user *rmtp); -asmlinkage long compat_sys_getitimer(int which, - struct compat_itimerval __user *it); -asmlinkage long compat_sys_setitimer(int which, - struct compat_itimerval __user *in, - struct compat_itimerval __user *out); asmlinkage long compat_sys_times(struct compat_tms __user *tbuf); asmlinkage long compat_sys_setrlimit(unsigned int resource, struct compat_rlimit __user *rlim); @@ -599,25 +413,6 @@ asmlinkage long compat_sys_sched_getaffinity(compat_pid_t pid, asmlinkage long compat_sys_timer_create(clockid_t which_clock, struct compat_sigevent __user *timer_event_spec, timer_t __user *created_timer_id); -asmlinkage long compat_sys_timer_settime(timer_t timer_id, int flags, - struct compat_itimerspec __user *new, - struct compat_itimerspec __user *old); -asmlinkage long compat_sys_timer_gettime(timer_t timer_id, - struct compat_itimerspec __user *setting); -asmlinkage long compat_sys_clock_settime(clockid_t which_clock, - struct compat_timespec __user *tp); -asmlinkage long compat_sys_clock_gettime(clockid_t which_clock, - struct compat_timespec __user *tp); -asmlinkage long compat_sys_clock_adjtime(clockid_t which_clock, - struct compat_timex __user *tp); -asmlinkage long compat_sys_clock_getres(clockid_t which_clock, - struct compat_timespec __user *tp); -asmlinkage long compat_sys_clock_nanosleep(clockid_t which_clock, int flags, - struct compat_timespec __user *rqtp, - struct compat_timespec __user *rmtp); -asmlinkage long compat_sys_rt_sigtimedwait(compat_sigset_t __user *uthese, - struct compat_siginfo __user *uinfo, - struct compat_timespec __user *uts, compat_size_t sigsetsize); asmlinkage long compat_sys_rt_sigsuspend(compat_sigset_t __user *unewset, compat_size_t sigsetsize); asmlinkage long compat_sys_rt_sigprocmask(int how, compat_sigset_t __user *set, @@ -636,9 +431,6 @@ asmlinkage long compat_sys_rt_sigqueueinfo(compat_pid_t pid, int sig, asmlinkage long compat_sys_sysinfo(struct compat_sysinfo __user *info); asmlinkage long compat_sys_ioctl(unsigned int fd, unsigned int cmd, compat_ulong_t arg); -asmlinkage long compat_sys_futex(u32 __user *uaddr, int op, u32 val, - struct compat_timespec __user *utime, u32 __user *uaddr2, - u32 val3); asmlinkage long compat_sys_getsockopt(int fd, int level, int optname, char __user *optval, int __user *optlen); asmlinkage long compat_sys_kexec_load(compat_ulong_t entry, @@ -653,14 +445,6 @@ asmlinkage long compat_sys_mq_notify(mqd_t mqdes, asmlinkage long compat_sys_mq_open(const char __user *u_name, int oflag, compat_mode_t mode, struct compat_mq_attr __user *u_attr); -asmlinkage long compat_sys_mq_timedsend(mqd_t mqdes, - const char __user *u_msg_ptr, - compat_size_t msg_len, unsigned int msg_prio, - const struct compat_timespec __user *u_abs_timeout); -asmlinkage ssize_t compat_sys_mq_timedreceive(mqd_t mqdes, - char __user *u_msg_ptr, - compat_size_t msg_len, unsigned int __user *u_msg_prio, - const struct compat_timespec __user *u_abs_timeout); asmlinkage long compat_sys_socketcall(int call, u32 __user *args); asmlinkage long compat_sys_sysctl(struct compat_sysctl_args __user *args);
@@ -707,9 +491,6 @@ int __compat_save_altstack(compat_stack_t __user *, unsigned long); put_user_ex(t->sas_ss_size, &__uss->ss_size); \ } while (0);
-asmlinkage long compat_sys_sched_rr_get_interval(compat_pid_t pid, - struct compat_timespec __user *interval); - asmlinkage long compat_sys_fanotify_mark(int, unsigned int, __u32, __u32, int, const char __user *); #else diff --git a/include/linux/compat_time.h b/include/linux/compat_time.h new file mode 100644 index 000000000000..37564582f6a5 --- /dev/null +++ b/include/linux/compat_time.h @@ -0,0 +1,255 @@ +#ifndef _LINUX_COMPAT_TIME_H +#define _LINUX_COMPAT_TIME_H +/* + * These are the type definitions for the architecture specific + * syscall compatibility layer for 32-bit time_t. + * + * The emulation is used both on 32-bit architectures that have + * been converted to 64-bit __kernel_time_t as well as on 64-bit + * architectures with 32-bit user space. + */ +#include <linux/resource.h> +#include <linux/signal.h> + +#ifndef COMPAT_USE_64BIT_TIME +#define COMPAT_USE_64BIT_TIME 0 +#endif + +typedef s32 compat_int_t; +typedef u32 compat_uint_t; +typedef s32 compat_long_t; +typedef u32 compat_ulong_t; +typedef u32 compat_size_t; +typedef s32 compat_ssize_t; +typedef s32 compat_time_t; +typedef u32 compat_aio_context_t; +typedef u32 compat_uptr_t; +typedef __kernel_pid_t compat_pid_t; + +struct compat_timespec { + compat_time_t tv_sec; + s32 tv_nsec; +}; + +struct compat_timeval { + compat_time_t tv_sec; + s32 tv_usec; +}; + +struct compat_itimerspec { + struct compat_timespec it_interval; + struct compat_timespec it_value; +}; + +struct compat_utimbuf { + compat_time_t actime; + compat_time_t modtime; +}; + +struct compat_itimerval { + struct compat_timeval it_interval; + struct compat_timeval it_value; +}; + +struct compat_timex { + compat_uint_t modes; + compat_long_t offset; + compat_long_t freq; + compat_long_t maxerror; + compat_long_t esterror; + compat_int_t status; + compat_long_t constant; + compat_long_t precision; + compat_long_t tolerance; + struct compat_timeval time; + compat_long_t tick; + compat_long_t ppsfreq; + compat_long_t jitter; + compat_int_t shift; + compat_long_t stabil; + compat_long_t jitcnt; + compat_long_t calcnt; + compat_long_t errcnt; + compat_long_t stbcnt; + compat_int_t tai; + + compat_int_t:32; compat_int_t:32; compat_int_t:32; compat_int_t:32; + compat_int_t:32; compat_int_t:32; compat_int_t:32; compat_int_t:32; + compat_int_t:32; compat_int_t:32; compat_int_t:32; +}; + + +/* + * These functions operate on 32- or 64-bit specs depending on + * COMPAT_USE_64BIT_TIME, hence the void user pointer arguments. + */ +extern int compat_get_timespec(struct timespec *, const void __user *); +extern int compat_put_timespec(const struct timespec *, void __user *); +extern int compat_get_timeval(struct timeval *, const void __user *); +extern int compat_put_timeval(const struct timeval *, void __user *); + +/* + * This function convert a timespec if necessary and returns a *user + * space* pointer. If no conversion is necessary, it returns the + * initial pointer. NULL is a legitimate argument and will always + * output NULL. + */ +extern int compat_convert_timespec(struct timespec __user **, + const void __user *); + +struct compat_rusage { + struct compat_timeval ru_utime; + struct compat_timeval ru_stime; + compat_long_t ru_maxrss; + compat_long_t ru_ixrss; + compat_long_t ru_idrss; + compat_long_t ru_isrss; + compat_long_t ru_minflt; + compat_long_t ru_majflt; + compat_long_t ru_nswap; + compat_long_t ru_inblock; + compat_long_t ru_oublock; + compat_long_t ru_msgsnd; + compat_long_t ru_msgrcv; + compat_long_t ru_nsignals; + compat_long_t ru_nvcsw; + compat_long_t ru_nivcsw; +}; + +struct rusage; +extern int put_compat_rusage(const struct rusage *, + struct compat_rusage __user *); + +static inline int compat_timeval_compare(struct compat_timeval *lhs, + struct compat_timeval *rhs) +{ + if (lhs->tv_sec < rhs->tv_sec) + return -1; + if (lhs->tv_sec > rhs->tv_sec) + return 1; + return lhs->tv_usec - rhs->tv_usec; +} + +static inline int compat_timespec_compare(struct compat_timespec *lhs, + struct compat_timespec *rhs) +{ + if (lhs->tv_sec < rhs->tv_sec) + return -1; + if (lhs->tv_sec > rhs->tv_sec) + return 1; + return lhs->tv_nsec - rhs->tv_nsec; +} + +extern int get_compat_itimerspec(struct itimerspec *dst, + const struct compat_itimerspec __user *src); +extern int put_compat_itimerspec(struct compat_itimerspec __user *dst, + const struct itimerspec *src); + +struct sembuf; +asmlinkage long compat_sys_semtimedop(int semid, struct sembuf __user *tsems, + unsigned nsems, const struct compat_timespec __user *timeout); +asmlinkage long compat_sys_select(int n, compat_ulong_t __user *inp, + compat_ulong_t __user *outp, compat_ulong_t __user *exp, + struct compat_timeval __user *tvp); +struct compat_sel_arg_struct; +asmlinkage long compat_sys_old_select(struct compat_sel_arg_struct __user *arg); +asmlinkage long compat_sys_pselect6(int n, compat_ulong_t __user *inp, + compat_ulong_t __user *outp, + compat_ulong_t __user *exp, + struct compat_timespec __user *tsp, + void __user *sig); +asmlinkage long compat_sys_gettimeofday(struct compat_timeval __user *tv, + struct timezone __user *tz); +asmlinkage long compat_sys_settimeofday(struct compat_timeval __user *tv, + struct timezone __user *tz); + +asmlinkage long compat_sys_adjtimex(struct compat_timex __user *utp); + +asmlinkage long compat_sys_utime(const char __user *filename, + struct compat_utimbuf __user *t); +asmlinkage long compat_sys_utimensat(unsigned int dfd, + const char __user *filename, + struct compat_timespec __user *t, + int flags); + +asmlinkage long compat_sys_time(compat_time_t __user *tloc); +asmlinkage long compat_sys_stime(compat_time_t __user *tptr); + +asmlinkage long compat_sys_timerfd_settime(int ufd, int flags, + const struct compat_itimerspec __user *utmr, + struct compat_itimerspec __user *otmr); +asmlinkage long compat_sys_timerfd_gettime(int ufd, + struct compat_itimerspec __user *otmr); + +asmlinkage long compat_sys_futimesat(unsigned int dfd, + const char __user *filename, + struct compat_timeval __user *t); +asmlinkage long compat_sys_utimes(const char __user *filename, + struct compat_timeval __user *t); +struct compat_stat; +asmlinkage long compat_sys_newstat(const char __user *filename, + struct compat_stat __user *statbuf); +asmlinkage long compat_sys_newlstat(const char __user *filename, + struct compat_stat __user *statbuf); +asmlinkage long compat_sys_newfstatat(unsigned int dfd, + const char __user *filename, + struct compat_stat __user *statbuf, + int flag); +asmlinkage long compat_sys_newfstat(unsigned int fd, + struct compat_stat __user *statbuf); +struct io_event; +asmlinkage long compat_sys_io_getevents(compat_aio_context_t ctx_id, + compat_long_t min_nr, + compat_long_t nr, + struct io_event __user *events, + struct compat_timespec __user *timeout); +asmlinkage long compat_sys_ppoll(struct pollfd __user *ufds, + unsigned int nfds, + struct compat_timespec __user *tsp, + const compat_sigset_t __user *sigmask, + compat_size_t sigsetsize); +struct compat_mmsghdr; +asmlinkage long compat_sys_recvmmsg(int fd, struct compat_mmsghdr __user *mmsg, + unsigned vlen, unsigned int flags, + struct compat_timespec __user *timeout); +asmlinkage long compat_sys_nanosleep(struct compat_timespec __user *rqtp, + struct compat_timespec __user *rmtp); +asmlinkage long compat_sys_getitimer(int which, + struct compat_itimerval __user *it); +asmlinkage long compat_sys_setitimer(int which, + struct compat_itimerval __user *in, + struct compat_itimerval __user *out); +asmlinkage long compat_sys_timer_settime(timer_t timer_id, int flags, + struct compat_itimerspec __user *new, + struct compat_itimerspec __user *old); +asmlinkage long compat_sys_timer_gettime(timer_t timer_id, + struct compat_itimerspec __user *setting); +asmlinkage long compat_sys_clock_settime(clockid_t which_clock, + struct compat_timespec __user *tp); +asmlinkage long compat_sys_clock_gettime(clockid_t which_clock, + struct compat_timespec __user *tp); +asmlinkage long compat_sys_clock_adjtime(clockid_t which_clock, + struct compat_timex __user *tp); +asmlinkage long compat_sys_clock_getres(clockid_t which_clock, + struct compat_timespec __user *tp); +asmlinkage long compat_sys_clock_nanosleep(clockid_t which_clock, int flags, + struct compat_timespec __user *rqtp, + struct compat_timespec __user *rmtp); +struct compat_siginfo; +asmlinkage long compat_sys_rt_sigtimedwait(compat_sigset_t __user *uthese, + struct compat_siginfo __user *uinfo, + struct compat_timespec __user *uts, compat_size_t sigsetsize); +asmlinkage long compat_sys_futex(u32 __user *uaddr, int op, u32 val, + struct compat_timespec __user *utime, u32 __user *uaddr2, + u32 val3); +asmlinkage long compat_sys_mq_timedsend(mqd_t mqdes, + const char __user *u_msg_ptr, + compat_size_t msg_len, unsigned int msg_prio, + const struct compat_timespec __user *u_abs_timeout); +asmlinkage ssize_t compat_sys_mq_timedreceive(mqd_t mqdes, + char __user *u_msg_ptr, + compat_size_t msg_len, unsigned int __user *u_msg_prio, + const struct compat_timespec __user *u_abs_timeout); +asmlinkage long compat_sys_sched_rr_get_interval(compat_pid_t pid, + struct compat_timespec __user *interval); +#endif
There are a total of system calls (aside from ioctl) that pass a time_t or derived data structure as an argument, and in order to extend time_t to 64-bit, we have to replace them with new system calls and keep providing backwards compatibility.
To avoid adding completely new and untested code for this purpose, we introduce a new CONFIG_COMPAT_TIME symbol that is always set on 64-bit architectures with 32-bit compat mode, as well as all 32-bit architectures that have been extended to provide the new system calls.
After this is done for all architectures, the CONFIG_COMPAT_TIME symbol can be made a user-selected option, to enable users to build a kernel that only provides y2038-safe system calls.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- arch/Kconfig | 11 +++++++++++ 1 file changed, 11 insertions(+)
diff --git a/arch/Kconfig b/arch/Kconfig index a65eafb24997..630d3d289569 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -545,4 +545,15 @@ config OLD_SIGACTION config COMPAT_OLD_SIGACTION bool
+config ARCH_HAS_COMPAT_TIME + def_bool COMPAT + +config COMPAT_TIME + def_bool ARCH_HAS_COMPAT_TIME + help + This should be selected by all architectures that need to support + system calls with a 32-bit time_t. Traditionally, this has been + used on all 32-bit architectures, and needs to be supported on + 64-bit architectures as part of compat syscall handling. + source "kernel/gcov/Kconfig"
This adds the necessary type definitions to allow using the linux/compat_time.h header file on 32-bit architectures without adding an asm/compat.h header in each architecture.
The types we define here are used in the compat system call definitions of some system calls that also pass a time_t in another structure, but are themselves not depending on the size of time_t.
This also adds a way to use COMPAT_SYSCALL_DEFINE() by including the compat.h header file. Some code may need to get reshuffled here.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/compat.h | 6 ++++-- include/linux/compat_time.h | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/include/linux/compat.h b/include/linux/compat.h index 41b0dae6203b..fefc45094107 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -5,8 +5,6 @@ * syscall compatibility layer. */
-#ifdef CONFIG_COMPAT - #include <linux/stat.h> #include <linux/param.h> /* for HZ */ #include <linux/sem.h> @@ -17,7 +15,9 @@ #include <linux/unistd.h> #include <linux/compat_time.h>
+#ifdef CONFIG_COMPAT #include <asm/compat.h> +#endif #include <asm/siginfo.h> #include <asm/signal.h>
@@ -52,6 +52,8 @@ } \ static inline long C_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__))
+#ifdef CONFIG_COMPAT + #ifndef compat_user_stack_pointer #define compat_user_stack_pointer() current_user_stack_pointer() #endif diff --git a/include/linux/compat_time.h b/include/linux/compat_time.h index 37564582f6a5..e17375a91f76 100644 --- a/include/linux/compat_time.h +++ b/include/linux/compat_time.h @@ -78,6 +78,30 @@ struct compat_timex { compat_int_t:32; compat_int_t:32; compat_int_t:32; };
+#ifdef CONFIG_COMPAT +#include <asm/compat.h> + +#define _COMPAT_NSIG_WORDS (_COMPAT_NSIG / _COMPAT_NSIG_BPW) +typedef struct { + compat_sigset_word sig[_COMPAT_NSIG_WORDS]; +} compat_sigset_t; + +#else + +#define compat_mmsghdr mmsghdr +#define compat_stat stat +#define compat_siginfo siginfo +#define compat_sigevent sigevent +#define compat_sigset_t sigset_t +#define __compat_uid_t __kernel_uid_t +#define __compat_gid_t __kernel_gid_t +#define compat_mode_t __kernel_mode_t +#define copy_siginfo_to_user32(uinfo, info) copy_siginfo_to_user(uinfo, info) +static inline void __user *compat_ptr(compat_uptr_t ptr) +{ + return (void __user*)ptr; +} +#endif
/* * These functions operate on 32- or 64-bit specs depending on
For the time being, we use conditional compilation in fs/compat.c and kernel/compat.c to differentiate two cases:
a) code that is used for CONFIG_COMPAT_TIME is shared between 32-bit and 64-bit architectures that provide support for existing 32-bit binaries with 32-bit time_t b) code that is defined under CONFIG_COMPAT is only used on 64-bit machines to provide support for 32-bit binaries, either system calls that do not use time_t or (rare) that use a 64-bit time_t in compat mode.
We probably want to split those into separate files in the long run, but at the moment, this makes it easier to avoid merge conflicts.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- fs/Makefile | 1 + fs/compat.c | 12 +++++++++++- include/linux/thread_info.h | 2 +- kernel/Makefile | 1 + kernel/compat.c | 28 ++++++++++++++++++++++++++++ 5 files changed, 42 insertions(+), 2 deletions(-)
diff --git a/fs/Makefile b/fs/Makefile index cb92fd4c3172..77f876cd94be 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_AIO) += aio.o obj-$(CONFIG_FS_DAX) += dax.o obj-$(CONFIG_FILE_LOCKING) += locks.o obj-$(CONFIG_COMPAT) += compat.o compat_ioctl.o +obj-$(CONFIG_COMPAT_TIME) += compat.o obj-$(CONFIG_BINFMT_AOUT) += binfmt_aout.o obj-$(CONFIG_BINFMT_EM86) += binfmt_em86.o obj-$(CONFIG_BINFMT_MISC) += binfmt_misc.o diff --git a/fs/compat.c b/fs/compat.c index 7d78cde20fc3..c5065aa1852c 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -54,6 +54,7 @@ #include <asm/ioctls.h> #include "internal.h"
+#ifdef CONFIG_COMPAT_TIME /* * Not all architectures have sys_utime, so implement this in terms * of sys_utimes. @@ -194,7 +195,9 @@ COMPAT_SYSCALL_DEFINE2(newfstat, unsigned int, fd, error = cp_compat_stat(&stat, statbuf); return error; } +#endif /* CONFIG_COMPAT_TIME */
+#ifdef CONFIG_COMPAT static int put_compat_statfs(struct compat_statfs __user *ubuf, struct kstatfs *kbuf) { @@ -505,7 +508,9 @@ COMPAT_SYSCALL_DEFINE2(io_setup, unsigned, nr_reqs, u32 __user *, ctx32p) ret = put_user((u32) ctx64, ctx32p); return ret; } +#endif
+#ifdef CONFIG_COMPAT_TIME COMPAT_SYSCALL_DEFINE5(io_getevents, compat_aio_context_t, ctx_id, compat_long_t, min_nr, compat_long_t, nr, @@ -525,7 +530,9 @@ COMPAT_SYSCALL_DEFINE5(io_getevents, compat_aio_context_t, ctx_id, } return sys_io_getevents(ctx_id, min_nr, nr, events, ut); } +#endif
+#ifdef CONFIG_COMPAT /* A write operation does a read from user space and vice versa */ #define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ)
@@ -1086,7 +1093,9 @@ COMPAT_SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, fla { return do_sys_open(dfd, filename, flags, mode); } +#endif
+#ifdef CONFIG_COMPAT_TIME #define __COMPAT_NFDBITS (8 * sizeof(compat_ulong_t))
static int poll_select_copy_remaining(struct timespec *end_time, void __user *p, @@ -1453,8 +1462,9 @@ COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds,
return ret; } +#endif
-#ifdef CONFIG_FHANDLE +#if defined(CONFIG_FHANDLE) && defined(CONFIG_COMPAT) /* * Exactly like fs/open.c:sys_open_by_handle_at(), except that it * doesn't set the O_LARGEFILE flag. diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h index ff307b548ed3..43686bb94374 100644 --- a/include/linux/thread_info.h +++ b/include/linux/thread_info.h @@ -32,7 +32,7 @@ struct restart_block { struct { clockid_t clockid; struct timespec __user *rmtp; -#ifdef CONFIG_COMPAT +#ifdef CONFIG_COMPAT_TIME struct compat_timespec __user *compat_rmtp; #endif u64 expires; diff --git a/kernel/Makefile b/kernel/Makefile index 60c302cfb4d3..970f880a5f17 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_KEXEC) += kexec.o obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o obj-$(CONFIG_COMPAT) += compat.o +obj-$(CONFIG_COMPAT_TIME) += compat.o obj-$(CONFIG_CGROUPS) += cgroup.o obj-$(CONFIG_CGROUP_FREEZER) += cgroup_freezer.o obj-$(CONFIG_CPUSETS) += cpuset.o diff --git a/kernel/compat.c b/kernel/compat.c index 24f00610c575..115ad84d4048 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -30,6 +30,7 @@
#include <asm/uaccess.h>
+#ifdef CONFIG_COMPAT_TIME static int compat_get_timex(struct timex *txc, struct compat_timex __user *utp) { memset(txc, 0, sizeof(struct timex)); @@ -190,6 +191,7 @@ int compat_put_timespec(const struct timespec *ts, void __user *uts) } EXPORT_SYMBOL_GPL(compat_put_timespec);
+#ifdef CONFIG_COMPAT int compat_convert_timespec(struct timespec __user **kts, const void __user *cts) { @@ -212,6 +214,7 @@ int compat_convert_timespec(struct timespec __user **kts, *kts = uts; return 0; } +#endif
static long compat_nanosleep_restart(struct restart_block *restart) { @@ -339,7 +342,9 @@ COMPAT_SYSCALL_DEFINE3(setitimer, int, which, return -EFAULT; return 0; } +#endif
+#ifdef CONFIG_COMPAT static compat_clock_t clock_t_to_compat_clock_t(clock_t x) { return compat_jiffies_to_clock_t(clock_t_to_jiffies(x)); @@ -507,7 +512,9 @@ COMPAT_SYSCALL_DEFINE2(getrlimit, unsigned int, resource, } return ret; } +#endif
+#ifdef CONFIG_COMPAT_TIME int put_compat_rusage(const struct rusage *r, struct compat_rusage __user *ru) { if (!access_ok(VERIFY_WRITE, ru, sizeof(*ru)) || @@ -598,7 +605,9 @@ COMPAT_SYSCALL_DEFINE5(waitid, info.si_code |= __SI_CHLD; return copy_siginfo_to_user32(uinfo, &info); } +#endif
+#ifdef CONFIG_COMPAT static int compat_get_user_cpu_mask(compat_ulong_t __user *user_mask_ptr, unsigned len, struct cpumask *new_mask) { @@ -660,7 +669,9 @@ COMPAT_SYSCALL_DEFINE3(sched_getaffinity, compat_pid_t, pid, unsigned int, len,
return ret; } +#endif
+#ifdef CONFIG_COMPAT_TIME int get_compat_itimerspec(struct itimerspec *dst, const struct compat_itimerspec __user *src) { @@ -678,7 +689,9 @@ int put_compat_itimerspec(struct compat_itimerspec __user *dst, return -EFAULT; return 0; } +#endif
+#ifdef CONFIG_COMPAT COMPAT_SYSCALL_DEFINE3(timer_create, clockid_t, which_clock, struct compat_sigevent __user *, timer_event_spec, timer_t __user *, created_timer_id) @@ -696,7 +709,9 @@ COMPAT_SYSCALL_DEFINE3(timer_create, clockid_t, which_clock,
return sys_timer_create(which_clock, event, created_timer_id); } +#endif
+#ifdef CONFIG_COMPAT_TIME COMPAT_SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags, struct compat_itimerspec __user *, new, struct compat_itimerspec __user *, old) @@ -865,7 +880,9 @@ COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags, } return err; } +#endif
+#ifdef CONFIG_COMPAT /* * We currently only need the following fields from the sigevent * structure: sigev_value, sigev_signo, sig_notify and (sometimes @@ -991,6 +1008,7 @@ sigset_to_compat(compat_sigset_t *compat, const sigset_t *set) } }
+#ifdef CONFIG_COMPAT_TIME COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait, compat_sigset_t __user *, uthese, struct compat_siginfo __user *, uinfo, struct compat_timespec __user *, uts, compat_size_t, sigsetsize) @@ -1022,7 +1040,10 @@ COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait, compat_sigset_t __user *, uthese,
return ret; } +#endif +#endif
+#ifdef CONFIG_COMPAT_TIME #ifdef __ARCH_WANT_COMPAT_SYS_TIME
/* compat_time_t is a 32 bit "long" and needs to get converted. */ @@ -1080,7 +1101,9 @@ COMPAT_SYSCALL_DEFINE1(adjtimex, struct compat_timex __user *, utp)
return ret; } +#endif
+#ifdef CONFIG_COMPAT #ifdef CONFIG_NUMA COMPAT_SYSCALL_DEFINE6(move_pages, pid_t, pid, compat_ulong_t, nr_pages, compat_uptr_t __user *, pages32, @@ -1135,7 +1158,9 @@ COMPAT_SYSCALL_DEFINE4(migrate_pages, compat_pid_t, pid, return sys_migrate_pages(pid, nr_bits + 1, old, new); } #endif +#endif
+#ifdef CONFIG_COMPAT_TIME COMPAT_SYSCALL_DEFINE2(sched_rr_get_interval, compat_pid_t, pid, struct compat_timespec __user *, interval) @@ -1151,7 +1176,9 @@ COMPAT_SYSCALL_DEFINE2(sched_rr_get_interval, return -EFAULT; return ret; } +#endif
+#ifdef CONFIG_COMPAT /* * Allocate user-space memory for the duration of a single system call, * in order to marshall parameters inside a compat thunk. @@ -1172,3 +1199,4 @@ void __user *compat_alloc_user_space(unsigned long len) return ptr; } EXPORT_SYMBOL_GPL(compat_alloc_user_space); +#endif
rt_sigtimedwait takes sigevent and sigset arguments that are always different between 32-bit and 64-bit user space. It also takes a timespec argument that will be the same in the future between architectures, and we want to share the compat system call handling.
We need to support all four combinations, and this adds the two new ones:
- native sys_rt_sigtimedwait: now takes a __kernel_timespec that will at some point use 64-bit time_t on all architectures - compat_sys_rt_sigtimedwait on 64-bit kernels: emulates the behavior of the old 32-bit user space - compat_sys_rt_sigtimedwait on 32-bit kernels: emulates the old timespec but uses the native 32-bit sigevent and siginfo arguments - compat_sys_rt_sigtimedwait_time64 on 64-bit kernels: emulates the 32-bit sigevent and siginfo structures but uses the native __kernel_timespec.
Having separate code for all four variants is a bit ugly, but I could not come up with a better solution, and the code duplication is fairly low in the end.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/compat_time.h | 5 ++++ kernel/compat.c | 66 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+)
diff --git a/include/linux/compat_time.h b/include/linux/compat_time.h index e17375a91f76..789b6415e90e 100644 --- a/include/linux/compat_time.h +++ b/include/linux/compat_time.h @@ -8,6 +8,7 @@ * been converted to 64-bit __kernel_time_t as well as on 64-bit * architectures with 32-bit user space. */ +#include <linux/signal.h> #include <linux/resource.h> #include <linux/signal.h>
@@ -260,9 +261,13 @@ asmlinkage long compat_sys_clock_nanosleep(clockid_t which_clock, int flags, struct compat_timespec __user *rqtp, struct compat_timespec __user *rmtp); struct compat_siginfo; +struct __kernel_timespec; asmlinkage long compat_sys_rt_sigtimedwait(compat_sigset_t __user *uthese, struct compat_siginfo __user *uinfo, struct compat_timespec __user *uts, compat_size_t sigsetsize); +asmlinkage long compat_sys_rt_sigtimedwait_time64(compat_sigset_t __user *uthese, + struct compat_siginfo __user *uinfo, + struct __kernel_timespec __user *uts, compat_size_t sigsetsize); asmlinkage long compat_sys_futex(u32 __user *uaddr, int op, u32 val, struct compat_timespec __user *utime, u32 __user *uaddr2, u32 val3); diff --git a/kernel/compat.c b/kernel/compat.c index 115ad84d4048..03bb63995027 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -27,6 +27,8 @@ #include <linux/times.h> #include <linux/ptrace.h> #include <linux/gfp.h> +#include <linux/signal.h> +#include <asm/signal.h>
#include <asm/uaccess.h>
@@ -1043,6 +1045,70 @@ COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait, compat_sigset_t __user *, uthese, #endif #endif
+#if defined(CONFIG_COMPAT_TIME) && !defined(CONFIG_COMPAT) +COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait, sigset_t __user *, uthese, + struct siginfo __user *, uinfo, + struct compat_timespec __user *, uts, size_t, sigsetsize) +{ + sigset_t s; + struct timespec t; + siginfo_t info; + long ret; + + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&s, uthese, sizeof(sigset_t))) + return -EFAULT; + if (uts) { + if (compat_get_timespec(&t, uts)) + return -EFAULT; + } + + ret = do_sigtimedwait(&s, &info, uts ? &t : NULL); + + if (ret > 0 && uinfo) { + if (copy_siginfo_to_user(uinfo, &info)) + ret = -EFAULT; + } + + return ret; +} +#endif + +#ifdef CONFIG_COMPAT +COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait_time64, compat_sigset_t __user *, uthese, + struct compat_siginfo __user *, uinfo, + struct __kernel_timespec __user *, uts, compat_size_t, sigsetsize) +{ + compat_sigset_t s32; + sigset_t s; + struct timespec t; + siginfo_t info; + long ret; + + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&s32, uthese, sizeof(compat_sigset_t))) + return -EFAULT; + sigset_from_compat(&s, &s32); + if (uts) { + if (get_timespec64(&t, uts)) + return -EFAULT; + } + + ret = do_sigtimedwait(&s, &info, uts ? &t : NULL); + + if (ret > 0 && uinfo) { + if (copy_siginfo_to_user32(uinfo, &info)) + ret = -EFAULT; + } + + return ret; +} +#endif + #ifdef CONFIG_COMPAT_TIME #ifdef __ARCH_WANT_COMPAT_SYS_TIME
A lot of system calls pass a 'struct timespec' from or to user space, and we want to change that type to be based on a 64-bit time_t by default.
This introduces a new type struct __kernel_timespec, which has the format we want to use eventually, but also has an override so all architectures that do not define CONFIG_COMPAT_TIME yet still get the old behavior. Once all architectures set this, we can remove that override.
This also introduces a get_timespec64/put_timespec64 set of functions that convert between a __kernel_timespec in user space and a timespec64 in kernel space.
The current behavior of get_timespec64 explicitly zeroes the upper half of the tv_nsec member, to allow user space to define its own 'struct timespec' with some padding in it. Whether this is a good or bad idea is open for discussion.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/time64.h | 17 +++++++++++------ include/uapi/linux/time.h | 17 +++++++++++++++++ kernel/time/time.c | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 6 deletions(-)
diff --git a/include/linux/time64.h b/include/linux/time64.h index a3831478d9cf..880ebe4b4ba4 100644 --- a/include/linux/time64.h +++ b/include/linux/time64.h @@ -1,14 +1,12 @@ #ifndef _LINUX_TIME64_H #define _LINUX_TIME64_H
-#include <uapi/linux/time.h> - typedef __s64 time64_t;
-/* - * This wants to go into uapi/linux/time.h once we agreed about the - * userspace interfaces. - */ +#ifndef CONFIG_COMPAT_TIME +# define __kernel_timespec timespec +#endif + #if __BITS_PER_LONG == 64 # define timespec64 timespec #else @@ -18,6 +16,8 @@ struct timespec64 { }; #endif
+#include <uapi/linux/time.h> + /* Parameters used to convert the timespec values: */ #define MSEC_PER_SEC 1000L #define USEC_PER_MSEC 1000L @@ -187,4 +187,9 @@ static __always_inline void timespec64_add_ns(struct timespec64 *a, u64 ns)
#endif
+extern int get_timespec64(struct timespec64 *ts, + const struct __kernel_timespec __user *uts); +extern int put_timespec64(const struct timespec64 *ts, + struct __kernel_timespec __user *uts); + #endif /* _LINUX_TIME64_H */ diff --git a/include/uapi/linux/time.h b/include/uapi/linux/time.h index e75e1b6ff27f..72d894df3013 100644 --- a/include/uapi/linux/time.h +++ b/include/uapi/linux/time.h @@ -66,4 +66,21 @@ struct itimerval { */ #define TIMER_ABSTIME 0x01
+/* types based on 64-bit time_t */ +#ifndef __kernel_timespec +typedef __s64 __kernel_time64_t; + +struct __kernel_timespec { + __kernel_time64_t tv_sec; + __s64 tv_nsec; +}; +#endif + +#ifndef __kernel_itimerspec +struct __kernel_itimerspec { + struct __kernel_timespec it_interval; + struct __kernel_timespec it_value; +}; +#endif + #endif /* _UAPI_LINUX_TIME_H */ diff --git a/kernel/time/time.c b/kernel/time/time.c index 2c85b7724af4..845af1db66fa 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -30,6 +30,7 @@ #include <linux/export.h> #include <linux/timex.h> #include <linux/capability.h> +#include <linux/compat.h> #include <linux/timekeeper_internal.h> #include <linux/errno.h> #include <linux/syscalls.h> @@ -37,6 +38,7 @@ #include <linux/fs.h> #include <linux/math64.h> #include <linux/ptrace.h> +#include <linux/time64.h>
#include <asm/uaccess.h> #include <asm/unistd.h> @@ -783,3 +785,33 @@ struct timespec timespec_add_safe(const struct timespec lhs,
return res; } + +int get_timespec64(struct timespec64 *ts, + const struct __kernel_timespec __user *uts) +{ + struct __kernel_timespec kts; + int ret; + + ret = copy_from_user(&kts, uts, sizeof(kts)); + if (ret) + return -EFAULT; + + ts->tv_sec = kts.tv_sec; + if (!IS_ENABLED(CONFIG_64BIT) || is_compat_task()) + kts.tv_nsec &= 0xfffffffful; + ts->tv_nsec = kts.tv_nsec; + + return 0; +} +EXPORT_SYMBOL_GPL(get_timespec64); + +int put_timespec64(const struct timespec64 *ts, + struct __kernel_timespec __user *uts) +{ + struct __kernel_timespec kts = { + .tv_sec = ts->tv_sec, + .tv_nsec = ts->tv_nsec + }; + return copy_to_user(uts, &kts, sizeof(kts)) ? -EFAULT : 0; +} +EXPORT_SYMBOL_GPL(put_timespec64);
The stat() family of system calls uses up to three different data structures: 'struct __kernel_oldstat', 'struct stat', and 'struct stat64', which were extended in various ways over time. Unfortunately, on most 32-bit architectures and even some 64-bit machines (parisc), all of them use 32-bit timestamps, which are already broken because they cannot represent the range of times that can be stored on typical file systems. When time_t overflows, things of course become much worse.
This introduces a fourth data structure, 'struct __kernel_stat', which is supposed to match the layout of 'struct stat' on 64-bit architectures, so the compat handlers can be shared between native 64-bit and compat 32-bit syscalls. On architectures that are always 32-bit, the asm-generic variant can be used.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- arch/alpha/include/uapi/asm/stat.h | 4 +++ arch/arm/include/uapi/asm/stat.h | 2 ++ arch/avr32/include/uapi/asm/stat.h | 2 ++ arch/blackfin/include/uapi/asm/stat.h | 2 ++ arch/cris/include/uapi/asm/stat.h | 2 ++ arch/frv/include/uapi/asm/stat.h | 2 ++ arch/ia64/include/uapi/asm/stat.h | 4 +++ arch/m32r/include/uapi/asm/stat.h | 1 + arch/m68k/include/uapi/asm/stat.h | 2 ++ arch/mips/include/uapi/asm/stat.h | 1 + arch/mn10300/include/uapi/asm/stat.h | 2 ++ arch/parisc/include/uapi/asm/stat.h | 1 + arch/powerpc/include/uapi/asm/stat.h | 25 +++++++++++++++++ arch/s390/include/uapi/asm/stat.h | 24 +++++++++++++++++ arch/sh/include/uapi/asm/stat.h | 2 ++ arch/sparc/include/uapi/asm/stat.h | 28 +++++++++++++++++++ arch/x86/include/uapi/asm/stat.h | 49 ++++++++++++++++++++++++---------- arch/xtensa/include/uapi/asm/stat.h | 2 ++ include/linux/syscalls.h | 11 ++++---- include/uapi/asm-generic/kernel_stat.h | 36 +++++++++++++++++++++++++ include/uapi/asm-generic/stat.h | 12 +++++---- 21 files changed, 189 insertions(+), 25 deletions(-) create mode 100644 include/uapi/asm-generic/kernel_stat.h
diff --git a/arch/alpha/include/uapi/asm/stat.h b/arch/alpha/include/uapi/asm/stat.h index 07ad3e6b3f3e..ca566bc620f6 100644 --- a/arch/alpha/include/uapi/asm/stat.h +++ b/arch/alpha/include/uapi/asm/stat.h @@ -1,6 +1,10 @@ #ifndef _ALPHA_STAT_H #define _ALPHA_STAT_H
+#ifndef __kernel_stat +#define __kernel_stat stat +#endif + struct stat { unsigned int st_dev; unsigned int st_ino; diff --git a/arch/arm/include/uapi/asm/stat.h b/arch/arm/include/uapi/asm/stat.h index 42c0c13999d5..537a12553dd8 100644 --- a/arch/arm/include/uapi/asm/stat.h +++ b/arch/arm/include/uapi/asm/stat.h @@ -1,6 +1,8 @@ #ifndef _ASMARM_STAT_H #define _ASMARM_STAT_H
+#include <asm-generic/kernel_stat.h> + struct __old_kernel_stat { unsigned short st_dev; unsigned short st_ino; diff --git a/arch/avr32/include/uapi/asm/stat.h b/arch/avr32/include/uapi/asm/stat.h index c06acef7fce7..2b528ca17985 100644 --- a/arch/avr32/include/uapi/asm/stat.h +++ b/arch/avr32/include/uapi/asm/stat.h @@ -8,6 +8,8 @@ #ifndef _UAPI__ASM_AVR32_STAT_H #define _UAPI__ASM_AVR32_STAT_H
+#include <asm-generic/kernel_stat.h> + struct __old_kernel_stat { unsigned short st_dev; unsigned short st_ino; diff --git a/arch/blackfin/include/uapi/asm/stat.h b/arch/blackfin/include/uapi/asm/stat.h index d3068a750b94..99ee343aec23 100644 --- a/arch/blackfin/include/uapi/asm/stat.h +++ b/arch/blackfin/include/uapi/asm/stat.h @@ -7,6 +7,8 @@ #ifndef _UAPI_BFIN_STAT_H #define _UAPI_BFIN_STAT_H
+#include <asm-generic/kernel_stat.h> + struct stat { unsigned short st_dev; unsigned short __pad1; diff --git a/arch/cris/include/uapi/asm/stat.h b/arch/cris/include/uapi/asm/stat.h index 9e558cc3c43b..4837884cd2d3 100644 --- a/arch/cris/include/uapi/asm/stat.h +++ b/arch/cris/include/uapi/asm/stat.h @@ -1,6 +1,8 @@ #ifndef _CRIS_STAT_H #define _CRIS_STAT_H
+#include <asm-generic/kernel_stat.h> + /* Keep this a verbatim copy of i386 version; tweak CRIS-specific bits in the kernel if necessary. */
diff --git a/arch/frv/include/uapi/asm/stat.h b/arch/frv/include/uapi/asm/stat.h index ce56de9b37ba..5448b198fbb6 100644 --- a/arch/frv/include/uapi/asm/stat.h +++ b/arch/frv/include/uapi/asm/stat.h @@ -1,6 +1,8 @@ #ifndef _ASM_STAT_H #define _ASM_STAT_H
+#include <asm-generic/kernel_stat.h> + struct __old_kernel_stat { unsigned short st_dev; unsigned short st_ino; diff --git a/arch/ia64/include/uapi/asm/stat.h b/arch/ia64/include/uapi/asm/stat.h index 367bb90cdffa..cde68a31e183 100644 --- a/arch/ia64/include/uapi/asm/stat.h +++ b/arch/ia64/include/uapi/asm/stat.h @@ -1,6 +1,10 @@ #ifndef _ASM_IA64_STAT_H #define _ASM_IA64_STAT_H
+#ifndef __kernel_stat +#define __kernel_stat stat +#endif + /* * Modified 1998, 1999 * David Mosberger-Tang davidm@hpl.hp.com, Hewlett-Packard Co diff --git a/arch/m32r/include/uapi/asm/stat.h b/arch/m32r/include/uapi/asm/stat.h index 98470fe483b6..d0ffa70f73c0 100644 --- a/arch/m32r/include/uapi/asm/stat.h +++ b/arch/m32r/include/uapi/asm/stat.h @@ -2,6 +2,7 @@ #define _ASM_M32R_STAT_H
#include <asm/byteorder.h> +#include <asm-generic/kernel_stat.h>
struct __old_kernel_stat { unsigned short st_dev; diff --git a/arch/m68k/include/uapi/asm/stat.h b/arch/m68k/include/uapi/asm/stat.h index dd38bc2e9f98..6f455db47b4e 100644 --- a/arch/m68k/include/uapi/asm/stat.h +++ b/arch/m68k/include/uapi/asm/stat.h @@ -1,6 +1,8 @@ #ifndef _M68K_STAT_H #define _M68K_STAT_H
+#include <asm-generic/kernel_stat.h> + struct __old_kernel_stat { unsigned short st_dev; unsigned short st_ino; diff --git a/arch/mips/include/uapi/asm/stat.h b/arch/mips/include/uapi/asm/stat.h index b47bc541bbc0..53e58fbd83fa 100644 --- a/arch/mips/include/uapi/asm/stat.h +++ b/arch/mips/include/uapi/asm/stat.h @@ -12,6 +12,7 @@ #include <linux/types.h>
#include <asm/sgidefs.h> +#include <asm-generic/kernel_stat.h>
#if (_MIPS_SIM == _MIPS_SIM_ABI32) || (_MIPS_SIM == _MIPS_SIM_NABI32)
diff --git a/arch/mn10300/include/uapi/asm/stat.h b/arch/mn10300/include/uapi/asm/stat.h index 63ff8371cf2c..af3b4d6b7b7a 100644 --- a/arch/mn10300/include/uapi/asm/stat.h +++ b/arch/mn10300/include/uapi/asm/stat.h @@ -1,6 +1,8 @@ #ifndef _ASM_STAT_H #define _ASM_STAT_H
+#include <asm-generic/kernel_stat.h> + struct __old_kernel_stat { unsigned short st_dev; unsigned short st_ino; diff --git a/arch/parisc/include/uapi/asm/stat.h b/arch/parisc/include/uapi/asm/stat.h index b606b366d0a7..f06ce7ba0115 100644 --- a/arch/parisc/include/uapi/asm/stat.h +++ b/arch/parisc/include/uapi/asm/stat.h @@ -2,6 +2,7 @@ #define _PARISC_STAT_H
#include <linux/types.h> +#include <asm-generic/kernel_stat.h>
struct stat { unsigned int st_dev; /* dev_t is 32 bits on parisc */ diff --git a/arch/powerpc/include/uapi/asm/stat.h b/arch/powerpc/include/uapi/asm/stat.h index 84880b80cc1c..248d8072267f 100644 --- a/arch/powerpc/include/uapi/asm/stat.h +++ b/arch/powerpc/include/uapi/asm/stat.h @@ -78,4 +78,29 @@ struct stat64 { unsigned int __unused5; };
+#ifndef __kernel_stat +/* this matches the powerpc64 'struct stat' for compat tasks */ +struct __kernel_stat { + unsigned long long st_dev; + unsigned long long st_ino; + unsigned long long st_nlink; + unsigned int st_mode; + unsigned int st_uid; + unsigned int st_gid; + unsigned long long st_rdev; + unsigned long long st_size; + unsigned long long st_blksize; + unsigned long long st_blocks; + unsigned long long st_atime; + unsigned long long st_atime_nsec; + unsigned long long st_mtime; + unsigned long long st_mtime_nsec; + unsigned long long st_ctime; + unsigned long long st_ctime_nsec; + unsigned long long __unused4; + unsigned long long __unused5; + unsigned long long __unused6; +}; +#endif + #endif /* _ASM_POWERPC_STAT_H */ diff --git a/arch/s390/include/uapi/asm/stat.h b/arch/s390/include/uapi/asm/stat.h index b4ca97d91466..d4c2711249dd 100644 --- a/arch/s390/include/uapi/asm/stat.h +++ b/arch/s390/include/uapi/asm/stat.h @@ -100,4 +100,28 @@ struct stat {
#define STAT_HAVE_NSEC 1
+/* same layout as 'struct stat on s390x' for both 32-bit and 64-bit tasks */ +#ifndef __kernel_stat +struct __kernel_stat { + unsigned long long st_dev; + unsigned long long st_ino; + unsigned long long st_nlink; + unsigned int st_mode; + unsigned int st_uid; + unsigned int st_gid; + unsigned int __pad1; + unsigned long long st_rdev; + unsigned long long st_size; + unsigned long long st_atime; + unsigned long long st_atime_nsec; + unsigned long long st_mtime; + unsigned long long st_mtime_nsec; + unsigned long long st_ctime; + unsigned long long st_ctime_nsec; + unsigned long long st_blksize; + long long st_blocks; + unsigned long long __unused[3]; +}; +#endif + #endif diff --git a/arch/sh/include/uapi/asm/stat.h b/arch/sh/include/uapi/asm/stat.h index e1810cc6e3da..a13ffbcccd50 100644 --- a/arch/sh/include/uapi/asm/stat.h +++ b/arch/sh/include/uapi/asm/stat.h @@ -1,6 +1,8 @@ #ifndef __ASM_SH_STAT_H #define __ASM_SH_STAT_H
+#include <asm-generic/kernel_stat.h> + struct __old_kernel_stat { unsigned short st_dev; unsigned short st_ino; diff --git a/arch/sparc/include/uapi/asm/stat.h b/arch/sparc/include/uapi/asm/stat.h index a232e9e1f4e5..6d19c7bdc641 100644 --- a/arch/sparc/include/uapi/asm/stat.h +++ b/arch/sparc/include/uapi/asm/stat.h @@ -104,4 +104,32 @@ struct stat64 { unsigned int __unused5; }; #endif /* defined(__sparc__) && defined(__arch64__) */ + +#ifndef __kernel_stat +/* This matches the sparc64 'struct stat64' in compat tasks */ +struct __kernel_stat { + unsigned long long st_dev; + unsigned long long st_ino; + unsigned long long st_nlink; + + unsigned int st_mode; + unsigned int st_uid; + unsigned int st_gid; + unsigned int __pad0; + + unsigned long long st_rdev; + long long st_size; + long long st_blksize; + long long st_blocks; + + unsigned long long st_atime; + unsigned long long st_atime_nsec; + unsigned long long st_mtime; + unsigned long long st_mtime_nsec; + unsigned long long st_ctime; + unsigned long long st_ctime_nsec; + long long __unused[3]; +}; +#endif + #endif /* __SPARC_STAT_H */ diff --git a/arch/x86/include/uapi/asm/stat.h b/arch/x86/include/uapi/asm/stat.h index bc03eb5d6360..5d5754fc3d36 100644 --- a/arch/x86/include/uapi/asm/stat.h +++ b/arch/x86/include/uapi/asm/stat.h @@ -27,12 +27,6 @@ struct stat { unsigned long __unused5; };
-/* We don't need to memset the whole thing just to initialize the padding */ -#define INIT_STRUCT_STAT_PADDING(st) do { \ - st.__unused4 = 0; \ - st.__unused5 = 0; \ -} while (0) - #define STAT64_HAS_BROKEN_ST_INO 1
/* This matches struct stat64 in glibc2.1, hence the absolutely @@ -102,14 +96,6 @@ struct stat { __kernel_long_t __unused[3]; };
-/* We don't need to memset the whole thing just to initialize the padding */ -#define INIT_STRUCT_STAT_PADDING(st) do { \ - st.__pad0 = 0; \ - st.__unused[0] = 0; \ - st.__unused[1] = 0; \ - st.__unused[2] = 0; \ -} while (0) - #endif
/* for 32bit emulation and 32 bit kernels */ @@ -134,4 +120,39 @@ struct __old_kernel_stat { #endif };
+#ifndef __kernel_stat +/* This matches the 64-bit version of 'struct stat' on i386 */ +struct __kernel_stat { + unsigned long long st_dev; + unsigned long long st_ino; + unsigned long long st_nlink; + + unsigned int st_mode; + unsigned int st_uid; + unsigned int st_gid; + unsigned int __pad0; + unsigned long long st_rdev; + long long st_size; + long long st_blksize; + long long st_blocks; /* Number 512-byte blocks allocated. */ + + unsigned long long st_atime; + unsigned long long st_atime_nsec; + unsigned long long st_mtime; + unsigned long long st_mtime_nsec; + unsigned long long st_ctime; + unsigned long long st_ctime_nsec; + long long __unused[3]; +}; + +/* We don't need to memset the whole thing just to initialize the padding */ +#define INIT_STRUCT_STAT_PADDING(st) do { \ + st.__pad0 = 0; \ + st.__unused[0] = 0; \ + st.__unused[1] = 0; \ + st.__unused[2] = 0; \ +} while (0) + +#endif + #endif /* _ASM_X86_STAT_H */ diff --git a/arch/xtensa/include/uapi/asm/stat.h b/arch/xtensa/include/uapi/asm/stat.h index c4992038cee0..8d9c1d9d82d0 100644 --- a/arch/xtensa/include/uapi/asm/stat.h +++ b/arch/xtensa/include/uapi/asm/stat.h @@ -11,6 +11,8 @@ #ifndef _XTENSA_STAT_H #define _XTENSA_STAT_H
+#include <asm-generic/kernel_stat.h> + #define STAT_HAVE_NSEC 1
struct stat { diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 76d1e38aabe1..71b574b0365e 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -44,8 +44,6 @@ struct semaphore; struct sembuf; struct shmid_ds; struct sockaddr; -struct stat; -struct stat64; struct statfs; struct statfs64; struct __sysctl_args; @@ -79,6 +77,7 @@ union bpf_attr; #include <linux/quota.h> #include <linux/key.h> #include <trace/syscall.h> +#include <linux/stat.h>
/* * __MAP - apply a macro to syscall arguments @@ -405,10 +404,10 @@ asmlinkage long sys_lstat(const char __user *filename, asmlinkage long sys_fstat(unsigned int fd, struct __old_kernel_stat __user *statbuf); asmlinkage long sys_newstat(const char __user *filename, - struct stat __user *statbuf); + struct __kernel_stat __user *statbuf); asmlinkage long sys_newlstat(const char __user *filename, - struct stat __user *statbuf); -asmlinkage long sys_newfstat(unsigned int fd, struct stat __user *statbuf); + struct __kernel_stat __user *statbuf); +asmlinkage long sys_newfstat(unsigned int fd, struct __kernel_stat __user *statbuf); asmlinkage long sys_ustat(unsigned dev, struct ustat __user *ubuf); #if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64) asmlinkage long sys_stat64(const char __user *filename, @@ -774,7 +773,7 @@ asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user, asmlinkage long sys_openat(int dfd, const char __user *filename, int flags, umode_t mode); asmlinkage long sys_newfstatat(int dfd, const char __user *filename, - struct stat __user *statbuf, int flag); + struct __kernel_stat __user *statbuf, int flag); asmlinkage long sys_readlinkat(int dfd, const char __user *path, char __user *buf, int bufsiz); asmlinkage long sys_utimensat(int dfd, const char __user *filename, diff --git a/include/uapi/asm-generic/kernel_stat.h b/include/uapi/asm-generic/kernel_stat.h new file mode 100644 index 000000000000..d1db22583046 --- /dev/null +++ b/include/uapi/asm-generic/kernel_stat.h @@ -0,0 +1,36 @@ +#ifndef __ASM_GENERIC_KERNEL_STAT_H +#define __ASM_GENERIC_KERNEL_STAT_H + +/* + * The new structure that works on both 32-bit and 64-bit and survives y2038 + * The layout matches 'struct stat' from asm-generic/stat.h on 64-bit + * architecture, but is identical on 32-bit architectures and uses 64-bit + * st_?time members so we don't wrap around in 2038. + */ + +#ifndef __kernel_stat +struct __kernel_stat { + unsigned long long st_dev; /* Device. */ + unsigned long long st_ino; /* File serial number. */ + unsigned int st_mode; /* File mode. */ + unsigned int st_nlink; /* Link count. */ + unsigned int st_uid; /* User ID of the file's owner. */ + unsigned int st_gid; /* Group ID of the file's group. */ + unsigned long long st_rdev; /* Device number, if device. */ + unsigned long long __pad1; + long long st_size; /* Size of file, in bytes. */ + int st_blksize; /* Optimal block size for I/O. */ + int __pad2; + long long st_blocks; /* Number 512-byte blocks allocated. */ + long long st_atime; /* Time of last access. */ + unsigned long long st_atime_nsec; + long long st_mtime; /* Time of last modification. */ + unsigned long long st_mtime_nsec; + long long st_ctime; /* Time of last status change. */ + unsigned long long st_ctime_nsec; + unsigned int __unused4; + unsigned int __unused5; +}; +#endif + +#endif /* __ASM_GENERIC_KERNEL_STAT_H */ diff --git a/include/uapi/asm-generic/stat.h b/include/uapi/asm-generic/stat.h index bd8cad21998e..64c32ba7c1a9 100644 --- a/include/uapi/asm-generic/stat.h +++ b/include/uapi/asm-generic/stat.h @@ -8,15 +8,17 @@ * * stat64 is copied from powerpc64, with explicit padding added. * stat is the same structure layout on 64-bit, without the 'long long' - * types. + * types. Unfortunately, we started out using '32-bit st_*time here, + * which was a huge mistake. * - * By convention, 64 bit architectures use the stat interface, while - * 32 bit architectures use the stat64 interface. Note that we don't - * provide an __old_kernel_stat here, which new architecture should - * not have to start with. + * New architectures use only the __kernel_stat interface with + * 'sys_newfstatat', everything else is provided for backwards + * compatibility. Note that we don't provide an __old_kernel_stat here, + * which new architecture should not have to start with. */
#include <asm/bitsperlong.h> +#include <asm-generic/kernel_stat.h>
#define STAT_HAVE_NSEC 1
This moves over the sys_new{stat,lstat,fstat,fstatat} system calls to use the newly introduced __kernel_stat. On all 64-bit architectures, the layout is the same as 'struct stat', so nothing changes.
On 32-bit architectures, we use a trick to '#define __kernel_stat stat' as long as CONFIG_COMPAT_TIME is not set, and hide the new definitions from the kernel as long as this is the case, so the behavior does not change until we change over the architectures individually to use compat_sys_stat() in place of sys_stat() so we can reuse the sys_stat() implementation for the new structure.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- fs/stat.c | 12 ++++++------ include/linux/stat.h | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/fs/stat.c b/fs/stat.c index cccc1aab9a8b..12efbdd258fc 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -226,9 +226,9 @@ SYSCALL_DEFINE2(fstat, unsigned int, fd, struct __old_kernel_stat __user *, stat # define INIT_STRUCT_STAT_PADDING(st) memset(&st, 0, sizeof(st)) #endif
-static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf) +static int cp_new_stat(struct kstat *stat, struct __kernel_stat __user *statbuf) { - struct stat tmp; + struct __kernel_stat tmp;
if (!valid_dev(stat->dev) || !valid_dev(stat->rdev)) return -EOVERFLOW; @@ -264,7 +264,7 @@ static int cp_new_stat(struct kstat *stat, struct stat __user *statbuf) }
SYSCALL_DEFINE2(newstat, const char __user *, filename, - struct stat __user *, statbuf) + struct __kernel_stat __user *, statbuf) { struct kstat stat; int error = vfs_stat(filename, &stat); @@ -275,7 +275,7 @@ SYSCALL_DEFINE2(newstat, const char __user *, filename, }
SYSCALL_DEFINE2(newlstat, const char __user *, filename, - struct stat __user *, statbuf) + struct __kernel_stat __user *, statbuf) { struct kstat stat; int error; @@ -289,7 +289,7 @@ SYSCALL_DEFINE2(newlstat, const char __user *, filename,
#if !defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_SYS_NEWFSTATAT) SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename, - struct stat __user *, statbuf, int, flag) + struct __kernel_stat __user *, statbuf, int, flag) { struct kstat stat; int error; @@ -301,7 +301,7 @@ SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename, } #endif
-SYSCALL_DEFINE2(newfstat, unsigned int, fd, struct stat __user *, statbuf) +SYSCALL_DEFINE2(newfstat, unsigned int, fd, struct __kernel_stat __user *, statbuf) { struct kstat stat; int error = vfs_fstat(fd, &stat); diff --git a/include/linux/stat.h b/include/linux/stat.h index 075cb0c7eb2a..9efc32774e98 100644 --- a/include/linux/stat.h +++ b/include/linux/stat.h @@ -1,6 +1,9 @@ #ifndef _LINUX_STAT_H #define _LINUX_STAT_H
+#ifndef CONFIG_COMPAT_TIME +#define __kernel_stat stat +#endif
#include <asm/stat.h> #include <uapi/linux/stat.h>
'struct rusage' is not compatible with user space that defines time_t as 64-bit. While there will never be an overflow of the timeval members in this structure, the current definition breaks any program that relies on the member to be a timeval.
This introduces a new struct __kernel_rusage that is defined to match the 64-bit version of struct rusage. 32-bit architectures that use CONFIG_COMPAT_TIME can then use compat_sys_getrusage() etc to get the original structure, while the normal sys_getrusage() function will provide the new layout in both native and compat32 mode.
On 32-bit architectures that do not set CONFIG_COMPAT_TIME, as well as all 64-bit architectures, this patch is intented to have no user-visible impact.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- arch/alpha/kernel/osf_sys.c | 4 ++-- include/linux/compat_time.h | 3 +-- include/linux/resource.h | 8 ++++++-- include/linux/syscalls.h | 7 +++---- include/uapi/linux/resource.h | 32 ++++++++++++++++++++++++++++++++ kernel/compat.c | 42 +++++++++++++++++++++++++++++++++++++----- kernel/exit.c | 6 +++--- kernel/sys.c | 23 ++++++++++++++--------- 8 files changed, 98 insertions(+), 27 deletions(-)
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index e51f578636a5..6824e68d32ad 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -1166,7 +1166,7 @@ SYSCALL_DEFINE2(osf_getrusage, int, who, struct rusage32 __user *, ru) SYSCALL_DEFINE4(osf_wait4, pid_t, pid, int __user *, ustatus, int, options, struct rusage32 __user *, ur) { - struct rusage r; + struct __kernel_rusage r; long ret, err; unsigned int status = 0; mm_segment_t old_fs; @@ -1178,7 +1178,7 @@ SYSCALL_DEFINE4(osf_wait4, pid_t, pid, int __user *, ustatus, int, options, set_fs (KERNEL_DS); ret = sys_wait4(pid, (unsigned int __user *) &status, options, - (struct rusage __user *) &r); + (struct __kernel_rusage __user *) &r); set_fs (old_fs);
if (!access_ok(VERIFY_WRITE, ur, sizeof(*ur))) diff --git a/include/linux/compat_time.h b/include/linux/compat_time.h index 789b6415e90e..e3b43bd79801 100644 --- a/include/linux/compat_time.h +++ b/include/linux/compat_time.h @@ -141,8 +141,7 @@ struct compat_rusage { compat_long_t ru_nivcsw; };
-struct rusage; -extern int put_compat_rusage(const struct rusage *, +extern int put_compat_rusage(const struct __kernel_rusage *, struct compat_rusage __user *);
static inline int compat_timeval_compare(struct compat_timeval *lhs, diff --git a/include/linux/resource.h b/include/linux/resource.h index 5bc3116e649c..8cdecf204607 100644 --- a/include/linux/resource.h +++ b/include/linux/resource.h @@ -1,12 +1,16 @@ #ifndef _LINUX_RESOURCE_H #define _LINUX_RESOURCE_H
-#include <uapi/linux/resource.h> +#ifndef CONFIG_COMPAT_TIME +#define __kernel_rusage rusage +#endif
+#include <uapi/linux/resource.h>
struct task_struct;
-int getrusage(struct task_struct *p, int who, struct rusage __user *ru); +int getrusage(struct task_struct *p, int who, + struct __kernel_rusage __user *ru); int do_prlimit(struct task_struct *tsk, unsigned int resource, struct rlimit *new_rlim, struct rlimit *old_rlim);
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 71b574b0365e..f3fdc312627b 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -36,7 +36,6 @@ struct old_utsname; struct pollfd; struct rlimit; struct rlimit64; -struct rusage; struct sched_param; struct sched_attr; struct sel_arg_struct; @@ -325,10 +324,10 @@ asmlinkage long sys_kexec_file_load(int kernel_fd, int initrd_fd, asmlinkage long sys_exit(int error_code); asmlinkage long sys_exit_group(int error_code); asmlinkage long sys_wait4(pid_t pid, int __user *stat_addr, - int options, struct rusage __user *ru); + int options, struct __kernel_rusage __user *ru); asmlinkage long sys_waitid(int which, pid_t pid, struct siginfo __user *infop, - int options, struct rusage __user *ru); + int options, struct __kernel_rusage __user *ru); asmlinkage long sys_waitpid(pid_t pid, int __user *stat_addr, int options); asmlinkage long sys_set_tid_address(int __user *tidptr); asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val, @@ -650,7 +649,7 @@ asmlinkage long sys_setrlimit(unsigned int resource, asmlinkage long sys_prlimit64(pid_t pid, unsigned int resource, const struct rlimit64 __user *new_rlim, struct rlimit64 __user *old_rlim); -asmlinkage long sys_getrusage(int who, struct rusage __user *ru); +asmlinkage long sys_getrusage(int who, struct __kernel_rusage __user *ru); asmlinkage long sys_umask(int mask);
asmlinkage long sys_msgget(key_t key, int msgflg); diff --git a/include/uapi/linux/resource.h b/include/uapi/linux/resource.h index 36fb3b5fb181..c4f3ba44db00 100644 --- a/include/uapi/linux/resource.h +++ b/include/uapi/linux/resource.h @@ -39,6 +39,38 @@ struct rusage { __kernel_long_t ru_nivcsw; /* involuntary " */ };
+ +/* + * __kernel_rusage replaces rusage, and matches the layout of 64-bit rusage + * on both 32-bit and 64-bit machines, to let 32-bit user space migrate to + * 64-bit tv_sec. + */ +#ifndef __kernel_rusage +struct __kernel_rusage_timeval { + __s64 tv_sec; + __s64 tv_usec; +}; + +struct __kernel_rusage { + struct __kernel_rusage_timeval ru_utime; /* user time used */ + struct __kernel_rusage_timeval ru_stime; /* system time used */ + __s64 ru_maxrss; /* maximum resident set size */ + __s64 ru_ixrss; /* integral shared memory size */ + __s64 ru_idrss; /* integral unshared data size */ + __s64 ru_isrss; /* integral unshared stack size */ + __s64 ru_minflt; /* page reclaims */ + __s64 ru_majflt; /* page faults */ + __s64 ru_nswap; /* swaps */ + __s64 ru_inblock; /* block input operations */ + __s64 ru_oublock; /* block output operations */ + __s64 ru_msgsnd; /* messages sent */ + __s64 ru_msgrcv; /* messages received */ + __s64 ru_nsignals; /* signals received */ + __s64 ru_nvcsw; /* voluntary context switches */ + __s64 ru_nivcsw; /* involuntary " */ +}; +#endif + struct rlimit { __kernel_ulong_t rlim_cur; __kernel_ulong_t rlim_max; diff --git a/kernel/compat.c b/kernel/compat.c index 03bb63995027..e56ee6a23c0f 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -517,7 +517,7 @@ COMPAT_SYSCALL_DEFINE2(getrlimit, unsigned int, resource, #endif
#ifdef CONFIG_COMPAT_TIME -int put_compat_rusage(const struct rusage *r, struct compat_rusage __user *ru) +int put_compat_rusage(const struct __kernel_rusage *r, struct compat_rusage __user *ru) { if (!access_ok(VERIFY_WRITE, ru, sizeof(*ru)) || __put_user(r->ru_utime.tv_sec, &ru->ru_utime.tv_sec) || @@ -551,7 +551,7 @@ COMPAT_SYSCALL_DEFINE4(wait4, if (!ru) { return sys_wait4(pid, stat_addr, options, NULL); } else { - struct rusage r; + struct __kernel_rusage r; int ret; unsigned int status; mm_segment_t old_fs = get_fs(); @@ -560,7 +560,7 @@ COMPAT_SYSCALL_DEFINE4(wait4, ret = sys_wait4(pid, (stat_addr ? (unsigned int __user *) &status : NULL), - options, (struct rusage __user *) &r); + options, (struct __kernel_rusage __user *) &r); set_fs (old_fs);
if (ret > 0) { @@ -579,7 +579,7 @@ COMPAT_SYSCALL_DEFINE5(waitid, struct compat_rusage __user *, uru) { siginfo_t info; - struct rusage ru; + struct __kernel_rusage ru; long ret; mm_segment_t old_fs = get_fs();
@@ -587,7 +587,7 @@ COMPAT_SYSCALL_DEFINE5(waitid,
set_fs(KERNEL_DS); ret = sys_waitid(which, pid, (siginfo_t __user *)&info, options, - uru ? (struct rusage __user *)&ru : NULL); + uru ? (struct __kernel_rusage __user *)&ru : NULL); set_fs(old_fs);
if ((ret < 0) || (info.si_signo == 0)) @@ -610,6 +610,38 @@ COMPAT_SYSCALL_DEFINE5(waitid, #endif
#ifdef CONFIG_COMPAT +COMPAT_SYSCALL_DEFINE5(waitid_time64, + int, which, compat_pid_t, pid, + struct compat_siginfo __user *, uinfo, int, options, + struct compat_rusage __user *, uru) +{ + siginfo_t info; + struct __kernel_rusage ru; + long ret; + mm_segment_t old_fs = get_fs(); + + memset(&info, 0, sizeof(info)); + + set_fs(KERNEL_DS); + ret = sys_waitid(which, pid, (siginfo_t __user *)&info, options, + uru ? (struct __kernel_rusage __user *)&ru : NULL); + set_fs(old_fs); + + if ((ret < 0) || (info.si_signo == 0)) + return ret; + + if (uru) { + /* sys_waitid() overwrites everything in ru */ + ret = copy_to_user(uru, &ru, sizeof(ru)); + if (ret) + return -EFAULT; + } + + BUG_ON(info.si_code & __SI_MASK); + info.si_code |= __SI_CHLD; + return copy_siginfo_to_user32(uinfo, &info); +} + static int compat_get_user_cpu_mask(compat_ulong_t __user *user_mask_ptr, unsigned len, struct cpumask *new_mask) { diff --git a/kernel/exit.c b/kernel/exit.c index 22fcc05dec40..bf2de12e6ae1 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -894,7 +894,7 @@ struct wait_opts {
struct siginfo __user *wo_info; int __user *wo_stat; - struct rusage __user *wo_rusage; + struct __kernel_rusage __user *wo_rusage;
wait_queue_t child_wait; int notask_error; @@ -1514,7 +1514,7 @@ end: }
SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *, - infop, int, options, struct rusage __user *, ru) + infop, int, options, struct __kernel_rusage __user *, ru) { struct wait_opts wo; struct pid *pid = NULL; @@ -1582,7 +1582,7 @@ SYSCALL_DEFINE5(waitid, int, which, pid_t, upid, struct siginfo __user *, }
SYSCALL_DEFINE4(wait4, pid_t, upid, int __user *, stat_addr, - int, options, struct rusage __user *, ru) + int, options, struct __kernel_rusage __user *, ru) { struct wait_opts wo; struct pid *pid = NULL; diff --git a/kernel/sys.c b/kernel/sys.c index a4e372b798a5..e578f33a286e 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1529,7 +1529,7 @@ SYSCALL_DEFINE2(setrlimit, unsigned int, resource, struct rlimit __user *, rlim) * */
-static void accumulate_thread_rusage(struct task_struct *t, struct rusage *r) +static void accumulate_thread_rusage(struct task_struct *t, struct __kernel_rusage *r) { r->ru_nvcsw += t->nvcsw; r->ru_nivcsw += t->nivcsw; @@ -1539,12 +1539,13 @@ static void accumulate_thread_rusage(struct task_struct *t, struct rusage *r) r->ru_oublock += task_io_get_oublock(t); }
-static void k_getrusage(struct task_struct *p, int who, struct rusage *r) +static void k_getrusage(struct task_struct *p, int who, struct __kernel_rusage *r) { struct task_struct *t; unsigned long flags; cputime_t tgutime, tgstime, utime, stime; unsigned long maxrss = 0; + struct timeval tv;
memset((char *)r, 0, sizeof (*r)); utime = stime = 0; @@ -1599,8 +1600,12 @@ static void k_getrusage(struct task_struct *p, int who, struct rusage *r) unlock_task_sighand(p, &flags);
out: - cputime_to_timeval(utime, &r->ru_utime); - cputime_to_timeval(stime, &r->ru_stime); + cputime_to_timeval(utime, &tv); + r->ru_utime.tv_sec = tv.tv_sec; + r->ru_utime.tv_usec = tv.tv_usec; + cputime_to_timeval(stime, &tv); + r->ru_stime.tv_sec = tv.tv_sec; + r->ru_stime.tv_usec = tv.tv_usec;
if (who != RUSAGE_CHILDREN) { struct mm_struct *mm = get_task_mm(p); @@ -1613,15 +1618,15 @@ out: r->ru_maxrss = maxrss * (PAGE_SIZE / 1024); /* convert pages to KBs */ }
-int getrusage(struct task_struct *p, int who, struct rusage __user *ru) +int getrusage(struct task_struct *p, int who, struct __kernel_rusage __user *ru) { - struct rusage r; + struct __kernel_rusage r;
k_getrusage(p, who, &r); return copy_to_user(ru, &r, sizeof(r)) ? -EFAULT : 0; }
-SYSCALL_DEFINE2(getrusage, int, who, struct rusage __user *, ru) +SYSCALL_DEFINE2(getrusage, int, who, struct __kernel_rusage __user *, ru) { if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN && who != RUSAGE_THREAD) @@ -1629,10 +1634,10 @@ SYSCALL_DEFINE2(getrusage, int, who, struct rusage __user *, ru) return getrusage(current, who, ru); }
-#ifdef CONFIG_COMPAT +#ifdef CONFIG_COMPAT_TIME COMPAT_SYSCALL_DEFINE2(getrusage, int, who, struct compat_rusage __user *, ru) { - struct rusage r; + struct __kernel_rusage r;
if (who != RUSAGE_SELF && who != RUSAGE_CHILDREN && who != RUSAGE_THREAD)
These are new helper functions that convert between a user compat_timespec and a kernel timespec64 structure.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/compat_time.h | 2 ++ kernel/compat.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+)
diff --git a/include/linux/compat_time.h b/include/linux/compat_time.h index e3b43bd79801..d91d0d9c753b 100644 --- a/include/linux/compat_time.h +++ b/include/linux/compat_time.h @@ -112,6 +112,8 @@ extern int compat_get_timespec(struct timespec *, const void __user *); extern int compat_put_timespec(const struct timespec *, void __user *); extern int compat_get_timeval(struct timeval *, const void __user *); extern int compat_put_timeval(const struct timeval *, void __user *); +extern int compat_get_timespec64(struct timespec64 *ts, const void __user *uts); +extern int compat_put_timespec64(const struct timespec64 *ts, void __user *uts);
/* * This function convert a timespec if necessary and returns a *user diff --git a/kernel/compat.c b/kernel/compat.c index e56ee6a23c0f..d8d0a4711cc7 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -157,6 +157,20 @@ static int __compat_put_timespec(const struct timespec *ts, struct compat_timesp __put_user(ts->tv_nsec, &cts->tv_nsec)) ? -EFAULT : 0; }
+static int __compat_get_timespec64(struct timespec64 *ts, const struct compat_timespec __user *cts) +{ + return (!access_ok(VERIFY_READ, cts, sizeof(*cts)) || + __get_user(ts->tv_sec, &cts->tv_sec) || + __get_user(ts->tv_nsec, &cts->tv_nsec)) ? -EFAULT : 0; +} + +static int __compat_put_timespec64(const struct timespec64 *ts, struct compat_timespec __user *cts) +{ + return (!access_ok(VERIFY_WRITE, cts, sizeof(*cts)) || + __put_user(ts->tv_sec, &cts->tv_sec) || + __put_user(ts->tv_nsec, &cts->tv_nsec)) ? -EFAULT : 0; +} + int compat_get_timeval(struct timeval *tv, const void __user *utv) { if (COMPAT_USE_64BIT_TIME) @@ -193,6 +207,24 @@ int compat_put_timespec(const struct timespec *ts, void __user *uts) } EXPORT_SYMBOL_GPL(compat_put_timespec);
+int compat_get_timespec64(struct timespec64 *ts, const void __user *uts) +{ + if (COMPAT_USE_64BIT_TIME) + return copy_from_user(ts, uts, sizeof(*ts)) ? -EFAULT : 0; + else + return __compat_get_timespec64(ts, uts); +} +EXPORT_SYMBOL_GPL(compat_get_timespec64); + +int compat_put_timespec64(const struct timespec64 *ts, void __user *uts) +{ + if (COMPAT_USE_64BIT_TIME) + return copy_to_user(uts, ts, sizeof(*ts)) ? -EFAULT : 0; + else + return __compat_put_timespec64(ts, uts); +} +EXPORT_SYMBOL_GPL(compat_put_timespec64); + #ifdef CONFIG_COMPAT int compat_convert_timespec(struct timespec __user **kts, const void __user *cts)
This moves the compat_sys_semtimedop function to ipc/sem.c so it can be shared with 32-bit architectures efficiently. Instead of copying the timespec back to user space, we take a shortcut and pass the jiffies value to the low-level implementation directly.
The native sys_semtimedop() function is modified to take a __kernel_timespec structure, which will be based on a 64-bit time_t in the future.
There is a small API change here: if multiple errors are present, and the timespec argument is invalid (bad pointer or bad tv_nsec), we now return that error before checking any of the other error conditions. If that is a problem, we need a more sophisticated approach.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/syscalls.h | 2 +- ipc/compat.c | 10 -------- ipc/sem.c | 60 ++++++++++++++++++++++++++++++++++-------------- ipc/syscall.c | 7 ++++++ 4 files changed, 51 insertions(+), 28 deletions(-)
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index f3fdc312627b..c2a70a8f907d 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -665,7 +665,7 @@ asmlinkage long sys_semop(int semid, struct sembuf __user *sops, asmlinkage long sys_semctl(int semid, int semnum, int cmd, unsigned long arg); asmlinkage long sys_semtimedop(int semid, struct sembuf __user *sops, unsigned nsops, - const struct timespec __user *timeout); + const struct __kernel_timespec __user *timeout); asmlinkage long sys_shmat(int shmid, char __user *shmaddr, int shmflg); asmlinkage long sys_shmget(key_t key, size_t size, int flag); asmlinkage long sys_shmdt(char __user *shmaddr); diff --git a/ipc/compat.c b/ipc/compat.c index 9b3c85f8a538..2bbdb093d1be 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -745,13 +745,3 @@ COMPAT_SYSCALL_DEFINE3(shmctl, int, first, int, second, void __user *, uptr) } return err; } - -COMPAT_SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsems, - unsigned, nsops, - const struct compat_timespec __user *, timeout) -{ - struct timespec __user *ts64; - if (compat_convert_timespec(&ts64, timeout)) - return -EFAULT; - return sys_semtimedop(semid, tsems, nsops, ts64); -} diff --git a/ipc/sem.c b/ipc/sem.c index d1a6edd17eba..a6ff6754651c 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -72,6 +72,7 @@ * The worst-case behavior is nevertheless O(N^2) for N wakeups. */
+#include <linux/compat.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/init.h> @@ -1779,8 +1780,9 @@ static int get_queue_result(struct sem_queue *q) return error; }
-SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, - unsigned, nsops, const struct timespec __user *, timeout) +static long semtimedop(int semid, struct sembuf __user * tsops, + unsigned nsops, unsigned long jiffies_left, + bool timeout) { int error = -EINVAL; struct sem_array *sma; @@ -1789,7 +1791,6 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, struct sem_undo *un; int undos = 0, alter = 0, max, locknum; struct sem_queue queue; - unsigned long jiffies_left = 0; struct ipc_namespace *ns; struct list_head tasks;
@@ -1808,19 +1809,6 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, error = -EFAULT; goto out_free; } - if (timeout) { - struct timespec _timeout; - if (copy_from_user(&_timeout, timeout, sizeof(*timeout))) { - error = -EFAULT; - goto out_free; - } - if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 || - _timeout.tv_nsec >= 1000000000L) { - error = -EINVAL; - goto out_free; - } - jiffies_left = timespec_to_jiffies(&_timeout); - } max = 0; for (sop = sops; sop < sops + nsops; sop++) { if (sop->sem_num >= max) @@ -2014,10 +2002,48 @@ out_free: return error; }
+SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, + unsigned, nsops, + const struct __kernel_timespec __user *, timeout) +{ + unsigned long jiffies_left = 0; + + if (timeout) { + struct timespec64 _timeout; + if (get_timespec64(&_timeout, timeout)) + return -EFAULT; + if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 || + _timeout.tv_nsec >= 1000000000L) + return -EINVAL; + jiffies_left = nsecs_to_jiffies(timespec64_to_ns(&_timeout)); + } + return semtimedop(semid, tsops, nsops, jiffies_left, timeout); +} + +#ifdef CONFIG_COMPAT_TIME +COMPAT_SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, + unsigned, nsops, + const struct compat_timespec __user *, timeout) +{ + unsigned long jiffies_left = 0; + + if (timeout) { + struct timespec64 _timeout; + if (compat_get_timespec64(&_timeout, timeout)) + return -EFAULT; + if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 || + _timeout.tv_nsec >= 1000000000L) + return -EINVAL; + jiffies_left = nsecs_to_jiffies(timespec64_to_ns(&_timeout)); + } + return semtimedop(semid, tsops, nsops, jiffies_left, timeout); +} +#endif + SYSCALL_DEFINE3(semop, int, semid, struct sembuf __user *, tsops, unsigned, nsops) { - return sys_semtimedop(semid, tsops, nsops, NULL); + return semtimedop(semid, tsops, nsops, 0, 0); }
/* If CLONE_SYSVSEM is set, establish sharing of SEM_UNDO state between diff --git a/ipc/syscall.c b/ipc/syscall.c index 52429489cde0..d7b17355d870 100644 --- a/ipc/syscall.c +++ b/ipc/syscall.c @@ -7,6 +7,7 @@ #include <linux/unistd.h>
#ifdef __ARCH_WANT_SYS_IPC +#include <linux/compat_time.h> #include <linux/errno.h> #include <linux/ipc.h> #include <linux/shm.h> @@ -26,9 +27,15 @@ SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second, return sys_semtimedop(first, (struct sembuf __user *)ptr, second, NULL); case SEMTIMEDOP: +#if defined(CONFIG_ARCH_HAS_COMPAT_TIME) && !defined(CONFIG_64BIT) + return compat_sys_semtimedop(first, (struct sembuf __user *)ptr, + second, + (const struct compat_timespec __user *)fifth); +#else return sys_semtimedop(first, (struct sembuf __user *)ptr, second, (const struct timespec __user *)fifth); +#endif
case SEMGET: return sys_semget(first, second, third);
On Wed, 6 May 2015, Arnd Bergmann wrote:
+SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
unsigned, nsops,
const struct __kernel_timespec __user *, timeout)
+{
- unsigned long jiffies_left = 0;
- if (timeout) {
struct timespec64 _timeout;
if (get_timespec64(&_timeout, timeout))
Moo. I had to look 3 times to get not confused by the extra underscore. What's wrong with a proper variable name which is easy to distinguish?
return -EFAULT;
if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 ||
_timeout.tv_nsec >= 1000000000L)
return -EINVAL;
We have proper helper functions to validate time specs.
Thanks,
tglx
On Saturday 16 May 2015 00:46:44 Thomas Gleixner wrote:
On Wed, 6 May 2015, Arnd Bergmann wrote:
+SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
unsigned, nsops,
const struct __kernel_timespec __user *, timeout)
+{
unsigned long jiffies_left = 0;
if (timeout) {
struct timespec64 _timeout;
if (get_timespec64(&_timeout, timeout))
Moo. I had to look 3 times to get not confused by the extra underscore. What's wrong with a proper variable name which is easy to distinguish?
return -EFAULT;
if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 ||
_timeout.tv_nsec >= 1000000000L)
return -EINVAL;
We have proper helper functions to validate time specs.
I tried to change the existing code as little as possible, but I agree with your points here. I'll add a cleanup patch to fix the current code before my own patches.
Arnd
On Saturday 16 May 2015 00:46:44 Thomas Gleixner wrote:
On Wed, 6 May 2015, Arnd Bergmann wrote:
+SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
unsigned, nsops,
const struct __kernel_timespec __user *, timeout)
+{
- unsigned long jiffies_left = 0;
- if (timeout) {
struct timespec64 _timeout;
if (get_timespec64(&_timeout, timeout))
Moo. I had to look 3 times to get not confused by the extra underscore. What's wrong with a proper variable name which is easy to distinguish?
return -EFAULT;
if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 ||
_timeout.tv_nsec >= 1000000000L)
return -EINVAL;
We have proper helper functions to validate time specs.
I ended up fixing both issues you noticed in the same patch after all, and also simplified it slightly more.
Finally, I also noticed that I had not done a timespec64_to_jiffies() call at the time when I wrote this patch, but it actually exists now, so I've reordered my series and am using it in the new version, as I should have done to start with.
Arnd
8<----
From e04b14d49273c27d92f1799233b82bcdafb43d9a Mon Sep 17 00:00:00 2001
From: Arnd Bergmann arnd@arndb.de Date: Mon, 27 Apr 2015 23:30:39 +0200 Subject: [UPDATED PATCH] y2038: add compat handling for sys_semtimedop
This moves the compat_sys_semtimedop function to ipc/sem.c so it can be shared with 32-bit architectures efficiently. Instead of copying the timespec back to user space, we take a shortcut and pass the kernel timespec64 value to the low-level implementation directly.
The native sys_semtimedop() function is modified to take a __kernel_timespec structure, which will be based on a 64-bit time_t in the future.
There is a small API change here: if multiple errors are present, and the timespec argument is an invalid pointer, we now return -EFAULT before checking any of the other error conditions. This is what the compat version has always done, but if it is a problem, we need a more sophisticated approach.
Signed-off-by: Arnd Bergmann arnd@arndb.de
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index f3fdc312627b..c2a70a8f907d 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -665,7 +665,7 @@ asmlinkage long sys_semop(int semid, struct sembuf __user *sops, asmlinkage long sys_semctl(int semid, int semnum, int cmd, unsigned long arg); asmlinkage long sys_semtimedop(int semid, struct sembuf __user *sops, unsigned nsops, - const struct timespec __user *timeout); + const struct __kernel_timespec __user *timeout); asmlinkage long sys_shmat(int shmid, char __user *shmaddr, int shmflg); asmlinkage long sys_shmget(key_t key, size_t size, int flag); asmlinkage long sys_shmdt(char __user *shmaddr); diff --git a/ipc/compat.c b/ipc/compat.c index 9b3c85f8a538..2bbdb093d1be 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -745,13 +745,3 @@ COMPAT_SYSCALL_DEFINE3(shmctl, int, first, int, second, void __user *, uptr) } return err; } - -COMPAT_SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsems, - unsigned, nsops, - const struct compat_timespec __user *, timeout) -{ - struct timespec __user *ts64; - if (compat_convert_timespec(&ts64, timeout)) - return -EFAULT; - return sys_semtimedop(semid, tsems, nsops, ts64); -} diff --git a/ipc/sem.c b/ipc/sem.c index d1a6edd17eba..84d354a34df3 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -72,6 +72,7 @@ * The worst-case behavior is nevertheless O(N^2) for N wakeups. */
+#include <linux/compat.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/init.h> @@ -1779,8 +1780,8 @@ static int get_queue_result(struct sem_queue *q) return error; }
-SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, - unsigned, nsops, const struct timespec __user *, timeout) +static long semtimedop(int semid, struct sembuf __user * tsops, + unsigned nsops, struct timespec64 *timeout) { int error = -EINVAL; struct sem_array *sma; @@ -1809,17 +1810,11 @@ SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, goto out_free; } if (timeout) { - struct timespec _timeout; - if (copy_from_user(&_timeout, timeout, sizeof(*timeout))) { - error = -EFAULT; - goto out_free; - } - if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 || - _timeout.tv_nsec >= 1000000000L) { + if (!timespec64_valid(timeout)) { error = -EINVAL; goto out_free; } - jiffies_left = timespec_to_jiffies(&_timeout); + jiffies_left = timespec64_to_jiffies(timeout); } max = 0; for (sop = sops; sop < sops + nsops; sop++) { @@ -2014,10 +2009,36 @@ out_free: return error; }
+SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, + unsigned, nsops, + const struct __kernel_timespec __user *, p) +{ + struct timespec64 timeout; + + if (p && get_timespec64(&timeout, p)) + return -EFAULT; + + return semtimedop(semid, tsops, nsops, p ? &timeout : NULL); +} + +#ifdef CONFIG_COMPAT_TIME +COMPAT_SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops, + unsigned, nsops, + const struct compat_timespec __user *, p) +{ + struct timespec64 timeout; + + if (p && compat_get_timespec64(&timeout, p)) + return -EFAULT; + + return semtimedop(semid, tsops, nsops, p ? &timeout : NULL); +} +#endif + SYSCALL_DEFINE3(semop, int, semid, struct sembuf __user *, tsops, unsigned, nsops) { - return sys_semtimedop(semid, tsops, nsops, NULL); + return semtimedop(semid, tsops, nsops, NULL); }
/* If CLONE_SYSVSEM is set, establish sharing of SEM_UNDO state between diff --git a/ipc/syscall.c b/ipc/syscall.c index 52429489cde0..d7b17355d870 100644 --- a/ipc/syscall.c +++ b/ipc/syscall.c @@ -7,6 +7,7 @@ #include <linux/unistd.h>
#ifdef __ARCH_WANT_SYS_IPC +#include <linux/compat_time.h> #include <linux/errno.h> #include <linux/ipc.h> #include <linux/shm.h> @@ -26,9 +27,15 @@ SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second, return sys_semtimedop(first, (struct sembuf __user *)ptr, second, NULL); case SEMTIMEDOP: +#if defined(CONFIG_ARCH_HAS_COMPAT_TIME) && !defined(CONFIG_64BIT) + return compat_sys_semtimedop(first, (struct sembuf __user *)ptr, + second, + (const struct compat_timespec __user *)fifth); +#else return sys_semtimedop(first, (struct sembuf __user *)ptr, second, (const struct timespec __user *)fifth); +#endif
case SEMGET: return sys_semget(first, second, third);
On Sat, 16 May 2015, Arnd Bergmann wrote:
On Saturday 16 May 2015 00:46:44 Thomas Gleixner wrote:
On Wed, 6 May 2015, Arnd Bergmann wrote:
+SYSCALL_DEFINE4(semtimedop, int, semid, struct sembuf __user *, tsops,
unsigned, nsops,
const struct __kernel_timespec __user *, timeout)
+{
- unsigned long jiffies_left = 0;
- if (timeout) {
struct timespec64 _timeout;
if (get_timespec64(&_timeout, timeout))
Moo. I had to look 3 times to get not confused by the extra underscore. What's wrong with a proper variable name which is easy to distinguish?
return -EFAULT;
if (_timeout.tv_sec < 0 || _timeout.tv_nsec < 0 ||
_timeout.tv_nsec >= 1000000000L)
return -EINVAL;
We have proper helper functions to validate time specs.
I ended up fixing both issues you noticed in the same patch after all, and also simplified it slightly more.
Finally, I also noticed that I had not done a timespec64_to_jiffies() call at the time when I wrote this patch, but it actually exists now, so I've reordered my series and am using it in the new version, as I should have done to start with.
Indeed. I didn't notice either.
From e04b14d49273c27d92f1799233b82bcdafb43d9a Mon Sep 17 00:00:00 2001
From: Arnd Bergmann arnd@arndb.de Date: Mon, 27 Apr 2015 23:30:39 +0200 Subject: [UPDATED PATCH] y2038: add compat handling for sys_semtimedop
This moves the compat_sys_semtimedop function to ipc/sem.c so it can be shared with 32-bit architectures efficiently. Instead of copying the timespec back to user space, we take a shortcut and pass the kernel timespec64 value to the low-level implementation directly.
The native sys_semtimedop() function is modified to take a __kernel_timespec structure, which will be based on a 64-bit time_t in the future.
There is a small API change here: if multiple errors are present, and the timespec argument is an invalid pointer, we now return -EFAULT before checking any of the other error conditions. This is what the compat version has always done, but if it is a problem, we need a more sophisticated approach.
The important part of error checks is that they catch all cases and combinations. In which order is completely irrelevant.
If something relies on the ordering of error check returns, it's broken by definition.
Signed-off-by: Arnd Bergmann arnd@arndb.de
Reviewed-by: Thomas Gleixner tglx@linutronix.de
This performs a complete conversion of the ipc/mqueue code to use 64-bit time_t both internally and on the user-facing side of 32-bit architectures that use CONFIG_COMPAT_TIME. For compatibility with existing user space, these now use compat_sys_mq_timed{send,receive}.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/audit.h | 4 ++-- include/linux/syscalls.h | 4 ++-- ipc/mqueue.c | 16 ++++++++-------- kernel/audit.h | 2 +- kernel/auditsc.c | 14 +++++++------- 5 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/include/linux/audit.h b/include/linux/audit.h index c2e7e3a83965..ab696322bb72 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -242,7 +242,7 @@ extern int __audit_socketcall(int nargs, unsigned long *args); extern int __audit_sockaddr(int len, void *addr); extern void __audit_fd_pair(int fd1, int fd2); extern void __audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr); -extern void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout); +extern void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec64 *abs_timeout); extern void __audit_mq_notify(mqd_t mqdes, const struct sigevent *notification); extern void __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat); extern int __audit_log_bprm_fcaps(struct linux_binprm *bprm, @@ -288,7 +288,7 @@ static inline void audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr) if (unlikely(!audit_dummy_context())) __audit_mq_open(oflag, mode, attr); } -static inline void audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout) +static inline void audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec64 *abs_timeout) { if (unlikely(!audit_dummy_context())) __audit_mq_sendrecv(mqdes, msg_len, msg_prio, abs_timeout); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index c2a70a8f907d..c3f15b5a9b79 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -675,8 +675,8 @@ asmlinkage long sys_ipc(unsigned int call, int first, unsigned long second,
asmlinkage long sys_mq_open(const char __user *name, int oflag, umode_t mode, struct mq_attr __user *attr); asmlinkage long sys_mq_unlink(const char __user *name); -asmlinkage long sys_mq_timedsend(mqd_t mqdes, const char __user *msg_ptr, size_t msg_len, unsigned int msg_prio, const struct timespec __user *abs_timeout); -asmlinkage long sys_mq_timedreceive(mqd_t mqdes, char __user *msg_ptr, size_t msg_len, unsigned int __user *msg_prio, const struct timespec __user *abs_timeout); +asmlinkage long sys_mq_timedsend(mqd_t mqdes, const char __user *msg_ptr, size_t msg_len, unsigned int msg_prio, const struct __kernel_timespec __user *abs_timeout); +asmlinkage long sys_mq_timedreceive(mqd_t mqdes, char __user *msg_ptr, size_t msg_len, unsigned int __user *msg_prio, const struct __kernel_timespec __user *abs_timeout); asmlinkage long sys_mq_notify(mqd_t mqdes, const struct sigevent __user *notification); asmlinkage long sys_mq_getsetattr(mqd_t mqdes, const struct mq_attr __user *mqstat, struct mq_attr __user *omqstat);
diff --git a/ipc/mqueue.c b/ipc/mqueue.c index 3aaea7ffd077..f3f27ad1ca44 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -672,15 +672,15 @@ static void __do_notify(struct mqueue_inode_info *info) wake_up(&info->wait_q); }
-static int prepare_timeout(const struct timespec __user *u_abs_timeout, - ktime_t *expires, struct timespec *ts) +static int prepare_timeout(const struct __kernel_timespec __user *u_abs_timeout, + ktime_t *expires, struct timespec64 *ts) { - if (copy_from_user(ts, u_abs_timeout, sizeof(struct timespec))) + if (get_timespec64(ts, u_abs_timeout)) return -EFAULT; if (!timespec_valid(ts)) return -EINVAL;
- *expires = timespec_to_ktime(*ts); + *expires = timespec64_to_ktime(*ts); return 0; }
@@ -953,7 +953,7 @@ static inline void pipelined_receive(struct mqueue_inode_info *info)
SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, size_t, msg_len, unsigned int, msg_prio, - const struct timespec __user *, u_abs_timeout) + const struct __kernel_timespec __user *, u_abs_timeout) { struct fd f; struct inode *inode; @@ -962,7 +962,7 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, struct msg_msg *msg_ptr; struct mqueue_inode_info *info; ktime_t expires, *timeout = NULL; - struct timespec ts; + struct timespec64 ts; struct posix_msg_tree_node *new_leaf = NULL; int ret = 0;
@@ -1073,7 +1073,7 @@ out:
SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr, size_t, msg_len, unsigned int __user *, u_msg_prio, - const struct timespec __user *, u_abs_timeout) + const struct __kernel_timespec __user *, u_abs_timeout) { ssize_t ret; struct msg_msg *msg_ptr; @@ -1082,7 +1082,7 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr, struct mqueue_inode_info *info; struct ext_wait_queue wait; ktime_t expires, *timeout = NULL; - struct timespec ts; + struct timespec64 ts; struct posix_msg_tree_node *new_leaf = NULL;
if (u_abs_timeout) { diff --git a/kernel/audit.h b/kernel/audit.h index d641f9bb3ed0..b83e7aaca647 100644 --- a/kernel/audit.h +++ b/kernel/audit.h @@ -179,7 +179,7 @@ struct audit_context { mqd_t mqdes; size_t msg_len; unsigned int msg_prio; - struct timespec abs_timeout; + struct timespec64 abs_timeout; } mq_sendrecv; struct { int oflag; diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 9fb9d1cb83ce..6af873dcad55 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1230,12 +1230,12 @@ static void show_special(struct audit_context *context, int *call_panic) case AUDIT_MQ_SENDRECV: { audit_log_format(ab, "mqdes=%d msg_len=%zd msg_prio=%u " - "abs_timeout_sec=%ld abs_timeout_nsec=%ld", + "abs_timeout_sec=%lld abs_timeout_nsec=%lld", context->mq_sendrecv.mqdes, context->mq_sendrecv.msg_len, context->mq_sendrecv.msg_prio, - context->mq_sendrecv.abs_timeout.tv_sec, - context->mq_sendrecv.abs_timeout.tv_nsec); + (s64)context->mq_sendrecv.abs_timeout.tv_sec, + (s64)context->mq_sendrecv.abs_timeout.tv_nsec); break; } case AUDIT_MQ_NOTIFY: { audit_log_format(ab, "mqdes=%d sigev_signo=%d", @@ -2062,15 +2062,15 @@ void __audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr) * */ void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, - const struct timespec *abs_timeout) + const struct timespec64 *abs_timeout) { struct audit_context *context = current->audit_context; - struct timespec *p = &context->mq_sendrecv.abs_timeout; + struct timespec64 *p = &context->mq_sendrecv.abs_timeout;
if (abs_timeout) - memcpy(p, abs_timeout, sizeof(struct timespec)); + memcpy(p, abs_timeout, sizeof(struct timespec64)); else - memset(p, 0, sizeof(struct timespec)); + memset(p, 0, sizeof(struct timespec64));
context->mq_sendrecv.mqdes = mqdes; context->mq_sendrecv.msg_len = msg_len;
This is needed to convert do_sigtimedwait to use timespec64.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/jiffies.h | 13 ++++++++++++- kernel/time/time.c | 11 ++--------- 2 files changed, 14 insertions(+), 10 deletions(-)
diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h index c367cbdf73ab..2aeb872f5547 100644 --- a/include/linux/jiffies.h +++ b/include/linux/jiffies.h @@ -290,7 +290,18 @@ static inline u64 jiffies_to_nsecs(const unsigned long j)
extern unsigned long msecs_to_jiffies(const unsigned int m); extern unsigned long usecs_to_jiffies(const unsigned int u); -extern unsigned long timespec_to_jiffies(const struct timespec *value); +extern unsigned long __timespec_to_jiffies(unsigned long sec, long nsec); +static inline unsigned long timespec_to_jiffies(const struct timespec *value) +{ + return __timespec_to_jiffies(value->tv_sec, value->tv_nsec); +} + +static inline unsigned long timespec64_to_jiffies(const struct timespec64 *value) +{ + return __timespec_to_jiffies(value->tv_sec, value->tv_nsec); +} + + extern void jiffies_to_timespec(const unsigned long jiffies, struct timespec *value); extern unsigned long timeval_to_jiffies(const struct timeval *value); diff --git a/kernel/time/time.c b/kernel/time/time.c index 845af1db66fa..4d96236c07b0 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -571,7 +571,7 @@ EXPORT_SYMBOL(usecs_to_jiffies); * The >> (NSEC_JIFFIE_SC - SEC_JIFFIE_SC) converts the scaled nsec * value to a scaled second value. */ -static unsigned long +unsigned long __timespec_to_jiffies(unsigned long sec, long nsec) { nsec = nsec + TICK_NSEC - 1; @@ -585,14 +585,7 @@ __timespec_to_jiffies(unsigned long sec, long nsec) (NSEC_JIFFIE_SC - SEC_JIFFIE_SC))) >> SEC_JIFFIE_SC;
} - -unsigned long -timespec_to_jiffies(const struct timespec *value) -{ - return __timespec_to_jiffies(value->tv_sec, value->tv_nsec); -} - -EXPORT_SYMBOL(timespec_to_jiffies); +EXPORT_SYMBOL(__timespec_to_jiffies);
void jiffies_to_timespec(const unsigned long jiffies, struct timespec *value)
On Wed, 6 May 2015, Arnd Bergmann wrote:
This is needed to convert do_sigtimedwait to use timespec64.
Signed-off-by: Arnd Bergmann arnd@arndb.de
Reviewed-by: Thomas Gleixner tglx@linutronix.de
This is a straightforward conversion of the native and compat sys_rt_sigtimedwait functions to use __kernel_timespec, so 32-bit user space can pass a 64-bit time_t into the native syscall and use the compat syscall for the traditional 32-bit time_t.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/signal.h | 3 ++- include/linux/syscalls.h | 2 +- kernel/compat.c | 10 +++++----- kernel/signal.c | 13 +++++++------ 4 files changed, 15 insertions(+), 13 deletions(-)
diff --git a/include/linux/signal.h b/include/linux/signal.h index ab1e0392b5ac..31970d22f6c1 100644 --- a/include/linux/signal.h +++ b/include/linux/signal.h @@ -2,6 +2,7 @@ #define _LINUX_SIGNAL_H
#include <linux/list.h> +#include <linux/time.h> #include <linux/bug.h> #include <uapi/linux/signal.h>
@@ -234,7 +235,7 @@ extern int do_send_sig_info(int sig, struct siginfo *info, extern int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p); extern int __group_send_sig_info(int, struct siginfo *, struct task_struct *); extern int do_sigtimedwait(const sigset_t *, siginfo_t *, - const struct timespec *); + const struct timespec64 *); extern int sigprocmask(int, sigset_t *, sigset_t *); extern void set_current_blocked(sigset_t *); extern void __set_current_blocked(const sigset_t *); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index c3f15b5a9b79..017aa1c970e6 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -365,7 +365,7 @@ asmlinkage long sys_rt_sigprocmask(int how, sigset_t __user *set, asmlinkage long sys_rt_sigpending(sigset_t __user *set, size_t sigsetsize); asmlinkage long sys_rt_sigtimedwait(const sigset_t __user *uthese, siginfo_t __user *uinfo, - const struct timespec __user *uts, + const struct __kernel_timespec __user *uts, size_t sigsetsize); asmlinkage long sys_rt_tgsigqueueinfo(pid_t tgid, pid_t pid, int sig, siginfo_t __user *uinfo); diff --git a/kernel/compat.c b/kernel/compat.c index d8d0a4711cc7..ec09c6e1d594 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -1081,7 +1081,7 @@ COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait, compat_sigset_t __user *, uthese, { compat_sigset_t s32; sigset_t s; - struct timespec t; + struct timespec64 t; siginfo_t info; long ret;
@@ -1093,7 +1093,7 @@ COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait, compat_sigset_t __user *, uthese, sigset_from_compat(&s, &s32);
if (uts) { - if (compat_get_timespec(&t, uts)) + if (compat_get_timespec64(&t, uts)) return -EFAULT; }
@@ -1115,7 +1115,7 @@ COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait, sigset_t __user *, uthese, struct compat_timespec __user *, uts, size_t, sigsetsize) { sigset_t s; - struct timespec t; + struct timespec64 t; siginfo_t info; long ret;
@@ -1125,7 +1125,7 @@ COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait, sigset_t __user *, uthese, if (copy_from_user(&s, uthese, sizeof(sigset_t))) return -EFAULT; if (uts) { - if (compat_get_timespec(&t, uts)) + if (compat_get_timespec64(&t, uts)) return -EFAULT; }
@@ -1147,7 +1147,7 @@ COMPAT_SYSCALL_DEFINE4(rt_sigtimedwait_time64, compat_sigset_t __user *, uthese, { compat_sigset_t s32; sigset_t s; - struct timespec t; + struct timespec64 t; siginfo_t info; long ret;
diff --git a/kernel/signal.c b/kernel/signal.c index d51c5ddd855c..1f12483ddf9a 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2798,7 +2798,7 @@ int copy_siginfo_to_user(siginfo_t __user *to, const siginfo_t *from) * @ts: upper bound on process time suspension */ int do_sigtimedwait(const sigset_t *which, siginfo_t *info, - const struct timespec *ts) + const struct timespec64 *ts) { struct task_struct *tsk = current; long timeout = MAX_SCHEDULE_TIMEOUT; @@ -2806,9 +2806,9 @@ int do_sigtimedwait(const sigset_t *which, siginfo_t *info, int sig;
if (ts) { - if (!timespec_valid(ts)) + if (!timespec64_valid(ts)) return -EINVAL; - timeout = timespec_to_jiffies(ts); + timeout = timespec64_to_jiffies(ts); /* * We can be close to the next tick, add another one * to ensure we will wait at least the time asked for. @@ -2860,11 +2860,12 @@ int do_sigtimedwait(const sigset_t *which, siginfo_t *info, * @sigsetsize: size of sigset_t type */ SYSCALL_DEFINE4(rt_sigtimedwait, const sigset_t __user *, uthese, - siginfo_t __user *, uinfo, const struct timespec __user *, uts, + siginfo_t __user *, uinfo, + const struct __kernel_timespec __user *, uts, size_t, sigsetsize) { sigset_t these; - struct timespec ts; + struct timespec64 ts; siginfo_t info; int ret;
@@ -2872,7 +2873,7 @@ SYSCALL_DEFINE4(rt_sigtimedwait, const sigset_t __user *, uthese, if (sigsetsize != sizeof(sigset_t)) return -EINVAL;
- if (copy_from_user(&these, uthese, sizeof(these))) + if (get_timespec64(&ts, uts)) return -EFAULT;
if (uts) {
On Wed, 6 May 2015, Arnd Bergmann wrote:
This is a straightforward conversion of the native and compat sys_rt_sigtimedwait functions to use __kernel_timespec, so 32-bit user space can pass a 64-bit time_t into the native syscall and use the compat syscall for the traditional 32-bit time_t.
Signed-off-by: Arnd Bergmann arnd@arndb.de
Reviewed-by: Thomas Gleixner tglx@linutronix.de
Conversion for sys_futex is particularly easy, we can use the unmodified compat_sys_futex on 32-bit systems to provide compatibility for 32-bit time_t, and change sys_futex to pass a __kernel_timespec, which matches what future libc implementations will use as their struct timespec.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/syscalls.h | 4 ++-- kernel/futex.c | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 017aa1c970e6..90c3cd889387 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -331,8 +331,8 @@ asmlinkage long sys_waitid(int which, pid_t pid, asmlinkage long sys_waitpid(pid_t pid, int __user *stat_addr, int options); asmlinkage long sys_set_tid_address(int __user *tidptr); asmlinkage long sys_futex(u32 __user *uaddr, int op, u32 val, - struct timespec __user *utime, u32 __user *uaddr2, - u32 val3); + struct __kernel_timespec __user *utime, + u32 __user *uaddr2, u32 val3);
asmlinkage long sys_init_module(void __user *umod, unsigned long len, const char __user *uargs); diff --git a/kernel/futex.c b/kernel/futex.c index 2579e407ff67..199e4f94a80c 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -2970,10 +2970,10 @@ long do_futex(u32 __user *uaddr, int op, u32 val, ktime_t *timeout,
SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val, - struct timespec __user *, utime, u32 __user *, uaddr2, + struct __kernel_timespec __user *, utime, u32 __user *, uaddr2, u32, val3) { - struct timespec ts; + struct timespec64 ts; ktime_t t, *tp = NULL; u32 val2 = 0; int cmd = op & FUTEX_CMD_MASK; @@ -2981,12 +2981,12 @@ SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val, if (utime && (cmd == FUTEX_WAIT || cmd == FUTEX_LOCK_PI || cmd == FUTEX_WAIT_BITSET || cmd == FUTEX_WAIT_REQUEUE_PI)) { - if (copy_from_user(&ts, utime, sizeof(ts)) != 0) + if (get_timespec64(&ts, utime)) return -EFAULT; - if (!timespec_valid(&ts)) + if (!timespec64_valid(&ts)) return -EINVAL;
- t = timespec_to_ktime(ts); + t = timespec64_to_ktime(ts); if (cmd == FUTEX_WAIT) t = ktime_add_safe(ktime_get(), t); tp = &t;
On Wed, 6 May 2015, Arnd Bergmann wrote:
Conversion for sys_futex is particularly easy, we can use the unmodified compat_sys_futex on 32-bit systems to provide compatibility for 32-bit time_t, and change sys_futex to pass a __kernel_timespec, which matches what future libc implementations will use as their struct timespec.
Unless I'm missing something I doubt that you get away that easy. It works on 32bit, but not on 64 bit with 32bit app support:
Native 64bit: sys_futex() 32bit timespec32: compat_sys_futex() 32bit timespec64: ?????
You cannot map that to sys_futex() because the pointer size differs.
Thanks,
tglx
On Sat, 16 May 2015, Thomas Gleixner wrote:
On Wed, 6 May 2015, Arnd Bergmann wrote:
Conversion for sys_futex is particularly easy, we can use the unmodified compat_sys_futex on 32-bit systems to provide compatibility for 32-bit time_t, and change sys_futex to pass a __kernel_timespec, which matches what future libc implementations will use as their struct timespec.
Unless I'm missing something I doubt that you get away that easy. It works on 32bit, but not on 64 bit with 32bit app support:
Native 64bit: sys_futex() 32bit timespec32: compat_sys_futex() 32bit timespec64: ?????
You cannot map that to sys_futex() because the pointer size differs.
Ignore that. You are right. Review induced brainmelt....
tglx
On Wed, 6 May 2015, Arnd Bergmann wrote:
Conversion for sys_futex is particularly easy, we can use the unmodified compat_sys_futex on 32-bit systems to provide compatibility for 32-bit time_t, and change sys_futex to pass a __kernel_timespec, which matches what future libc implementations will use as their struct timespec.
Signed-off-by: Arnd Bergmann arnd@arndb.de
Reviewed-by: Thomas Gleixner tglx@linutronix.de
Needed for converting sys_sched_rr_get_interval
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/jiffies.h | 12 +++++++++--- kernel/time/time.c | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h index 2aeb872f5547..6647257fa3da 100644 --- a/include/linux/jiffies.h +++ b/include/linux/jiffies.h @@ -301,9 +301,15 @@ static inline unsigned long timespec64_to_jiffies(const struct timespec64 *value return __timespec_to_jiffies(value->tv_sec, value->tv_nsec); }
- -extern void jiffies_to_timespec(const unsigned long jiffies, - struct timespec *value); +extern void jiffies_to_timespec64(const unsigned long jiffies, + struct timespec64 *value); +static inline void jiffies_to_timespec(const unsigned long jiffies, + struct timespec *value) +{ + struct timespec64 ts64; + jiffies_to_timespec64(jiffies, &ts64); + *value = timespec64_to_timespec(ts64); +} extern unsigned long timeval_to_jiffies(const struct timeval *value); extern void jiffies_to_timeval(const unsigned long jiffies, struct timeval *value); diff --git a/kernel/time/time.c b/kernel/time/time.c index 4d96236c07b0..5e71dbd36fff 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -588,7 +588,7 @@ __timespec_to_jiffies(unsigned long sec, long nsec) EXPORT_SYMBOL(__timespec_to_jiffies);
void -jiffies_to_timespec(const unsigned long jiffies, struct timespec *value) +jiffies_to_timespec64(const unsigned long jiffies, struct timespec64 *value) { /* * Convert jiffies to nanoseconds and separate with
On Wed, 6 May 2015, Arnd Bergmann wrote:
Needed for converting sys_sched_rr_get_interval
Signed-off-by: Arnd Bergmann arnd@arndb.de
Reviewed-by: Thomas Gleixner tglx@linutronix.de
sys_sched_rr_get_interval is easily converted to use __kernel_timespec, by changing the function prototype. In order to allow compat handling on 32-bit architectures, we also move compat_sys_sched_getaffinity into the same file and unify the implementation, which avoids converting the structure multiple times.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/syscalls.h | 2 +- kernel/compat.c | 20 -------------------- kernel/sched/core.c | 35 ++++++++++++++++++++++++++++------- 3 files changed, 29 insertions(+), 28 deletions(-)
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 90c3cd889387..ce41abb4cd0a 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -305,7 +305,7 @@ asmlinkage long sys_sched_yield(void); asmlinkage long sys_sched_get_priority_max(int policy); asmlinkage long sys_sched_get_priority_min(int policy); asmlinkage long sys_sched_rr_get_interval(pid_t pid, - struct timespec __user *interval); + struct __kernel_timespec __user *interval); asmlinkage long sys_setpriority(int which, int who, int niceval); asmlinkage long sys_getpriority(int which, int who);
diff --git a/kernel/compat.c b/kernel/compat.c index ec09c6e1d594..f6d17f06ec3d 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -1288,27 +1288,7 @@ COMPAT_SYSCALL_DEFINE4(migrate_pages, compat_pid_t, pid, return sys_migrate_pages(pid, nr_bits + 1, old, new); } #endif -#endif - -#ifdef CONFIG_COMPAT_TIME -COMPAT_SYSCALL_DEFINE2(sched_rr_get_interval, - compat_pid_t, pid, - struct compat_timespec __user *, interval) -{ - struct timespec t; - int ret; - mm_segment_t old_fs = get_fs(); - - set_fs(KERNEL_DS); - ret = sys_sched_rr_get_interval(pid, (struct timespec __user *)&t); - set_fs(old_fs); - if (compat_put_timespec(&t, interval)) - return -EFAULT; - return ret; -} -#endif
-#ifdef CONFIG_COMPAT /* * Allocate user-space memory for the duration of a single system call, * in order to marshall parameters inside a compat thunk. diff --git a/kernel/sched/core.c b/kernel/sched/core.c index fe22f7510bce..498ab3ab46dc 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c @@ -35,6 +35,8 @@ #include <asm/mmu_context.h> #include <linux/interrupt.h> #include <linux/capability.h> +#include <linux/compat.h> +#include <linux/compat_time.h> #include <linux/completion.h> #include <linux/kernel_stat.h> #include <linux/debug_locks.h> @@ -4468,15 +4470,13 @@ SYSCALL_DEFINE1(sched_get_priority_min, int, policy) * Return: On success, 0 and the timeslice is in @interval. Otherwise, * an error code. */ -SYSCALL_DEFINE2(sched_rr_get_interval, pid_t, pid, - struct timespec __user *, interval) +static int sched_rr_get_interval(pid_t pid, struct timespec64 *t) { struct task_struct *p; unsigned int time_slice; unsigned long flags; struct rq *rq; int retval; - struct timespec t;
if (pid < 0) return -EINVAL; @@ -4497,16 +4497,37 @@ SYSCALL_DEFINE2(sched_rr_get_interval, pid_t, pid, time_slice = p->sched_class->get_rr_interval(rq, p); task_rq_unlock(rq, p, &flags);
- rcu_read_unlock(); - jiffies_to_timespec(time_slice, &t); - retval = copy_to_user(interval, &t, sizeof(t)) ? -EFAULT : 0; - return retval; + jiffies_to_timespec64(time_slice, t);
out_unlock: rcu_read_unlock(); return retval; }
+SYSCALL_DEFINE2(sched_rr_get_interval, pid_t, pid, + struct __kernel_timespec __user *, interval) +{ + struct timespec64 t; + int ret = sched_rr_get_interval(pid, &t); + if (ret) + return ret; + + return put_timespec64(&t, interval); +} + +#ifdef CONFIG_COMPAT_TIME +COMPAT_SYSCALL_DEFINE2(sched_rr_get_interval, compat_pid_t, pid, + struct compat_timespec __user *, interval) +{ + struct timespec64 t; + int ret = sched_rr_get_interval(pid, &t); + if (ret) + return ret; + + return compat_put_timespec64(&t, interval); +} +#endif + static const char stat_nam[] = TASK_STATE_TO_CHAR_STR;
void sched_show_task(struct task_struct *p)
On Wed, 6 May 2015, Arnd Bergmann wrote:
sys_sched_rr_get_interval is easily converted to use __kernel_timespec, by changing the function prototype. In order to allow compat handling on 32-bit architectures, we also move compat_sys_sched_getaffinity into the same file and unify the implementation, which avoids converting the structure multiple times.
Signed-off-by: Arnd Bergmann arnd@arndb.de
Reviewed-by: Thomas Gleixner tglx@linutronix.de
On Wed, 2015-05-06 at 18:30 +0200, Arnd Bergmann wrote:
- After all system calls are converted, we can change one architecture at a time to select ARCH_HAS_COMPAT_TIME, and modify its system call table accordingly. In this version, I do it for ARM32, x86-32, and x86-64 for demonstration purposes.
Perhaps this was correct for your first draft. Because this series adds ARCH_HAS_COMPAT_TIME in 04/19, but it doesn't add any selects of that symbol, does it? As far as I can see ARCH_HAS_COMPAT_TIME simply functions as an alias for COMPAT in this series.
A follow-up series changes over all other architectures.
The last patch in the series changes the CONFIG_COMPAT_TIME Kconfig symbol to be user visible. Disabling this symbol will get you a kernel that intentionally breaks support for old tasks in order to provide an interface that will survive 2038.
This doesn't happen in 19/19, or in any other patch in this series. Maybe also something that has changed since the first draft.
This is meant mostly as a debugging help for now, to let people build a y2038 safe distro, but at some point in the 2030s, we should remove that option and all the compat handling.
Thanks,
Paul Bolle
On Thu, 2015-05-07 at 09:27 +0200, Paul Bolle wrote:
On Wed, 2015-05-06 at 18:30 +0200, Arnd Bergmann wrote:
- After all system calls are converted, we can change one architecture at a time to select ARCH_HAS_COMPAT_TIME, and modify its system call table accordingly. In this version, I do it for ARM32, x86-32, and x86-64 for demonstration purposes.
Perhaps this was correct for your first draft. Because this series adds ARCH_HAS_COMPAT_TIME in 04/19, but it doesn't add any selects of that symbol, does it? As far as I can see ARCH_HAS_COMPAT_TIME simply functions as an alias for COMPAT in this series.
A follow-up series changes over all other architectures.
The last patch in the series changes the CONFIG_COMPAT_TIME Kconfig symbol to be user visible. Disabling this symbol will get you a kernel that intentionally breaks support for old tasks in order to provide an interface that will survive 2038.
This doesn't happen in 19/19, or in any other patch in this series. Maybe also something that has changed since the first draft.
This is meant mostly as a debugging help for now, to let people build a y2038 safe distro, but at some point in the 2030s, we should remove that option and all the compat handling.
And then it occurred to me to check the y2038-syscalls git branch you referenced. After which the above made much more sense. (Though my remark that ARCH_HAS_COMPAT_TIME simply functions as an alias for COMPAT also seems to hold for that branch.)
Paul Bolle
On Thursday 07 May 2015 09:39:18 Paul Bolle wrote:
On Thu, 2015-05-07 at 09:27 +0200, Paul Bolle wrote:
On Wed, 2015-05-06 at 18:30 +0200, Arnd Bergmann wrote:
- After all system calls are converted, we can change one architecture at a time to select ARCH_HAS_COMPAT_TIME, and modify its system call table accordingly. In this version, I do it for ARM32, x86-32, and x86-64 for demonstration purposes.
Perhaps this was correct for your first draft. Because this series adds ARCH_HAS_COMPAT_TIME in 04/19, but it doesn't add any selects of that symbol, does it? As far as I can see ARCH_HAS_COMPAT_TIME simply functions as an alias for COMPAT in this series.
It is still the intention of the patch to add the selects in the later patches that are required to see any benefit of the earlier patches.
ARCH_HAS_COMPAT_TIME is in the "x86: use COMPAT_SYS_TIME" and "ARM: use CONFIG_COMPAT_TIME" patches of the full series available on my git tree.
I realize the downsides of not posting the entire series at once here, but it seemed better to avoid spamming everyone too much, while I try to find out if we have agreement on the overall strategy.
For reference, see below for the ARM patch.
A follow-up series changes over all other architectures.
The last patch in the series changes the CONFIG_COMPAT_TIME Kconfig symbol to be user visible. Disabling this symbol will get you a kernel that intentionally breaks support for old tasks in order to provide an interface that will survive 2038.
This doesn't happen in 19/19, or in any other patch in this series. Maybe also something that has changed since the first draft.
This is meant mostly as a debugging help for now, to let people build a y2038 safe distro, but at some point in the 2030s, we should remove that option and all the compat handling.
And then it occurred to me to check the y2038-syscalls git branch you referenced. After which the above made much more sense. (Though my remark that ARCH_HAS_COMPAT_TIME simply functions as an alias for COMPAT also seems to hold for that branch.)
Right, in fact both of these happen at the end of the git tree.
Arnd
8<------- commit 93a8526e02b6e29888df516e5750cd6483ee4472 Author: Arnd Bergmann arnd@arndb.de Date: Fri Apr 24 17:12:08 2015 +0200
ARM: use CONFIG_COMPAT_TIME
This changes the ARM architecture to use the new compat system call definitions that were added in previous patches. All system calls that use 32-bit time_t now go through the COMPAT framework, while a set of 25 new system calls gets added to replace them using 64-bit data structures __kernel_timespec, __kernel_itimerspec, __kernel_rusage, __kernel_msqid_ds, __kernel_semid_ds, __kernel_shmid_ds and __kernel_stat.
Similarly, we add all the new system calls to the arm64 compat syscall table.
Signed-off-by: Arnd Bergmann arnd@arndb.de -- arch/arm/Kconfig | 1 + arch/arm/include/asm/unistd.h | 11 ++-- arch/arm/include/uapi/asm/unistd.h | 22 ++++++++ arch/arm/kernel/calls.S | 101 +++++++++++++++++++++++-------------- arch/arm/kernel/sys_oabi-compat.c | 9 ++-- arch/arm64/include/asm/unistd32.h | 44 ++++++++++++++++ 6 files changed, 142 insertions(+), 46 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 45df48ba0b12..491896833c76 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -2,6 +2,7 @@ config ARM bool default y select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE + select ARCH_HAS_COMPAT_TIME select ARCH_HAS_ELF_RANDOMIZE select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST select ARCH_HAVE_CUSTOM_GPIO_H diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h index 32640c431a08..ad7588eb9d58 100644 --- a/arch/arm/include/asm/unistd.h +++ b/arch/arm/include/asm/unistd.h @@ -19,7 +19,7 @@ * This may need to be greater than __NR_last_syscall+1 in order to * account for the padding in the syscall table */ -#define __NR_syscalls (388) +#define __NR_syscalls (412)
/* * *NOTE*: This is a ghost syscall private to the kernel. Only the @@ -28,7 +28,9 @@ */ #define __ARM_NR_cmpxchg (__ARM_NR_BASE+0x00fff0)
+#ifdef CONFIG_COMPAT_TIME #define __ARCH_WANT_STAT64 +#endif #define __ARCH_WANT_SYS_GETHOSTNAME #define __ARCH_WANT_SYS_PAUSE #define __ARCH_WANT_SYS_GETPGRP @@ -40,11 +42,13 @@ #define __ARCH_WANT_SYS_OLD_SELECT
#if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT) -#define __ARCH_WANT_SYS_TIME +#ifdef CONFIG_COMPAT_TIME +#define __ARCH_WANT_COMPAT_SYS_TIME #define __ARCH_WANT_SYS_IPC +#define __ARCH_WANT_SYS_UTIME +#endif #define __ARCH_WANT_SYS_OLDUMOUNT #define __ARCH_WANT_SYS_ALARM -#define __ARCH_WANT_SYS_UTIME #define __ARCH_WANT_SYS_OLD_GETRLIMIT #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_SYS_SOCKETCALL @@ -52,6 +56,7 @@ #define __ARCH_WANT_SYS_FORK #define __ARCH_WANT_SYS_VFORK #define __ARCH_WANT_SYS_CLONE +#define __ARCH_WANT_SYS_NEWFSTATAT
/* * Unimplemented (or alternatively implemented) syscalls diff --git a/arch/arm/include/uapi/asm/unistd.h b/arch/arm/include/uapi/asm/unistd.h index 0c3f5a0dafd3..4ea2d7312f15 100644 --- a/arch/arm/include/uapi/asm/unistd.h +++ b/arch/arm/include/uapi/asm/unistd.h @@ -414,6 +414,28 @@ #define __NR_memfd_create (__NR_SYSCALL_BASE+385) #define __NR_bpf (__NR_SYSCALL_BASE+386) #define __NR_execveat (__NR_SYSCALL_BASE+387) +#define __NR_clock_gettime64 (__NR_SYSCALL_BASE+388) +#define __NR_clock_settime64 (__NR_SYSCALL_BASE+389) +#define __NR_clock_adjtime64 (__NR_SYSCALL_BASE+390) +#define __NR_clock_getres64 (__NR_SYSCALL_BASE+391) +#define __NR_clock_nanosleep64 (__NR_SYSCALL_BASE+392) +#define __NR_timer_gettime64 (__NR_SYSCALL_BASE+393) +#define __NR_timer_settime64 (__NR_SYSCALL_BASE+394) +#define __NR_timerfd_gettime64 (__NR_SYSCALL_BASE+395) +#define __NR_timerfd_settime64 (__NR_SYSCALL_BASE+396) +#define __NR_pselect64 (__NR_SYSCALL_BASE+397) +#define __NR_ppoll64 (__NR_SYSCALL_BASE+398) +#define __NR_io_getevents64 (__NR_SYSCALL_BASE+399) +#define __NR_recvmmsg64 (__NR_SYSCALL_BASE+400) +#define __NR_semtimedop64 (__NR_SYSCALL_BASE+401) +#define __NR_mq_timedsend64 (__NR_SYSCALL_BASE+402) +#define __NR_mq_timedreceive64 (__NR_SYSCALL_BASE+403) +#define __NR_utimensat64 (__NR_SYSCALL_BASE+404) +#define __NR_newfstat64 (__NR_SYSCALL_BASE+405) +#define __NR_newfstatat64 (__NR_SYSCALL_BASE+406) +#define __NR_rt_sigtimedwait64 (__NR_SYSCALL_BASE+407) +#define __NR_getrusage64 (__NR_SYSCALL_BASE+408) +#define __NR_waitid64 (__NR_SYSCALL_BASE+409)
/* * The following SWIs are ARM private. diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S index 05745eb838c5..092a2e1d979c 100644 --- a/arch/arm/kernel/calls.S +++ b/arch/arm/kernel/calls.S @@ -22,7 +22,7 @@ /* 10 */ CALL(sys_unlink) CALL(sys_execve) CALL(sys_chdir) - CALL(OBSOLETE(sys_time)) /* used by libc4 */ + CALL(OBSOLETE(compat_sys_time)) /* used by libc4 */ CALL(sys_mknod) /* 15 */ CALL(sys_chmod) CALL(sys_lchown16) @@ -34,12 +34,12 @@ CALL(OBSOLETE(sys_oldumount)) /* used by libc4 */ CALL(sys_setuid16) CALL(sys_getuid16) -/* 25 */ CALL(OBSOLETE(sys_stime)) +/* 25 */ CALL(OBSOLETE(compat_sys_stime)) CALL(sys_ptrace) CALL(OBSOLETE(sys_alarm)) /* used by libc4 */ CALL(sys_ni_syscall) /* was sys_fstat */ CALL(sys_pause) -/* 30 */ CALL(OBSOLETE(sys_utime)) /* used by libc4 */ +/* 30 */ CALL(OBSOLETE(compat_sys_utime))/* used by libc4 */ CALL(sys_ni_syscall) /* was sys_stty */ CALL(sys_ni_syscall) /* was sys_getty */ CALL(sys_access) @@ -86,12 +86,12 @@ CALL(sys_sethostname) /* 75 */ CALL(sys_setrlimit) CALL(OBSOLETE(sys_old_getrlimit)) /* used by libc4 */ - CALL(sys_getrusage) - CALL(sys_gettimeofday) - CALL(sys_settimeofday) + CALL(compat_sys_getrusage) + CALL(compat_sys_gettimeofday) + CALL(compat_sys_settimeofday) /* 80 */ CALL(sys_getgroups16) CALL(sys_setgroups16) - CALL(OBSOLETE(sys_old_select)) /* used by libc4 */ + CALL(OBSOLETE(compat_sys_old_select)) /* used by libc4 */ CALL(sys_symlink) CALL(sys_ni_syscall) /* was sys_lstat */ /* 85 */ CALL(sys_readlink) @@ -113,17 +113,17 @@ CALL(sys_ni_syscall) /* sys_ioperm */ CALL(OBSOLETE(ABI(sys_socketcall, sys_oabi_socketcall))) CALL(sys_syslog) - CALL(sys_setitimer) -/* 105 */ CALL(sys_getitimer) - CALL(sys_newstat) - CALL(sys_newlstat) - CALL(sys_newfstat) + CALL(compat_sys_setitimer) +/* 105 */ CALL(compat_sys_getitimer) + CALL(compat_sys_newstat) + CALL(compat_sys_newlstat) + CALL(compat_sys_newfstat) CALL(sys_ni_syscall) /* was sys_uname */ /* 110 */ CALL(sys_ni_syscall) /* was sys_iopl */ CALL(sys_vhangup) CALL(sys_ni_syscall) CALL(OBSOLETE(sys_syscall)) /* call a syscall */ - CALL(sys_wait4) + CALL(compat_sys_wait4) /* 115 */ CALL(sys_swapoff) CALL(sys_sysinfo) CALL(OBSOLETE(ABI(sys_ipc, sys_oabi_ipc))) @@ -133,7 +133,7 @@ CALL(sys_setdomainname) CALL(sys_newuname) CALL(sys_ni_syscall) /* modify_ldt */ - CALL(sys_adjtimex) + CALL(compat_sys_adjtimex) /* 125 */ CALL(sys_mprotect) CALL(sys_sigprocmask) CALL(sys_ni_syscall) /* was sys_create_module */ @@ -151,7 +151,7 @@ CALL(sys_setfsgid16) /* 140 */ CALL(sys_llseek) CALL(sys_getdents) - CALL(sys_select) + CALL(compat_sys_select) CALL(sys_flock) CALL(sys_msync) /* 145 */ CALL(sys_readv) @@ -170,8 +170,8 @@ CALL(sys_sched_yield) CALL(sys_sched_get_priority_max) /* 160 */ CALL(sys_sched_get_priority_min) - CALL(sys_sched_rr_get_interval) - CALL(sys_nanosleep) + CALL(compat_sys_sched_rr_get_interval) + CALL(compat_sys_nanosleep) CALL(sys_mremap) CALL(sys_setresuid16) /* 165 */ CALL(sys_getresuid16) @@ -186,7 +186,7 @@ CALL(sys_rt_sigaction) /* 175 */ CALL(sys_rt_sigprocmask) CALL(sys_rt_sigpending) - CALL(sys_rt_sigtimedwait) + CALL(compat_sys_rt_sigtimedwait) CALL(sys_rt_sigqueueinfo) CALL(sys_rt_sigsuspend) /* 180 */ CALL(ABI(sys_pread64, sys_oabi_pread64)) @@ -249,12 +249,12 @@ CALL(sys_fremovexattr) CALL(sys_tkill) CALL(sys_sendfile64) -/* 240 */ CALL(sys_futex) +/* 240 */ CALL(compat_sys_futex) CALL(sys_sched_setaffinity) CALL(sys_sched_getaffinity) CALL(sys_io_setup) CALL(sys_io_destroy) -/* 245 */ CALL(sys_io_getevents) +/* 245 */ CALL(compat_sys_io_getevents) CALL(sys_io_submit) CALL(sys_io_cancel) CALL(sys_exit_group) @@ -267,29 +267,29 @@ /* 255 */ CALL(sys_ni_syscall) /* sys_get_thread_area */ CALL(sys_set_tid_address) CALL(sys_timer_create) - CALL(sys_timer_settime) - CALL(sys_timer_gettime) + CALL(compat_sys_timer_settime) + CALL(compat_sys_timer_gettime) /* 260 */ CALL(sys_timer_getoverrun) CALL(sys_timer_delete) - CALL(sys_clock_settime) - CALL(sys_clock_gettime) - CALL(sys_clock_getres) -/* 265 */ CALL(sys_clock_nanosleep) + CALL(compat_sys_clock_settime) + CALL(compat_sys_clock_gettime) + CALL(compat_sys_clock_getres) +/* 265 */ CALL(compat_sys_clock_nanosleep) CALL(sys_statfs64_wrapper) CALL(sys_fstatfs64_wrapper) CALL(sys_tgkill) - CALL(sys_utimes) + CALL(compat_sys_utimes) /* 270 */ CALL(sys_arm_fadvise64_64) CALL(sys_pciconfig_iobase) CALL(sys_pciconfig_read) CALL(sys_pciconfig_write) CALL(sys_mq_open) /* 275 */ CALL(sys_mq_unlink) - CALL(sys_mq_timedsend) - CALL(sys_mq_timedreceive) + CALL(compat_sys_mq_timedsend) + CALL(compat_sys_mq_timedreceive) CALL(sys_mq_notify) CALL(sys_mq_getsetattr) -/* 280 */ CALL(sys_waitid) +/* 280 */ CALL(compat_sys_waitid) CALL(sys_socket) CALL(ABI(sys_bind, sys_oabi_bind)) CALL(ABI(sys_connect, sys_oabi_connect)) @@ -321,7 +321,7 @@ CALL(sys_add_key) /* 310 */ CALL(sys_request_key) CALL(sys_keyctl) - CALL(ABI(sys_semtimedop, sys_oabi_semtimedop)) + CALL(ABI(compat_sys_semtimedop, sys_oabi_semtimedop)) /* vserver */ CALL(sys_ni_syscall) CALL(sys_ioprio_set) /* 315 */ CALL(sys_ioprio_get) @@ -335,7 +335,7 @@ CALL(sys_mkdirat) CALL(sys_mknodat) /* 325 */ CALL(sys_fchownat) - CALL(sys_futimesat) + CALL(compat_sys_futimesat) CALL(ABI(sys_fstatat64, sys_oabi_fstatat64)) CALL(sys_unlinkat) CALL(sys_renameat) @@ -344,8 +344,8 @@ CALL(sys_readlinkat) CALL(sys_fchmodat) CALL(sys_faccessat) -/* 335 */ CALL(sys_pselect6) - CALL(sys_ppoll) +/* 335 */ CALL(compat_sys_pselect6) + CALL(compat_sys_ppoll) CALL(sys_unshare) CALL(sys_set_robust_list) CALL(sys_get_robust_list) @@ -357,13 +357,13 @@ /* 345 */ CALL(sys_getcpu) CALL(sys_epoll_pwait) CALL(sys_kexec_load) - CALL(sys_utimensat) + CALL(compat_sys_utimensat) CALL(sys_signalfd) /* 350 */ CALL(sys_timerfd_create) CALL(sys_eventfd) CALL(sys_fallocate) - CALL(sys_timerfd_settime) - CALL(sys_timerfd_gettime) + CALL(compat_sys_timerfd_settime) + CALL(compat_sys_timerfd_gettime) /* 355 */ CALL(sys_signalfd4) CALL(sys_eventfd2) CALL(sys_epoll_create1) @@ -374,14 +374,14 @@ CALL(sys_pwritev) CALL(sys_rt_tgsigqueueinfo) CALL(sys_perf_event_open) -/* 365 */ CALL(sys_recvmmsg) +/* 365 */ CALL(compat_sys_recvmmsg) CALL(sys_accept4) CALL(sys_fanotify_init) CALL(sys_fanotify_mark) CALL(sys_prlimit64) /* 370 */ CALL(sys_name_to_handle_at) CALL(sys_open_by_handle_at) - CALL(sys_clock_adjtime) + CALL(compat_sys_clock_adjtime) CALL(sys_syncfs) CALL(sys_sendmmsg) /* 375 */ CALL(sys_setns) @@ -397,6 +397,29 @@ /* 385 */ CALL(sys_memfd_create) CALL(sys_bpf) CALL(sys_execveat) + CALL(sys_clock_gettime) + CALL(sys_clock_settime) +/* 390 */ CALL(sys_clock_adjtime) + CALL(sys_clock_getres) + CALL(sys_clock_nanosleep) + CALL(sys_timer_gettime) + CALL(sys_timer_settime) +/* 395 */ CALL(sys_timerfd_gettime) + CALL(sys_timerfd_settime) + CALL(sys_pselect6) + CALL(sys_ppoll) + CALL(sys_io_getevents) +/* 400 */ CALL(sys_recvmmsg) + CALL(sys_semtimedop) + CALL(sys_mq_timedsend) + CALL(sys_mq_timedreceive) + CALL(sys_utimensat) +/* 405 */ CALL(sys_newfstat) + CALL(sys_newfstatat) + CALL(sys_rt_sigtimedwait) + CALL(sys_getrusage) + CALL(sys_waitid) + #ifndef syscalls_counted .equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls #define syscalls_counted diff --git a/arch/arm/kernel/sys_oabi-compat.c b/arch/arm/kernel/sys_oabi-compat.c index b83f3b7737fb..2e990b564371 100644 --- a/arch/arm/kernel/sys_oabi-compat.c +++ b/arch/arm/kernel/sys_oabi-compat.c @@ -73,6 +73,7 @@ * wrappers provided below. */
+#include <linux/compat_time.h> #include <linux/syscalls.h> #include <linux/errno.h> #include <linux/fs.h> @@ -307,10 +308,10 @@ struct oabi_sembuf { asmlinkage long sys_oabi_semtimedop(int semid, struct oabi_sembuf __user *tsops, unsigned nsops, - const struct timespec __user *timeout) + const struct compat_timespec __user *timeout) { struct sembuf *sops; - struct timespec local_timeout; + struct compat_timespec local_timeout; long err; int i;
@@ -336,7 +337,7 @@ asmlinkage long sys_oabi_semtimedop(int semid, } else { mm_segment_t fs = get_fs(); set_fs(KERNEL_DS); - err = sys_semtimedop(semid, sops, nsops, timeout); + err = compat_sys_semtimedop(semid, sops, nsops, timeout); set_fs(fs); } kfree(sops); @@ -361,7 +362,7 @@ asmlinkage int sys_oabi_ipc(uint call, int first, int second, int third, return sys_oabi_semtimedop(first, (struct oabi_sembuf __user *)ptr, second, - (const struct timespec __user *)fifth); + (const struct compat_timespec __user *)fifth); default: return sys_ipc(call, first, second, third, ptr, fifth); } diff --git a/arch/arm64/include/asm/unistd32.h b/arch/arm64/include/asm/unistd32.h index cef934a90f17..1659a0c68ea3 100644 --- a/arch/arm64/include/asm/unistd32.h +++ b/arch/arm64/include/asm/unistd32.h @@ -797,3 +797,47 @@ __SYSCALL(__NR_memfd_create, sys_memfd_create) __SYSCALL(__NR_bpf, sys_bpf) #define __NR_execveat 387 __SYSCALL(__NR_execveat, compat_sys_execveat) +#define __NR_clock_gettime64 388 +_SYSCALL(__NR_clock_gettime64, sys_clock_gettime) +#define __NR_clock_settime64 389 +_SYSCALL(__NR_clock_settime64, sys_clock_settime) +#define __NR_clock_adjtime64 390 +_SYSCALL(__NR_clock_adjtime64, sys_clock_adjtime) +#define __NR_clock_getres64 391 +_SYSCALL(__NR_clock_getres64, sys_clock_getres) +#define __NR_clock_nanosleep64 392 +_SYSCALL(__NR_clock_nanosleep64, sys_clock_nanosleep) +#define __NR_timer_gettime64 393 +_SYSCALL(__NR_timer_gettime64, sys_timer_gettime) +#define __NR_timer_settime64 394 +_SYSCALL(__NR_timer_settime64, sys_timer_settime) +#define __NR_timerfd_gettime64 395 +_SYSCALL(__NR_timerfd_gettime64, sys_timerfd_gettime) +#define __NR_timerfd_settime64 396 +_SYSCALL(__NR_timerfd_settime64, sys_timerfd_settime) +#define __NR_pselect64 397 +_SYSCALL(__NR_pselect64, compat_sys_pselect6_time64) +#define __NR_ppoll64 398 +_SYSCALL(__NR_ppoll64, compat_sys_ppoll_time64) +#define __NR_io_getevents64 399 +_SYSCALL(__NR_io_getevents64, sys_io_getevents) +#define __NR_recvmmsg64 400 +_SYSCALL(__NR_recvmmsg64, compat_sys_recvmmsg64) +#define __NR_semtimedop64 401 +_SYSCALL(__NR_semtimedop64, sys_semtimedop) +#define __NR_mq_timedsend64 402 +_SYSCALL(__NR_mq_timedsend64, sys_mq_timedsend) +#define __NR_mq_timedreceive64 403 +_SYSCALL(__NR_mq_timedreceive64, sys_mq_timedreceive) +#define __NR_utimensat64 404 +_SYSCALL(__NR_utimensat64, sys_utimensat) +#define __NR_newfstat64 405 +_SYSCALL(__NR_newfstat64, sys_newfstat) +#define __NR_newfstatat64 406 +_SYSCALL(__NR_newfstatat64, sys_newfstatat) +#define __NR_rt_sigtimedwait64 407 +_SYSCALL(__NR_rt_sigtimedwait64, compat_sys_rt_sigtimedwait_time64) +#define __NR_getrusage64 408 +_SYSCALL(__NR_getrusage64, sys_getrusage) +#define __NR_waitid64 409 +_SYSCALL(__NR_waitid64, compat_sys_waitid_time64)
On Thu, 2015-05-07 at 10:52 +0200, Arnd Bergmann wrote:
On Thursday 07 May 2015 09:39:18 Paul Bolle wrote: I realize the downsides of not posting the entire series at once here, but it seemed better to avoid spamming everyone too much, while I try to find out if we have agreement on the overall strategy.
That downside is worse when people only quickly skim your message for clues while thinking about the problem they _think_ they've spotted (like I did).
For reference, see below for the ARM patch.
And then it occurred to me to check the y2038-syscalls git branch you referenced. After which the above made much more sense. (Though my remark that ARCH_HAS_COMPAT_TIME simply functions as an alias for COMPAT also seems to hold for that branch.)
Right, in fact both of these happen at the end of the git tree.
Still, you might consider making ARCH_HAS_COMPAT_TIME a plain bool and adding "select ARCH_HAS_COMPAT_TIME" to the eight or so COMPAT entries. But, clearly, I'm now wasting even more of your time by trying to save face here.
Thanks,
Paul Bolle