Apologies for the overly long patch series, I was hoping this would have been simpler.
This is my first 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. For the time being, I'm mainly interested in feedback about the general approach, to see if this is a reasonable concept, or whether we should do this in a completely different way.
The current submission is for a relatively small group of people and only for the y2038 mailing list, and if we can agree on the approach, I will post it to a wider group of people and mailing lists next.
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 (37): 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_rusage y2038: introduce struct __kernel_timespec y2038: introduce struct __kernel_stat y2038: use timespec64 for poll/select/recvmmsg y2038: add compat_{get,put}_timespec64 y2038: use __kernel_timespec in sys_aio_getevents y2038: factor out compat fd_set and sigset handling y2038: add compat handling for sys_recvmmsg y2038: add compat handling for sys_semtimedop y2038: extend sysvipc data structures for 64-bit time_t y2038: ipc: report long times to user space y2038: introduce {get,put}_itimerspec y2038: use __kernel_itimerspec in timerfd y2038: introduce compat_sys_pselect6_time64 y2038: introduce compat_sys_ppoll_time64 y2038: use __kernel_timespec for sys_mq_timed{send,receive} y2038: make compat_sys_mq_timed{send,receive} usable on 32-bit y2038: use __kernel_timespec in sys_utimensat 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 y2038: move compat posix time handling to posix-timers.c y2038: introduce struct __kernel_timex y2038: convert all timex users to use __kernel_timex y2038: convert posix clock syscalls y2038: ignore new syscalls for now ARM: use CONFIG_COMPAT_TIME x86: use COMPAT_SYS_TIME [NOT YET] y2038: make CONFIG_COMPAT_TIME optional
arch/Kconfig | 12 ++ arch/alpha/include/asm/Kbuild | 4 + arch/alpha/include/uapi/asm/ipcbuf.h | 1 - arch/alpha/include/uapi/asm/msgbuf.h | 27 --- arch/alpha/include/uapi/asm/sembuf.h | 22 -- arch/alpha/include/uapi/asm/shmbuf.h | 38 ---- arch/alpha/include/uapi/asm/stat.h | 4 + arch/alpha/kernel/osf_sys.c | 8 +- arch/arm/Kconfig | 1 + arch/arm/include/asm/unistd.h | 11 +- arch/arm/include/uapi/asm/stat.h | 2 + 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/compat.h | 43 ++-- arch/avr32/include/asm/Kbuild | 4 + arch/avr32/include/uapi/asm/Kbuild | 1 - arch/avr32/include/uapi/asm/msgbuf.h | 31 --- arch/avr32/include/uapi/asm/sembuf.h | 25 --- arch/avr32/include/uapi/asm/shmbuf.h | 42 ---- arch/avr32/include/uapi/asm/stat.h | 2 + arch/blackfin/include/uapi/asm/stat.h | 2 + arch/cris/include/asm/Kbuild | 6 +- arch/cris/include/uapi/asm/ipcbuf.h | 1 - arch/cris/include/uapi/asm/msgbuf.h | 33 --- arch/cris/include/uapi/asm/sembuf.h | 25 --- arch/cris/include/uapi/asm/shmbuf.h | 42 ---- arch/cris/include/uapi/asm/stat.h | 2 + arch/frv/include/asm/Kbuild | 4 + arch/frv/include/uapi/asm/ipcbuf.h | 1 - arch/frv/include/uapi/asm/msgbuf.h | 32 --- arch/frv/include/uapi/asm/sembuf.h | 26 --- arch/frv/include/uapi/asm/shmbuf.h | 43 ---- arch/frv/include/uapi/asm/stat.h | 2 + arch/ia64/include/asm/Kbuild | 4 + arch/ia64/include/uapi/asm/ipcbuf.h | 1 - arch/ia64/include/uapi/asm/msgbuf.h | 27 --- arch/ia64/include/uapi/asm/sembuf.h | 22 -- arch/ia64/include/uapi/asm/shmbuf.h | 38 ---- arch/ia64/include/uapi/asm/stat.h | 4 + arch/m32r/include/asm/Kbuild | 4 + arch/m32r/include/uapi/asm/ipcbuf.h | 1 - arch/m32r/include/uapi/asm/msgbuf.h | 31 --- arch/m32r/include/uapi/asm/sembuf.h | 25 --- arch/m32r/include/uapi/asm/shmbuf.h | 42 ---- arch/m32r/include/uapi/asm/stat.h | 1 + arch/m68k/include/uapi/asm/stat.h | 2 + arch/mips/include/asm/compat.h | 49 ++--- arch/mips/include/uapi/asm/msgbuf.h | 56 ++++-- arch/mips/include/uapi/asm/sembuf.h | 15 +- arch/mips/include/uapi/asm/shmbuf.h | 23 ++- arch/mips/include/uapi/asm/stat.h | 1 + arch/mn10300/include/asm/Kbuild | 4 + arch/mn10300/include/uapi/asm/ipcbuf.h | 1 - arch/mn10300/include/uapi/asm/msgbuf.h | 31 --- arch/mn10300/include/uapi/asm/sembuf.h | 25 --- arch/mn10300/include/uapi/asm/shmbuf.h | 42 ---- arch/mn10300/include/uapi/asm/stat.h | 2 + arch/parisc/include/asm/compat.h | 43 ++-- arch/parisc/include/uapi/asm/msgbuf.h | 32 +-- arch/parisc/include/uapi/asm/sembuf.h | 13 +- arch/parisc/include/uapi/asm/shmbuf.h | 18 +- arch/parisc/include/uapi/asm/stat.h | 1 + arch/powerpc/include/asm/compat.h | 43 ++-- arch/powerpc/include/uapi/asm/msgbuf.h | 18 +- arch/powerpc/include/uapi/asm/sembuf.h | 14 +- arch/powerpc/include/uapi/asm/shmbuf.h | 18 +- arch/powerpc/include/uapi/asm/stat.h | 25 +++ arch/s390/include/asm/Kbuild | 4 +- arch/s390/include/asm/compat.h | 43 ++-- arch/s390/include/uapi/asm/msgbuf.h | 37 ---- arch/s390/include/uapi/asm/sembuf.h | 29 --- arch/s390/include/uapi/asm/shmbuf.h | 48 ----- arch/s390/include/uapi/asm/stat.h | 24 +++ arch/s390/kernel/time.c | 2 +- arch/sh/include/uapi/asm/stat.h | 2 + arch/sparc/include/asm/compat.h | 43 ++-- arch/sparc/include/uapi/asm/msgbuf.h | 21 +- arch/sparc/include/uapi/asm/sembuf.h | 15 +- arch/sparc/include/uapi/asm/shmbuf.h | 20 +- arch/sparc/include/uapi/asm/stat.h | 28 +++ arch/tile/include/asm/compat.h | 43 ++-- arch/x86/Kconfig | 1 + arch/x86/include/asm/compat.h | 44 ++-- arch/x86/include/asm/ftrace.h | 2 +- arch/x86/include/asm/sys_ia32.h | 2 +- arch/x86/include/asm/unistd.h | 21 +- arch/x86/include/uapi/asm/msgbuf.h | 41 +++- arch/x86/include/uapi/asm/sembuf.h | 10 + arch/x86/include/uapi/asm/shmbuf.h | 58 +++++- arch/x86/include/uapi/asm/stat.h | 49 +++-- arch/x86/kernel/cpu/perf_event.c | 2 +- arch/x86/syscalls/syscall_32.tbl | 98 +++++---- arch/xtensa/include/uapi/asm/msgbuf.h | 24 +-- arch/xtensa/include/uapi/asm/sembuf.h | 16 +- arch/xtensa/include/uapi/asm/shmbuf.h | 36 +--- arch/xtensa/include/uapi/asm/stat.h | 2 + drivers/ptp/ptp_clock.c | 2 +- fs/Makefile | 1 + fs/aio.c | 54 +++-- fs/compat.c | 198 +++++++++++------- fs/eventpoll.c | 12 +- fs/select.c | 64 +++--- fs/stat.c | 12 +- fs/timerfd.c | 13 +- fs/utimes.c | 19 +- include/linux/audit.h | 4 +- include/linux/compat.h | 261 +++--------------------- include/linux/compat_time.h | 298 +++++++++++++++++++++++++++ include/linux/hrtimer.h | 2 +- include/linux/jiffies.h | 23 ++- include/linux/msg.h | 7 +- include/linux/poll.h | 10 +- include/linux/posix-clock.h | 2 +- include/linux/posix-timers.h | 11 +- include/linux/resource.h | 8 +- include/linux/sem.h | 3 +- include/linux/shm.h | 7 +- include/linux/signal.h | 3 +- include/linux/socket.h | 4 +- include/linux/stat.h | 3 + include/linux/syscalls.h | 73 ++++--- include/linux/thread_info.h | 8 +- include/linux/time.h | 11 +- include/linux/time64.h | 25 ++- include/linux/timex.h | 6 +- include/net/compat.h | 3 + include/uapi/asm-generic/kernel_stat.h | 36 ++++ include/uapi/asm-generic/msgbuf.h | 16 +- include/uapi/asm-generic/sembuf.h | 26 ++- include/uapi/asm-generic/shmbuf.h | 16 +- include/uapi/asm-generic/stat.h | 12 +- include/uapi/linux/net.h | 1 + include/uapi/linux/resource.h | 32 +++ include/uapi/linux/time.h | 17 ++ include/uapi/linux/timex.h | 45 +++++ init/do_mounts.h | 22 +- init/initramfs.c | 14 +- ipc/compat.c | 18 +- ipc/compat_mq.c | 28 --- ipc/mqueue.c | 140 +++++++++---- ipc/msg.c | 23 ++- ipc/sem.c | 92 ++++++--- ipc/shm.c | 21 +- ipc/syscall.c | 9 +- kernel/Makefile | 1 + kernel/audit.h | 2 +- kernel/auditsc.c | 14 +- kernel/compat.c | 340 +++++++++++++------------------ kernel/exit.c | 6 +- kernel/futex.c | 10 +- kernel/sched/core.c | 35 +++- kernel/signal.c | 13 +- kernel/sys.c | 23 ++- kernel/sys_ni.c | 38 ++++ kernel/time/alarmtimer.c | 13 +- kernel/time/hrtimer.c | 20 +- kernel/time/ntp.c | 16 +- kernel/time/ntp_internal.h | 4 +- kernel/time/posix-clock.c | 2 +- kernel/time/posix-cpu-timers.c | 14 +- kernel/time/posix-timers.c | 357 ++++++++++++++++++++++++++++----- kernel/time/time.c | 63 ++++-- kernel/time/timekeeping.c | 2 +- net/compat.c | 18 +- net/socket.c | 52 ++++- scripts/checksyscalls.sh | 25 +++ 167 files changed, 2564 insertions(+), 2289 deletions(-) delete mode 100644 arch/alpha/include/uapi/asm/ipcbuf.h delete mode 100644 arch/alpha/include/uapi/asm/msgbuf.h delete mode 100644 arch/alpha/include/uapi/asm/sembuf.h delete mode 100644 arch/alpha/include/uapi/asm/shmbuf.h delete mode 100644 arch/avr32/include/uapi/asm/msgbuf.h delete mode 100644 arch/avr32/include/uapi/asm/sembuf.h delete mode 100644 arch/avr32/include/uapi/asm/shmbuf.h delete mode 100644 arch/cris/include/uapi/asm/ipcbuf.h delete mode 100644 arch/cris/include/uapi/asm/msgbuf.h delete mode 100644 arch/cris/include/uapi/asm/sembuf.h delete mode 100644 arch/cris/include/uapi/asm/shmbuf.h delete mode 100644 arch/frv/include/uapi/asm/ipcbuf.h delete mode 100644 arch/frv/include/uapi/asm/msgbuf.h delete mode 100644 arch/frv/include/uapi/asm/sembuf.h delete mode 100644 arch/frv/include/uapi/asm/shmbuf.h delete mode 100644 arch/ia64/include/uapi/asm/ipcbuf.h delete mode 100644 arch/ia64/include/uapi/asm/msgbuf.h delete mode 100644 arch/ia64/include/uapi/asm/sembuf.h delete mode 100644 arch/ia64/include/uapi/asm/shmbuf.h delete mode 100644 arch/m32r/include/uapi/asm/ipcbuf.h delete mode 100644 arch/m32r/include/uapi/asm/msgbuf.h delete mode 100644 arch/m32r/include/uapi/asm/sembuf.h delete mode 100644 arch/m32r/include/uapi/asm/shmbuf.h delete mode 100644 arch/mn10300/include/uapi/asm/ipcbuf.h delete mode 100644 arch/mn10300/include/uapi/asm/msgbuf.h delete mode 100644 arch/mn10300/include/uapi/asm/sembuf.h delete mode 100644 arch/mn10300/include/uapi/asm/shmbuf.h delete mode 100644 arch/s390/include/uapi/asm/msgbuf.h delete mode 100644 arch/s390/include/uapi/asm/sembuf.h delete mode 100644 arch/s390/include/uapi/asm/shmbuf.h create mode 100644 include/linux/compat_time.h create mode 100644 include/uapi/asm-generic/kernel_stat.h
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 needs more cleanup
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/compat.h | 36 ++++++++++++++++++++++++++---------- include/linux/compat_time.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 10 deletions(-)
diff --git a/include/linux/compat.h b/include/linux/compat.h index 41b0dae6203b..f550ff00fd30 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,14 +15,6 @@ #include <linux/unistd.h> #include <linux/compat_time.h>
-#include <asm/compat.h> -#include <asm/siginfo.h> -#include <asm/signal.h> - -#ifndef __SC_DELOUSE -#define __SC_DELOUSE(t,v) ((t)(unsigned long)(v)) -#endif - #define COMPAT_SYSCALL_DEFINE0(name) \ asmlinkage long compat_sys_##name(void)
@@ -41,6 +31,32 @@ #define COMPAT_SYSCALL_DEFINE6(name, ...) \ COMPAT_SYSCALL_DEFINEx(6, _##name, __VA_ARGS__)
+#ifndef __SC_DELOUSE +#define __SC_DELOUSE(t,v) ((t)(unsigned long)(v)) +#endif + +#define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ + asmlinkage long compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))\ + __attribute__((alias(__stringify(compat_SyS##name)))); \ + static inline long C_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ + asmlinkage long compat_SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__));\ + asmlinkage long compat_SyS##name(__MAP(x,__SC_LONG,__VA_ARGS__))\ + { \ + return C_SYSC##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__)); \ + } \ + static inline long C_SYSC##name(__MAP(x,__SC_DECL,__VA_ARGS__)) + +extern void __user *compat_alloc_user_space(unsigned long len); + +#ifdef CONFIG_COMPAT +#include <asm/compat.h> +#include <asm/siginfo.h> +#include <asm/signal.h> + +#ifndef __SC_DELOUSE +#define __SC_DELOUSE(t,v) ((t)(unsigned long)(v)) +#endif + #define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ asmlinkage long compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))\ __attribute__((alias(__stringify(compat_SyS##name)))); \ diff --git a/include/linux/compat_time.h b/include/linux/compat_time.h index 37564582f6a5..b5ed064c601c 100644 --- a/include/linux/compat_time.h +++ b/include/linux/compat_time.h @@ -78,6 +78,36 @@ 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 compat_ipc_pid_t __kernel_ipc_pid_t +#define compat_ipc64_perm ipc64_perm +#define compat_semid64_ds semid64_ds +#define compat_shmid64_ds shmid64_ds +#define compat_msgid64_ds msgid64_ds +#define compat_msqid64_ds msqid64_ds +#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 6c1eddc194e8..b2198b6dcde0 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -54,6 +54,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 b5ed064c601c..d3f055036f6d 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>
@@ -266,9 +267,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
'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 | 10 +++++----- kernel/exit.c | 6 +++--- kernel/sys.c | 23 ++++++++++++++--------- 8 files changed, 66 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 d3f055036f6d..84a999be3856 100644 --- a/include/linux/compat_time.h +++ b/include/linux/compat_time.h @@ -147,8 +147,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 76d1e38aabe1..c2342774c192 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; @@ -326,10 +325,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, @@ -651,7 +650,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..c3596ce64ac4 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)) 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)
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 | 42 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 65 insertions(+), 11 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..82933be3e9d9 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> @@ -770,16 +772,46 @@ EXPORT_SYMBOL_GPL(nsecs_to_jiffies); * Add two timespec values and do a safety check for overflow. * It's assumed that both values are valid (>= 0) */ -struct timespec timespec_add_safe(const struct timespec lhs, - const struct timespec rhs) +struct timespec64 timespec64_add_safe(const struct timespec64 lhs, + const struct timespec64 rhs) { - struct timespec res; + struct timespec64 res;
- set_normalized_timespec(&res, lhs.tv_sec + rhs.tv_sec, - lhs.tv_nsec + rhs.tv_nsec); + set_normalized_timespec64(&res, lhs.tv_sec + rhs.tv_sec, + lhs.tv_nsec + rhs.tv_nsec);
if (res.tv_sec < lhs.tv_sec || res.tv_sec < rhs.tv_sec) res.tv_sec = TIME_T_MAX;
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 ++ fs/stat.c | 12 ++++----- include/linux/stat.h | 3 +++ include/linux/syscalls.h | 11 ++++---- include/uapi/asm-generic/kernel_stat.h | 36 +++++++++++++++++++++++++ include/uapi/asm-generic/stat.h | 12 +++++---- 23 files changed, 198 insertions(+), 31 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/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> diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index c2342774c192..f3fdc312627b 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -43,8 +43,6 @@ struct semaphore; struct sembuf; struct shmid_ds; struct sockaddr; -struct stat; -struct stat64; struct statfs; struct statfs64; struct __sysctl_args; @@ -78,6 +76,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 @@ -404,10 +403,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, @@ -773,7 +772,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 changes all system calls that get a select()-style timeout from using timespec internally to using timespec64, and changes the user-facing API to use __kernel_timespec, as a preparation for 32-bit architectures to set CONFIG_COMPAT_TIME.
Note that all these system calls pass a time interval into the kernel, not an absolute time, so using a 32-bit value is sufficient for the operation, but when user space changes over to using 64-bit time_t, its own data structures change, and this makes the kernel match what user space will use.
There may be a small slowdown from using timespec64_sub and timespec64_add_safe instead of the 32-bit variants, so any suggestion for how to avoid that overhead would be welcome.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- fs/compat.c | 16 ++++++------ fs/eventpoll.c | 12 ++++----- fs/select.c | 64 ++++++++++++++++++++++++------------------------ include/linux/poll.h | 10 ++++---- include/linux/socket.h | 4 ++- include/linux/syscalls.h | 6 ++--- include/linux/time.h | 9 ------- include/linux/time64.h | 8 +++++- net/socket.c | 18 +++++++------- 9 files changed, 73 insertions(+), 74 deletions(-)
diff --git a/fs/compat.c b/fs/compat.c index c5065aa1852c..78ffecce6379 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -1098,10 +1098,10 @@ COMPAT_SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, fla #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, +static int poll_select_copy_remaining(struct timespec64 *end_time, void __user *p, int timeval, int ret) { - struct timespec ts; + struct timespec64 ts;
if (!p) return ret; @@ -1113,8 +1113,8 @@ static int poll_select_copy_remaining(struct timespec *end_time, void __user *p, if (!end_time->tv_sec && !end_time->tv_nsec) return ret;
- ktime_get_ts(&ts); - ts = timespec_sub(*end_time, ts); + ktime_get_ts64(&ts); + ts = timespec64_sub(*end_time, ts); if (ts.tv_sec < 0) ts.tv_sec = ts.tv_nsec = 0;
@@ -1228,7 +1228,7 @@ int compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset, */ int compat_core_sys_select(int n, compat_ulong_t __user *inp, compat_ulong_t __user *outp, compat_ulong_t __user *exp, - struct timespec *end_time) + struct timespec64 *end_time) { fd_set_bits fds; void *bits; @@ -1301,7 +1301,7 @@ COMPAT_SYSCALL_DEFINE5(select, int, n, compat_ulong_t __user *, inp, compat_ulong_t __user *, outp, compat_ulong_t __user *, exp, struct compat_timeval __user *, tvp) { - struct timespec end_time, *to = NULL; + struct timespec64 end_time, *to = NULL; struct compat_timeval tv; int ret;
@@ -1348,7 +1348,7 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp, compat_sigset_t ss32; sigset_t ksigmask, sigsaved; struct compat_timespec ts; - struct timespec end_time, *to = NULL; + struct timespec64 end_time, *to = NULL; int ret;
if (tsp) { @@ -1417,7 +1417,7 @@ COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, compat_sigset_t ss32; sigset_t ksigmask, sigsaved; struct compat_timespec ts; - struct timespec end_time, *to = NULL; + struct timespec64 end_time, *to = NULL; int ret;
if (tsp) { diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 1e009cad8d5c..6d1a966967a5 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -1554,15 +1554,15 @@ static int ep_send_events(struct eventpoll *ep, return ep_scan_ready_list(ep, ep_send_events_proc, &esed, 0, false); }
-static inline struct timespec ep_set_mstimeout(long ms) +static inline struct timespec64 ep_set_mstimeout(long ms) { - struct timespec now, ts = { + struct timespec64 now, ts = { .tv_sec = ms / MSEC_PER_SEC, .tv_nsec = NSEC_PER_MSEC * (ms % MSEC_PER_SEC), };
- ktime_get_ts(&now); - return timespec_add_safe(now, ts); + ktime_get_ts64(&now); + return timespec64_add_safe(now, ts); }
/** @@ -1592,11 +1592,11 @@ static int ep_poll(struct eventpoll *ep, struct epoll_event __user *events, ktime_t expires, *to = NULL;
if (timeout > 0) { - struct timespec end_time = ep_set_mstimeout(timeout); + struct timespec64 end_time = ep_set_mstimeout(timeout);
slack = select_estimate_accuracy(&end_time); to = &expires; - *to = timespec_to_ktime(end_time); + *to = timespec64_to_ktime(end_time); } else if (timeout == 0) { /* * Avoid the unnecessary trip to the wait queue loop, if the diff --git a/fs/select.c b/fs/select.c index f684c750e08a..5aa82769ec8b 100644 --- a/fs/select.c +++ b/fs/select.c @@ -47,7 +47,7 @@
#define MAX_SLACK (100 * NSEC_PER_MSEC)
-static long __estimate_accuracy(struct timespec *tv) +static long __estimate_accuracy(struct timespec64 *tv) { long slack; int divfactor = 1000; @@ -70,10 +70,10 @@ static long __estimate_accuracy(struct timespec *tv) return slack; }
-long select_estimate_accuracy(struct timespec *tv) +long select_estimate_accuracy(struct timespec64 *tv) { unsigned long ret; - struct timespec now; + struct timespec64 now;
/* * Realtime tasks get a slack of 0 for obvious reasons. @@ -82,8 +82,8 @@ long select_estimate_accuracy(struct timespec *tv) if (rt_task(current)) return 0;
- ktime_get_ts(&now); - now = timespec_sub(*tv, now); + ktime_get_ts64(&now); + now = timespec64_sub(*tv, now); ret = __estimate_accuracy(&now); if (ret < current->timer_slack_ns) return current->timer_slack_ns; @@ -269,27 +269,27 @@ EXPORT_SYMBOL(poll_schedule_timeout); * * Returns -EINVAL if sec/nsec are not normalized. Otherwise 0. */ -int poll_select_set_timeout(struct timespec *to, long sec, long nsec) +int poll_select_set_timeout(struct timespec64 *to, s64 sec, long nsec) { - struct timespec ts = {.tv_sec = sec, .tv_nsec = nsec}; + struct timespec64 ts = {.tv_sec = sec, .tv_nsec = nsec};
- if (!timespec_valid(&ts)) + if (!timespec64_valid(&ts)) return -EINVAL;
/* Optimize for the zero timeout value here */ if (!sec && !nsec) { to->tv_sec = to->tv_nsec = 0; } else { - ktime_get_ts(to); - *to = timespec_add_safe(*to, ts); + ktime_get_ts64(to); + *to = timespec64_add_safe(*to, ts); } return 0; }
-static int poll_select_copy_remaining(struct timespec *end_time, void __user *p, +static int poll_select_copy_remaining(struct timespec64 *end_time, void __user *p, int timeval, int ret) { - struct timespec rts; + struct timespec64 rts; struct timeval rtv;
if (!p) @@ -302,8 +302,8 @@ static int poll_select_copy_remaining(struct timespec *end_time, void __user *p, if (!end_time->tv_sec && !end_time->tv_nsec) return ret;
- ktime_get_ts(&rts); - rts = timespec_sub(*end_time, rts); + ktime_get_ts64(&rts); + rts = timespec64_sub(*end_time, rts); if (rts.tv_sec < 0) rts.tv_sec = rts.tv_nsec = 0;
@@ -316,7 +316,7 @@ static int poll_select_copy_remaining(struct timespec *end_time, void __user *p, if (!copy_to_user(p, &rtv, sizeof(rtv))) return ret;
- } else if (!copy_to_user(p, &rts, sizeof(rts))) + } else if (!put_timespec64(&rts, p)) return ret;
/* @@ -396,7 +396,7 @@ static inline void wait_key_set(poll_table *wait, unsigned long in, wait->_key |= POLLOUT_SET; }
-int do_select(int n, fd_set_bits *fds, struct timespec *end_time) +int do_select(int n, fd_set_bits *fds, struct timespec64 *end_time) { ktime_t expire, *to = NULL; struct poll_wqueues table; @@ -522,7 +522,7 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time) * pointer to the expiry value. */ if (end_time && !to) { - expire = timespec_to_ktime(*end_time); + expire = timespec64_to_ktime(*end_time); to = &expire; }
@@ -545,7 +545,7 @@ int do_select(int n, fd_set_bits *fds, struct timespec *end_time) * I'm trying ERESTARTNOHAND which restart only when you want to. */ int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, - fd_set __user *exp, struct timespec *end_time) + fd_set __user *exp, struct timespec64 *end_time) { fd_set_bits fds; void *bits; @@ -622,7 +622,7 @@ out_nofds: SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, fd_set __user *, outp, fd_set __user *, exp, struct timeval __user *, tvp) { - struct timespec end_time, *to = NULL; + struct timespec64 end_time, *to = NULL; struct timeval tv; int ret;
@@ -644,15 +644,15 @@ SYSCALL_DEFINE5(select, int, n, fd_set __user *, inp, fd_set __user *, outp, }
static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp, - fd_set __user *exp, struct timespec __user *tsp, + fd_set __user *exp, struct __kernel_timespec __user *tsp, const sigset_t __user *sigmask, size_t sigsetsize) { sigset_t ksigmask, sigsaved; - struct timespec ts, end_time, *to = NULL; + struct timespec64 ts, end_time, *to = NULL; int ret;
if (tsp) { - if (copy_from_user(&ts, tsp, sizeof(ts))) + if (get_timespec64(&ts, tsp)) return -EFAULT;
to = &end_time; @@ -698,7 +698,7 @@ static long do_pselect(int n, fd_set __user *inp, fd_set __user *outp, * the sigset size. */ SYSCALL_DEFINE6(pselect6, int, n, fd_set __user *, inp, fd_set __user *, outp, - fd_set __user *, exp, struct timespec __user *, tsp, + fd_set __user *, exp, struct __kernel_timespec __user *, tsp, void __user *, sig) { size_t sigsetsize = 0; @@ -779,7 +779,7 @@ static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait, }
static int do_poll(unsigned int nfds, struct poll_list *list, - struct poll_wqueues *wait, struct timespec *end_time) + struct poll_wqueues *wait, struct timespec64 *end_time) { poll_table* pt = &wait->pt; ktime_t expire, *to = NULL; @@ -854,7 +854,7 @@ static int do_poll(unsigned int nfds, struct poll_list *list, * pointer to the expiry value. */ if (end_time && !to) { - expire = timespec_to_ktime(*end_time); + expire = timespec64_to_ktime(*end_time); to = &expire; }
@@ -868,7 +868,7 @@ static int do_poll(unsigned int nfds, struct poll_list *list, sizeof(struct pollfd))
int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, - struct timespec *end_time) + struct timespec64 *end_time) { struct poll_wqueues table; int err = -EFAULT, fdcount, len, size; @@ -936,7 +936,7 @@ static long do_restart_poll(struct restart_block *restart_block) { struct pollfd __user *ufds = restart_block->poll.ufds; int nfds = restart_block->poll.nfds; - struct timespec *to = NULL, end_time; + struct timespec64 *to = NULL, end_time; int ret;
if (restart_block->poll.has_timeout) { @@ -957,7 +957,7 @@ static long do_restart_poll(struct restart_block *restart_block) SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds, int, timeout_msecs) { - struct timespec end_time, *to = NULL; + struct timespec64 end_time, *to = NULL; int ret;
if (timeout_msecs >= 0) { @@ -989,15 +989,15 @@ SYSCALL_DEFINE3(poll, struct pollfd __user *, ufds, unsigned int, nfds, }
SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds, - struct timespec __user *, tsp, const sigset_t __user *, sigmask, - size_t, sigsetsize) + struct __kernel_timespec __user *, tsp, + const sigset_t __user *, sigmask, size_t, sigsetsize) { sigset_t ksigmask, sigsaved; - struct timespec ts, end_time, *to = NULL; + struct timespec64 ts, end_time, *to = NULL; int ret;
if (tsp) { - if (copy_from_user(&ts, tsp, sizeof(ts))) + if (get_timespec64(&ts, tsp)) return -EFAULT;
to = &end_time; diff --git a/include/linux/poll.h b/include/linux/poll.h index c08386fb3e08..92b7f5bea749 100644 --- a/include/linux/poll.h +++ b/include/linux/poll.h @@ -96,7 +96,7 @@ extern void poll_initwait(struct poll_wqueues *pwq); extern void poll_freewait(struct poll_wqueues *pwq); extern int poll_schedule_timeout(struct poll_wqueues *pwq, int state, ktime_t *expires, unsigned long slack); -extern long select_estimate_accuracy(struct timespec *tv); +extern long select_estimate_accuracy(struct timespec64 *tv);
static inline int poll_schedule(struct poll_wqueues *pwq, int state) @@ -153,12 +153,12 @@ void zero_fd_set(unsigned long nr, unsigned long *fdset)
#define MAX_INT64_SECONDS (((s64)(~((u64)0)>>1)/HZ)-1)
-extern int do_select(int n, fd_set_bits *fds, struct timespec *end_time); +extern int do_select(int n, fd_set_bits *fds, struct timespec64 *end_time); extern int do_sys_poll(struct pollfd __user * ufds, unsigned int nfds, - struct timespec *end_time); + struct timespec64 *end_time); extern int core_sys_select(int n, fd_set __user *inp, fd_set __user *outp, - fd_set __user *exp, struct timespec *end_time); + fd_set __user *exp, struct timespec64 *end_time);
-extern int poll_select_set_timeout(struct timespec *to, long sec, long nsec); +extern int poll_select_set_timeout(struct timespec64 *to, long long sec, long nsec);
#endif /* _LINUX_POLL_H */ diff --git a/include/linux/socket.h b/include/linux/socket.h index 5bf59c8493b7..ebacba2fa111 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -7,6 +7,8 @@ #include <linux/uio.h> /* iovec support */ #include <linux/types.h> /* pid_t */ #include <linux/compiler.h> /* __user */ +#include <linux/math64.h> +#include <linux/time64.h> /* timespec64 */ #include <uapi/linux/socket.h>
struct pid; @@ -335,7 +337,7 @@ struct timespec; extern long __sys_recvmsg(int fd, struct user_msghdr __user *msg, unsigned flags); extern long __sys_sendmsg(int fd, struct user_msghdr __user *msg, unsigned flags); extern int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, - unsigned int flags, struct timespec *timeout); + unsigned int flags, struct timespec64 *timeout); extern int __sys_sendmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, unsigned int flags); #endif /* _LINUX_SOCKET_H */ diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index f3fdc312627b..855897ee0c6d 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -612,7 +612,7 @@ asmlinkage long sys_recvfrom(int, void __user *, size_t, unsigned, asmlinkage long sys_recvmsg(int fd, struct user_msghdr __user *msg, unsigned flags); asmlinkage long sys_recvmmsg(int fd, struct mmsghdr __user *msg, unsigned int vlen, unsigned flags, - struct timespec __user *timeout); + struct __kernel_timespec __user *timeout); asmlinkage long sys_socket(int, int, int); asmlinkage long sys_socketpair(int, int, int, int __user *); asmlinkage long sys_socketcall(int call, unsigned long __user *args); @@ -811,10 +811,10 @@ asmlinkage long sys_memfd_create(const char __user *uname_ptr, unsigned int flag asmlinkage long sys_fallocate(int fd, int mode, loff_t offset, loff_t len); asmlinkage long sys_old_readdir(unsigned int, struct old_linux_dirent __user *, unsigned int); asmlinkage long sys_pselect6(int, fd_set __user *, fd_set __user *, - fd_set __user *, struct timespec __user *, + fd_set __user *, struct __kernel_timespec __user *, void __user *); asmlinkage long sys_ppoll(struct pollfd __user *, unsigned int, - struct timespec __user *, const sigset_t __user *, + struct __kernel_timespec __user *, const sigset_t __user *, size_t); asmlinkage long sys_fanotify_init(unsigned int flags, unsigned int event_f_flags); asmlinkage long sys_fanotify_mark(int fanotify_fd, unsigned int flags, diff --git a/include/linux/time.h b/include/linux/time.h index beebe3a02d43..5b5a64952df7 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -56,15 +56,6 @@ static inline unsigned long mktime(const unsigned int year,
extern void set_normalized_timespec(struct timespec *ts, time_t sec, s64 nsec);
-/* - * timespec_add_safe assumes both values are positive and checks - * for overflow. It will return TIME_T_MAX if the reutrn would be - * smaller then either of the arguments. - */ -extern struct timespec timespec_add_safe(const struct timespec lhs, - const struct timespec rhs); - - static inline struct timespec timespec_add(struct timespec lhs, struct timespec rhs) { diff --git a/include/linux/time64.h b/include/linux/time64.h index 880ebe4b4ba4..4ec589daf565 100644 --- a/include/linux/time64.h +++ b/include/linux/time64.h @@ -46,7 +46,6 @@ static inline struct timespec64 timespec_to_timespec64(const struct timespec ts) # define timespec64_equal timespec_equal # define timespec64_compare timespec_compare # define set_normalized_timespec64 set_normalized_timespec -# define timespec64_add_safe timespec_add_safe # define timespec64_add timespec_add # define timespec64_sub timespec_sub # define timespec64_valid timespec_valid @@ -187,6 +186,13 @@ static __always_inline void timespec64_add_ns(struct timespec64 *a, u64 ns)
#endif
+/* + * timespec64_add_safe assumes both values are positive and checks + * for overflow. It will return TIME_T_MAX if the reutrn would be + * smaller then either of the arguments. + */ +extern struct timespec64 timespec64_add_safe(const struct timespec64 lhs, + const struct timespec64 rhs); extern int get_timespec64(struct timespec64 *ts, const struct __kernel_timespec __user *uts); extern int put_timespec64(const struct timespec64 *ts, diff --git a/net/socket.c b/net/socket.c index 884e32997698..bfe50f60688b 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2170,14 +2170,14 @@ SYSCALL_DEFINE3(recvmsg, int, fd, struct user_msghdr __user *, msg, */
int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, - unsigned int flags, struct timespec *timeout) + unsigned int flags, struct timespec64 *timeout) { int fput_needed, err, datagrams; struct socket *sock; struct mmsghdr __user *entry; struct compat_mmsghdr __user *compat_entry; struct msghdr msg_sys; - struct timespec end_time; + struct timespec64 end_time;
if (timeout && poll_select_set_timeout(&end_time, timeout->tv_sec, @@ -2229,8 +2229,8 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, flags |= MSG_DONTWAIT;
if (timeout) { - ktime_get_ts(timeout); - *timeout = timespec_sub(end_time, *timeout); + ktime_get_ts64(timeout); + *timeout = timespec64_sub(end_time, *timeout); if (timeout->tv_sec < 0) { timeout->tv_sec = timeout->tv_nsec = 0; break; @@ -2275,10 +2275,10 @@ out_put:
SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg, unsigned int, vlen, unsigned int, flags, - struct timespec __user *, timeout) + struct __kernel_timespec __user *, timeout) { int datagrams; - struct timespec timeout_sys; + struct timespec64 timeout_sys;
if (flags & MSG_CMSG_COMPAT) return -EINVAL; @@ -2286,13 +2286,13 @@ SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg, if (!timeout) return __sys_recvmmsg(fd, mmsg, vlen, flags, NULL);
- if (copy_from_user(&timeout_sys, timeout, sizeof(timeout_sys))) + if (get_timespec64(&timeout_sys, timeout)) return -EFAULT;
datagrams = __sys_recvmmsg(fd, mmsg, vlen, flags, &timeout_sys);
if (datagrams > 0 && - copy_to_user(timeout, &timeout_sys, sizeof(timeout_sys))) + put_timespec64(&timeout_sys, timeout)) datagrams = -EFAULT;
return datagrams; @@ -2410,7 +2410,7 @@ SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args) break; case SYS_RECVMMSG: err = sys_recvmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3], - (struct timespec __user *)a[4]); + (struct __kernel_timespec __user *)a[4]); break; case SYS_ACCEPT4: err = sys_accept4(a0, (struct sockaddr __user *)a1,
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 84a999be3856..dada7bd49f58 100644 --- a/include/linux/compat_time.h +++ b/include/linux/compat_time.h @@ -118,6 +118,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 c3596ce64ac4..ad428b433d02 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)
Signed-off-by: Arnd Bergmann arnd@arndb.de --- fs/aio.c | 54 +++++++++++++++++++++++++++++++++++------------- fs/compat.c | 24 --------------------- include/linux/syscalls.h | 2 +- 3 files changed, 41 insertions(+), 39 deletions(-)
diff --git a/fs/aio.c b/fs/aio.c index 480440f4701f..321212b9f4b6 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -1250,20 +1250,9 @@ static bool aio_read_events(struct kioctx *ctx, long min_nr, long nr,
static long read_events(struct kioctx *ctx, long min_nr, long nr, struct io_event __user *event, - struct timespec __user *timeout) + ktime_t until) { - ktime_t until = { .tv64 = KTIME_MAX }; long ret = 0; - - if (timeout) { - struct timespec ts; - - if (unlikely(copy_from_user(&ts, timeout, sizeof(ts)))) - return -EFAULT; - - until = timespec_to_ktime(ts); - } - /* * Note that aio_read_events() is being called as the conditional - i.e. * we're calling it after prepare_to_wait() has set task state to @@ -1718,15 +1707,52 @@ SYSCALL_DEFINE5(io_getevents, aio_context_t, ctx_id, long, min_nr, long, nr, struct io_event __user *, events, - struct timespec __user *, timeout) + struct __kernel_timespec __user *, timeout) +{ + struct kioctx *ioctx = lookup_ioctx(ctx_id); + ktime_t until = { .tv64 = KTIME_MAX }; + struct timespec64 ts; + long ret = -EINVAL; + + if (likely(ioctx)) { + if (timeout) { + if (unlikely(get_timespec64(&ts, timeout))) + return -EFAULT; + + until = timespec64_to_ktime(ts); + } + + if (likely(min_nr <= nr && min_nr >= 0)) + ret = read_events(ioctx, min_nr, nr, events, until); + percpu_ref_put(&ioctx->users); + } + return ret; +} + +#ifdef CONFIG_COMPAT_TIME +COMPAT_SYSCALL_DEFINE5(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) { struct kioctx *ioctx = lookup_ioctx(ctx_id); + ktime_t until = { .tv64 = KTIME_MAX }; + struct timespec64 ts; long ret = -EINVAL;
if (likely(ioctx)) { + if (timeout) { + if (unlikely(compat_get_timespec64(&ts, timeout))) + return -EFAULT; + + until = timespec64_to_ktime(ts); + } + if (likely(min_nr <= nr && min_nr >= 0)) - ret = read_events(ioctx, min_nr, nr, events, timeout); + ret = read_events(ioctx, min_nr, nr, events, until); percpu_ref_put(&ioctx->users); } return ret; } +#endif diff --git a/fs/compat.c b/fs/compat.c index 78ffecce6379..b9e55d5de311 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -508,31 +508,7 @@ 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, - struct io_event __user *, events, - struct compat_timespec __user *, timeout) -{ - struct timespec t; - struct timespec __user *ut = NULL; - - if (timeout) { - if (compat_get_timespec(&t, timeout)) - return -EFAULT;
- ut = compat_alloc_user_space(sizeof(*ut)); - if (copy_to_user(ut, &t, sizeof(t)) ) - return -EFAULT; - } - 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)
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 855897ee0c6d..4a8e7294ed2c 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -500,7 +500,7 @@ asmlinkage long sys_io_getevents(aio_context_t ctx_id, long min_nr, long nr, struct io_event __user *events, - struct timespec __user *timeout); + struct __kernel_timespec __user *timeout); asmlinkage long sys_io_submit(aio_context_t, long, struct iocb __user * __user *); asmlinkage long sys_io_cancel(aio_context_t ctx_id, struct iocb __user *iocb,
Signed-off-by: Arnd Bergmann arnd@arndb.de --- fs/compat.c | 47 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 15 deletions(-)
diff --git a/fs/compat.c b/fs/compat.c index b9e55d5de311..fac162df4b9b 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -1125,6 +1125,7 @@ sticky: return ret; }
+#ifdef CONFIG_COMPAT /* * Ooo, nasty. We need here to frob 32-bit unsigned longs to * 64-bit unsigned longs. @@ -1187,7 +1188,10 @@ int compat_set_fd_set(unsigned long nr, compat_ulong_t __user *ufdset, return -EFAULT; return 0; } - +#else +#define compat_get_fd_set get_fd_set +#define compat_set_fd_set set_fd_set +#endif
/* * This is a virtual copy of sys_select from fs/select.c and probably @@ -1316,12 +1320,32 @@ COMPAT_SYSCALL_DEFINE1(old_select, struct compat_sel_arg_struct __user *, arg) compat_ptr(a.exp), compat_ptr(a.tvp)); }
+int compat_copy_sigset_from_user(sigset_t *out, const compat_sigset_t __user *in, + size_t sigsetsize) +{ +#ifdef CONFIG_COMPAT + compat_sigset_t ss32; + + if (!in) + return 0; + + if (sigsetsize != sizeof(compat_sigset_t)) + return -EINVAL; + if (copy_from_user(&ss32, in, sizeof(ss32))) + return -EFAULT; + sigset_from_compat(out, &ss32); +#else + if (copy_from_user(out, in, sizeof(*out))) + return -EFAULT; +#endif + return 0; +} + static long do_compat_pselect(int n, compat_ulong_t __user *inp, compat_ulong_t __user *outp, compat_ulong_t __user *exp, struct compat_timespec __user *tsp, compat_sigset_t __user *sigmask, compat_size_t sigsetsize) { - compat_sigset_t ss32; sigset_t ksigmask, sigsaved; struct compat_timespec ts; struct timespec64 end_time, *to = NULL; @@ -1337,12 +1361,9 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp, }
if (sigmask) { - if (sigsetsize != sizeof(compat_sigset_t)) - return -EINVAL; - if (copy_from_user(&ss32, sigmask, sizeof(ss32))) - return -EFAULT; - sigset_from_compat(&ksigmask, &ss32); - + ret = compat_copy_sigset_from_user(&ksigmask, sigmask, sigsetsize); + if (ret) + return ret; sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); } @@ -1390,7 +1411,6 @@ COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, unsigned int, nfds, struct compat_timespec __user *, tsp, const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize) { - compat_sigset_t ss32; sigset_t ksigmask, sigsaved; struct compat_timespec ts; struct timespec64 end_time, *to = NULL; @@ -1406,12 +1426,9 @@ COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, }
if (sigmask) { - if (sigsetsize != sizeof(compat_sigset_t)) - return -EINVAL; - if (copy_from_user(&ss32, sigmask, sizeof(ss32))) - return -EFAULT; - sigset_from_compat(&ksigmask, &ss32); - + ret = compat_copy_sigset_from_user(&ksigmask, sigmask, sigsetsize); + if (ret) + return ret; sigdelsetmask(&ksigmask, sigmask(SIGKILL)|sigmask(SIGSTOP)); sigprocmask(SIG_SETMASK, &ksigmask, &sigsaved); }
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/compat_time.h | 3 +++ include/net/compat.h | 3 +++ include/uapi/linux/net.h | 1 + net/compat.c | 18 ++++++++++++------ net/socket.c | 36 ++++++++++++++++++++++++++++++++++-- 5 files changed, 53 insertions(+), 8 deletions(-)
diff --git a/include/linux/compat_time.h b/include/linux/compat_time.h index dada7bd49f58..6d129a7f3843 100644 --- a/include/linux/compat_time.h +++ b/include/linux/compat_time.h @@ -244,6 +244,9 @@ 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_recvmmsg64(int fd, struct compat_mmsghdr __user *mmsg, + unsigned vlen, unsigned int flags, + struct __kernel_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, diff --git a/include/net/compat.h b/include/net/compat.h index 48103cf94e97..dbb5b6b949c5 100644 --- a/include/net/compat.h +++ b/include/net/compat.h @@ -51,6 +51,9 @@ asmlinkage long compat_sys_recvmsg(int, struct compat_msghdr __user *, asmlinkage long compat_sys_recvmmsg(int, struct compat_mmsghdr __user *, unsigned int, unsigned int, struct compat_timespec __user *); +asmlinkage long compat_sys_recvmmsg64(int, struct compat_mmsghdr __user *, + unsigned int, unsigned int, + struct __kernel_timespec __user *); asmlinkage long compat_sys_getsockopt(int, int, int, char __user *, int __user *); int put_cmsg_compat(struct msghdr*, int, int, int, void *); diff --git a/include/uapi/linux/net.h b/include/uapi/linux/net.h index 9457239ed219..7601df60f380 100644 --- a/include/uapi/linux/net.h +++ b/include/uapi/linux/net.h @@ -43,6 +43,7 @@ #define SYS_ACCEPT4 18 /* sys_accept4(2) */ #define SYS_RECVMMSG 19 /* sys_recvmmsg(2) */ #define SYS_SENDMMSG 20 /* sys_sendmmsg(2) */ +#define SYS_RECVMMSG64 21 /* sys_recvmmsg(2), time64_t */
typedef enum { SS_FREE = 0, /* not allocated */ diff --git a/net/compat.c b/net/compat.c index 5cfd26a0006f..15c326adbb6c 100644 --- a/net/compat.c +++ b/net/compat.c @@ -743,23 +743,23 @@ COMPAT_SYSCALL_DEFINE6(recvfrom, int, fd, void __user *, buf, compat_size_t, len return sys_recvfrom(fd, buf, len, flags | MSG_CMSG_COMPAT, addr, addrlen); }
-COMPAT_SYSCALL_DEFINE5(recvmmsg, int, fd, struct compat_mmsghdr __user *, mmsg, +COMPAT_SYSCALL_DEFINE5(recvmmsg64, int, fd, struct compat_mmsghdr __user *, mmsg, unsigned int, vlen, unsigned int, flags, - struct compat_timespec __user *, timeout) + struct __kernel_timespec __user *, timeout) { int datagrams; - struct timespec ktspec; + struct timespec64 timeout_sys;
if (timeout == NULL) return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, flags | MSG_CMSG_COMPAT, NULL);
- if (compat_get_timespec(&ktspec, timeout)) + if (get_timespec64(&timeout_sys, timeout)) return -EFAULT;
datagrams = __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, - flags | MSG_CMSG_COMPAT, &ktspec); - if (datagrams > 0 && compat_put_timespec(&ktspec, timeout)) + flags | MSG_CMSG_COMPAT, &timeout_sys); + if (datagrams > 0 && put_timespec64(&timeout_sys, timeout)) datagrams = -EFAULT;
return datagrams; @@ -836,13 +836,19 @@ COMPAT_SYSCALL_DEFINE2(socketcall, int, call, u32 __user *, args) case SYS_RECVMSG: ret = compat_sys_recvmsg(a0, compat_ptr(a1), a[2]); break; +#ifdef CONFIG_COMPAT_TIME case SYS_RECVMMSG: ret = compat_sys_recvmmsg(a0, compat_ptr(a1), a[2], a[3], compat_ptr(a[4])); break; +#endif case SYS_ACCEPT4: ret = sys_accept4(a0, compat_ptr(a1), compat_ptr(a[2]), a[3]); break; + case SYS_RECVMMSG64: + ret = compat_sys_recvmmsg64(a0, compat_ptr(a1), a[2], a[3], + compat_ptr(a[4])); + break; default: ret = -EINVAL; break; diff --git a/net/socket.c b/net/socket.c index bfe50f60688b..632e0004c972 100644 --- a/net/socket.c +++ b/net/socket.c @@ -2197,6 +2197,9 @@ int __sys_recvmmsg(int fd, struct mmsghdr __user *mmsg, unsigned int vlen, entry = mmsg; compat_entry = (struct compat_mmsghdr __user *)mmsg;
+ if (!IS_ENABLED(CONFIG_COMPAT)) + flags &= ~MSG_CMSG_COMPAT; + while (datagrams < vlen) { /* * No need to ask LSM for more than the first datagram. @@ -2291,13 +2294,36 @@ SYSCALL_DEFINE5(recvmmsg, int, fd, struct mmsghdr __user *, mmsg,
datagrams = __sys_recvmmsg(fd, mmsg, vlen, flags, &timeout_sys);
- if (datagrams > 0 && - put_timespec64(&timeout_sys, timeout)) + if (datagrams > 0 && put_timespec64(&timeout_sys, timeout)) datagrams = -EFAULT;
return datagrams; }
+#ifdef CONFIG_COMPAT_TIME +COMPAT_SYSCALL_DEFINE5(recvmmsg, int, fd, struct compat_mmsghdr __user *, mmsg, + unsigned int, vlen, unsigned int, flags, + struct compat_timespec __user *, timeout) +{ + int datagrams; + struct timespec64 ktspec; + + if (timeout == NULL) + return __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, + flags | MSG_CMSG_COMPAT, NULL); + + if (compat_get_timespec64(&ktspec, timeout)) + return -EFAULT; + + datagrams = __sys_recvmmsg(fd, (struct mmsghdr __user *)mmsg, vlen, + flags | MSG_CMSG_COMPAT, &ktspec); + if (datagrams > 0 && compat_put_timespec64(&ktspec, timeout)) + datagrams = -EFAULT; + + return datagrams; +} +#endif + #ifdef __ARCH_WANT_SYS_SOCKETCALL /* Argument list sizes for sys_socketcall */ #define AL(x) ((x) * sizeof(unsigned long)) @@ -2409,6 +2435,12 @@ SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args) err = sys_recvmsg(a0, (struct user_msghdr __user *)a1, a[2]); break; case SYS_RECVMMSG: +#if !defined(CONFIG_64BIT) && defined(CONFIG_COMPAT_TIME) + err = compat_sys_recvmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3], + (struct compat_timespec __user *)a[4]); + break; + case SYS_RECVMMSG64: +#endif err = sys_recvmmsg(a0, (struct mmsghdr __user *)a1, a[2], a[3], (struct __kernel_timespec __user *)a[4]); break;
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 4a8e7294ed2c..ad196d7b9eba 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);
The shmid64_ds/semid64_ds/msqid64_ds data structures have been extended to contain extra fields for storing the upper bits of the time stamps, this patch does the other half of the job and converts the internal data structures to use time64_t, and fill the new fields on 32-bit architectures as well as 32-bit tasks running on a 64-bit kernel in compat mode. There should be no change for native 64-bit tasks.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/msg.h | 7 ++++--- include/linux/sem.h | 3 ++- include/linux/shm.h | 7 ++++--- ipc/compat.c | 8 ++++++++ ipc/msg.c | 23 ++++++++++++++--------- ipc/sem.c | 32 +++++++++++++++++++------------- ipc/shm.c | 21 +++++++++++++-------- 7 files changed, 64 insertions(+), 37 deletions(-)
diff --git a/include/linux/msg.h b/include/linux/msg.h index f3f302f9c197..10a0d8c99567 100644 --- a/include/linux/msg.h +++ b/include/linux/msg.h @@ -2,6 +2,7 @@ #define _LINUX_MSG_H
#include <linux/list.h> +#include <linux/time.h> #include <uapi/linux/msg.h>
/* one msg_msg structure for each message */ @@ -17,9 +18,9 @@ struct msg_msg { /* one msq_queue structure for each present queue on the system */ struct msg_queue { struct kern_ipc_perm q_perm; - time_t q_stime; /* last msgsnd time */ - time_t q_rtime; /* last msgrcv time */ - time_t q_ctime; /* last change time */ + time64_t q_stime; /* last msgsnd time */ + time64_t q_rtime; /* last msgrcv time */ + time64_t q_ctime; /* last change time */ unsigned long q_cbytes; /* current number of bytes on queue */ unsigned long q_qnum; /* number of messages in queue */ unsigned long q_qbytes; /* max number of bytes on queue */ diff --git a/include/linux/sem.h b/include/linux/sem.h index 976ce3a19f1b..b408983f029e 100644 --- a/include/linux/sem.h +++ b/include/linux/sem.h @@ -4,6 +4,7 @@ #include <linux/atomic.h> #include <linux/rcupdate.h> #include <linux/cache.h> +#include <linux/time.h> #include <uapi/linux/sem.h>
struct task_struct; @@ -12,7 +13,7 @@ struct task_struct; struct sem_array { struct kern_ipc_perm ____cacheline_aligned_in_smp sem_perm; /* permissions .. see ipc.h */ - time_t sem_ctime; /* last change time */ + time64_t sem_ctime; /* last change time */ struct sem *sem_base; /* ptr to first semaphore in array */ struct list_head pending_alter; /* pending operations */ /* that alter the array */ diff --git a/include/linux/shm.h b/include/linux/shm.h index 6fb801686ad6..0a28ae1be608 100644 --- a/include/linux/shm.h +++ b/include/linux/shm.h @@ -2,6 +2,7 @@ #define _LINUX_SHM_H_
#include <linux/list.h> +#include <linux/time.h> #include <asm/page.h> #include <uapi/linux/shm.h> #include <asm/shmparam.h> @@ -12,9 +13,9 @@ struct shmid_kernel /* private to the kernel */ struct file *shm_file; unsigned long shm_nattch; unsigned long shm_segsz; - time_t shm_atim; - time_t shm_dtim; - time_t shm_ctim; + time64_t shm_atim; + time64_t shm_dtim; + time64_t shm_ctim; pid_t shm_cprid; pid_t shm_lprid; struct user_struct *mlock_user; diff --git a/ipc/compat.c b/ipc/compat.c index 2bbdb093d1be..2505f628e221 100644 --- a/ipc/compat.c +++ b/ipc/compat.c @@ -215,7 +215,9 @@ static inline int put_compat_semid64_ds(struct semid64_ds *sem64, if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) return -EFAULT; err = __put_compat_ipc64_perm(&sem64->sem_perm, &up64->sem_perm); + err |= __put_user(sem64->sem_otime >> 32, &up64->sem_otime_high); err |= __put_user(sem64->sem_otime, &up64->sem_otime); + err |= __put_user(sem64->sem_ctime >> 32, &up64->sem_ctime_high); err |= __put_user(sem64->sem_ctime, &up64->sem_ctime); err |= __put_user(sem64->sem_nsems, &up64->sem_nsems); return err; @@ -465,8 +467,11 @@ static inline int put_compat_msqid64_ds(struct msqid64_ds *m64, if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) return -EFAULT; err = __put_compat_ipc64_perm(&m64->msg_perm, &up64->msg_perm); + err |= __put_user(m64->msg_stime >> 32, &up64->msg_stime_high); err |= __put_user(m64->msg_stime, &up64->msg_stime); + err |= __put_user(m64->msg_rtime >> 32, &up64->msg_rtime_high); err |= __put_user(m64->msg_rtime, &up64->msg_rtime); + err |= __put_user(m64->msg_ctime >> 32, &up64->msg_ctime_high); err |= __put_user(m64->msg_ctime, &up64->msg_ctime); err |= __put_user(m64->msg_cbytes, &up64->msg_cbytes); err |= __put_user(m64->msg_qnum, &up64->msg_qnum); @@ -585,8 +590,11 @@ static inline int put_compat_shmid64_ds(struct shmid64_ds *sem64, if (!access_ok(VERIFY_WRITE, up64, sizeof(*up64))) return -EFAULT; err = __put_compat_ipc64_perm(&sem64->shm_perm, &up64->shm_perm); + err |= __put_user(sem64->shm_atime >> 32, &up64->shm_atime_high); err |= __put_user(sem64->shm_atime, &up64->shm_atime); + err |= __put_user(sem64->shm_dtime >> 32, &up64->shm_dtime_high); err |= __put_user(sem64->shm_dtime, &up64->shm_dtime); + err |= __put_user(sem64->shm_ctime >> 32, &up64->shm_ctime_high); err |= __put_user(sem64->shm_ctime, &up64->shm_ctime); err |= __put_user(sem64->shm_segsz, &up64->shm_segsz); err |= __put_user(sem64->shm_nattch, &up64->shm_nattch); diff --git a/ipc/msg.c b/ipc/msg.c index e0b5e216ace5..6817779e8269 100644 --- a/ipc/msg.c +++ b/ipc/msg.c @@ -146,7 +146,7 @@ static int newque(struct ipc_namespace *ns, struct ipc_params *params) }
msq->q_stime = msq->q_rtime = 0; - msq->q_ctime = get_seconds(); + msq->q_ctime = ktime_get_real_seconds(); msq->q_cbytes = msq->q_qnum = 0; msq->q_qbytes = ns->msg_ctlmnb; msq->q_lspid = msq->q_lrpid = 0; @@ -386,7 +386,7 @@ static int msgctl_down(struct ipc_namespace *ns, int msqid, int cmd,
msq->q_qbytes = msqid64.msg_qbytes;
- msq->q_ctime = get_seconds(); + msq->q_ctime = ktime_get_real_seconds(); /* sleeping receivers might be excluded by * stricter permissions. */ @@ -498,6 +498,11 @@ static int msgctl_nolock(struct ipc_namespace *ns, int msqid, tbuf.msg_stime = msq->q_stime; tbuf.msg_rtime = msq->q_rtime; tbuf.msg_ctime = msq->q_ctime; +#ifndef CONFIG_64BIT + tbuf.msg_stime_high = msq->q_stime >> 32; + tbuf.msg_rtime_high = msq->q_rtime >> 32; + tbuf.msg_ctime_high = msq->q_ctime >> 32; +#endif tbuf.msg_cbytes = msq->q_cbytes; tbuf.msg_qnum = msq->q_qnum; tbuf.msg_qbytes = msq->q_qbytes; @@ -586,7 +591,7 @@ static inline int pipelined_send(struct msg_queue *msq, struct msg_msg *msg) } else { msr->r_msg = NULL; msq->q_lrpid = task_pid_vnr(msr->r_tsk); - msq->q_rtime = get_seconds(); + msq->q_rtime = ktime_get_real_seconds(); wake_up_process(msr->r_tsk); /* * Ensure that the wakeup is visible before @@ -695,7 +700,7 @@ long do_msgsnd(int msqid, long mtype, void __user *mtext,
} msq->q_lspid = task_tgid_vnr(current); - msq->q_stime = get_seconds(); + msq->q_stime = ktime_get_real_seconds();
if (!pipelined_send(msq, msg)) { /* no one is waiting for this message, enqueue it */ @@ -887,7 +892,7 @@ long do_msgrcv(int msqid, void __user *buf, size_t bufsz, long msgtyp, int msgfl
list_del(&msg->m_list); msq->q_qnum--; - msq->q_rtime = get_seconds(); + msq->q_rtime = ktime_get_real_seconds(); msq->q_lrpid = task_tgid_vnr(current); msq->q_cbytes -= msg->m_ts; atomic_sub(msg->m_ts, &ns->msg_bytes); @@ -1017,7 +1022,7 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it) struct msg_queue *msq = it;
seq_printf(s, - "%10d %10d %4o %10lu %10lu %5u %5u %5u %5u %5u %5u %10lu %10lu %10lu\n", + "%10d %10d %4o %10lu %10lu %5u %5u %5u %5u %5u %5u %10llu %10llu %10llu\n", msq->q_perm.key, msq->q_perm.id, msq->q_perm.mode, @@ -1029,9 +1034,9 @@ static int sysvipc_msg_proc_show(struct seq_file *s, void *it) from_kgid_munged(user_ns, msq->q_perm.gid), from_kuid_munged(user_ns, msq->q_perm.cuid), from_kgid_munged(user_ns, msq->q_perm.cgid), - msq->q_stime, - msq->q_rtime, - msq->q_ctime); + (u64)msq->q_stime, + (u64)msq->q_rtime, + (u64)msq->q_ctime);
return 0; } diff --git a/ipc/sem.c b/ipc/sem.c index a6ff6754651c..42032ae3cc12 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -99,7 +99,7 @@ struct sem { /* that alter the semaphore */ struct list_head pending_const; /* pending single-sop operations */ /* that do not alter the semaphore*/ - time_t sem_otime; /* candidate for sem_otime */ + time64_t sem_otime; /* candidate for sem_otime */ } ____cacheline_aligned_in_smp;
/* One queue for each sleeping process in the system. */ @@ -528,7 +528,7 @@ static int newary(struct ipc_namespace *ns, struct ipc_params *params) INIT_LIST_HEAD(&sma->pending_const); INIT_LIST_HEAD(&sma->list_id); sma->sem_nsems = nsems; - sma->sem_ctime = get_seconds(); + sma->sem_ctime = ktime_get_real_seconds();
id = ipc_addid(&sem_ids(ns), &sma->sem_perm, ns->sc_semmni); if (id < 0) { @@ -940,10 +940,10 @@ again: static void set_semotime(struct sem_array *sma, struct sembuf *sops) { if (sops == NULL) { - sma->sem_base[0].sem_otime = get_seconds(); + sma->sem_base[0].sem_otime = ktime_get_real_seconds(); } else { sma->sem_base[sops[0].sem_num].sem_otime = - get_seconds(); + ktime_get_real_seconds(); } }
@@ -1151,14 +1151,14 @@ static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, } }
-static time_t get_semotime(struct sem_array *sma) +static time64_t get_semotime(struct sem_array *sma) { int i; - time_t res; + time64_t res;
res = sma->sem_base[0].sem_otime; for (i = 1; i < sma->sem_nsems; i++) { - time_t to = sma->sem_base[i].sem_otime; + time64_t to = sma->sem_base[i].sem_otime;
if (to > res) res = to; @@ -1210,6 +1210,7 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, case SEM_STAT: { struct semid64_ds tbuf; + time64_t semotime; int id = 0;
memset(&tbuf, 0, sizeof(tbuf)); @@ -1239,9 +1240,14 @@ static int semctl_nolock(struct ipc_namespace *ns, int semid, goto out_unlock;
kernel_to_ipc64_perm(&sma->sem_perm, &tbuf.sem_perm); - tbuf.sem_otime = get_semotime(sma); + semotime = get_semotime(sma); + tbuf.sem_otime = semotime; tbuf.sem_ctime = sma->sem_ctime; tbuf.sem_nsems = sma->sem_nsems; +#ifndef CONFIG_64BIT + tbuf.sem_ctime_high = sma->sem_ctime >> 32; + tbuf.sem_ctime_high = semotime >> 32; +#endif rcu_read_unlock(); if (copy_semid_to_user(p, &tbuf, version)) return -EFAULT; @@ -1317,7 +1323,7 @@ static int semctl_setval(struct ipc_namespace *ns, int semid, int semnum,
curr->semval = val; curr->sempid = task_tgid_vnr(current); - sma->sem_ctime = get_seconds(); + sma->sem_ctime = ktime_get_real_seconds(); /* maybe some queued-up processes were waiting for this */ do_smart_update(sma, NULL, 0, 0, &tasks); sem_unlock(sma, -1); @@ -1443,7 +1449,7 @@ static int semctl_main(struct ipc_namespace *ns, int semid, int semnum, for (i = 0; i < nsems; i++) un->semadj[i] = 0; } - sma->sem_ctime = get_seconds(); + sma->sem_ctime = ktime_get_real_seconds(); /* maybe some queued-up processes were waiting for this */ do_smart_update(sma, NULL, 0, 0, &tasks); err = 0; @@ -1559,7 +1565,7 @@ static int semctl_down(struct ipc_namespace *ns, int semid, err = ipc_update_perm(&semid64.sem_perm, ipcp); if (err) goto out_unlock0; - sma->sem_ctime = get_seconds(); + sma->sem_ctime = ktime_get_real_seconds(); break; default: err = -EINVAL; @@ -2184,7 +2190,7 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it) { struct user_namespace *user_ns = seq_user_ns(s); struct sem_array *sma = it; - time_t sem_otime; + u64 sem_otime;
/* * The proc interface isn't aware of sem_lock(), it calls @@ -2197,7 +2203,7 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it) sem_otime = get_semotime(sma);
seq_printf(s, - "%10d %10d %4o %10u %5u %5u %5u %5u %10lu %10lu\n", + "%10d %10d %4o %10u %5u %5u %5u %5u %10llu %10llu\n", sma->sem_perm.key, sma->sem_perm.id, sma->sem_perm.mode, diff --git a/ipc/shm.c b/ipc/shm.c index 6d767071c367..d6272640c148 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -192,7 +192,7 @@ static void shm_open(struct vm_area_struct *vma)
shp = shm_lock(sfd->ns, sfd->id); BUG_ON(IS_ERR(shp)); - shp->shm_atim = get_seconds(); + shp->shm_atim = ktime_get_real_seconds(); shp->shm_lprid = task_tgid_vnr(current); shp->shm_nattch++; shm_unlock(shp); @@ -260,7 +260,7 @@ static void shm_close(struct vm_area_struct *vma) shp = shm_lock(ns, sfd->id); BUG_ON(IS_ERR(shp)); shp->shm_lprid = task_tgid_vnr(current); - shp->shm_dtim = get_seconds(); + shp->shm_dtim = ktime_get_real_seconds(); shp->shm_nattch--; if (shm_may_destroy(ns, shp)) shm_destroy(ns, shp); @@ -559,7 +559,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) shp->shm_cprid = task_tgid_vnr(current); shp->shm_lprid = 0; shp->shm_atim = shp->shm_dtim = 0; - shp->shm_ctim = get_seconds(); + shp->shm_ctim = ktime_get_real_seconds(); shp->shm_segsz = size; shp->shm_nattch = 0; shp->shm_file = file; @@ -813,7 +813,7 @@ static int shmctl_down(struct ipc_namespace *ns, int shmid, int cmd, err = ipc_update_perm(&shmid64.shm_perm, ipcp); if (err) goto out_unlock0; - shp->shm_ctim = get_seconds(); + shp->shm_ctim = ktime_get_real_seconds(); break; default: err = -EINVAL; @@ -922,6 +922,11 @@ static int shmctl_nolock(struct ipc_namespace *ns, int shmid, tbuf.shm_atime = shp->shm_atim; tbuf.shm_dtime = shp->shm_dtim; tbuf.shm_ctime = shp->shm_ctim; +#ifndef CONFIG_64BIT + tbuf.shm_atime_high = shp->shm_atim >> 32; + tbuf.shm_dtime_high = shp->shm_dtim >> 32; + tbuf.shm_ctime_high = shp->shm_ctim >> 32; +#endif tbuf.shm_cpid = shp->shm_cprid; tbuf.shm_lpid = shp->shm_lprid; tbuf.shm_nattch = shp->shm_nattch; @@ -1344,7 +1349,7 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it)
seq_printf(s, "%10d %10d %4o " SIZE_SPEC " %5u %5u " - "%5lu %5u %5u %5u %5u %10lu %10lu %10lu " + "%5lu %5u %5u %5u %5u %10llu %10llu %10llu " SIZE_SPEC " " SIZE_SPEC "\n", shp->shm_perm.key, shp->shm_perm.id, @@ -1357,9 +1362,9 @@ static int sysvipc_shm_proc_show(struct seq_file *s, void *it) from_kgid_munged(user_ns, shp->shm_perm.gid), from_kuid_munged(user_ns, shp->shm_perm.cuid), from_kgid_munged(user_ns, shp->shm_perm.cgid), - shp->shm_atim, - shp->shm_dtim, - shp->shm_ctim, + (u64)shp->shm_atim, + (u64)shp->shm_dtim, + (u64)shp->shm_ctim, rss * PAGE_SIZE, swp * PAGE_SIZE);
As we change the user space type for the timerfd and posix timer functions to __kernel_itimerspec, we need some form of conversion helpers to avoid duplicating that logic.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/posix-timers.h | 5 +++++ kernel/time/posix-timers.c | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+)
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index 907f3fd191ac..bc44076f7e0b 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -137,5 +137,10 @@ void set_process_cpu_timer(struct task_struct *task, unsigned int clock_idx, long clock_nanosleep_restart(struct restart_block *restart_block);
void update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new); +int get_itimerspec(struct itimerspec *it, + const struct __kernel_itimerspec __user *uit); +int put_itimerspec(const struct itimerspec *it, + struct __kernel_itimerspec __user *uit); +
#endif diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index 31ea01f42e1f..c99b10725025 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -794,6 +794,40 @@ SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id, return ret; }
+int get_itimerspec(struct itimerspec *it, const struct __kernel_itimerspec __user *uit) +{ + int ret; + struct timespec64 ts; + + ret = get_timespec64(&ts, &uit->it_interval); + if (ret) + return ret; + it->it_interval = timespec64_to_timespec(ts); + + ret = get_timespec64(&ts, &uit->it_value); + if (ret) + return ret; + it->it_value = timespec64_to_timespec(ts); + + return ret; +} + +int put_itimerspec(const struct itimerspec *it, struct __kernel_itimerspec __user *uit) +{ + int ret; + struct timespec64 ts; + + ts = timespec_to_timespec64(it->it_interval); + ret = put_timespec64(&ts, &uit->it_interval); + if (ret) + return ret; + + ts = timespec_to_timespec64(it->it_value); + ret = put_timespec64(&ts, &uit->it_value); + + return ret; +} + /* * Get the number of overruns of a POSIX.1b interval timer. This is to * be the overrun of the timer last delivered. At the same time we are
32-bit architectures that want to use a 64-bit time_t need a definition of timerfd_gettime and timerfd_settime that takes __kernel_itimerspec arguments, and use the compat_time syscalls for the legacy support.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- fs/timerfd.c | 13 +++++++------ include/linux/syscalls.h | 6 +++--- 2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/fs/timerfd.c b/fs/timerfd.c index b94fa6c3c6eb..4e80fc2d6c32 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -11,6 +11,7 @@ #include <linux/alarmtimer.h> #include <linux/file.h> #include <linux/poll.h> +#include <linux/posix-timers.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/sched.h> @@ -512,30 +513,30 @@ static int do_timerfd_gettime(int ufd, struct itimerspec *t) }
SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags, - const struct itimerspec __user *, utmr, - struct itimerspec __user *, otmr) + const struct __kernel_itimerspec __user *, utmr, + struct __kernel_itimerspec __user *, otmr) { struct itimerspec new, old; int ret;
- if (copy_from_user(&new, utmr, sizeof(new))) + if (get_itimerspec(&new, utmr)) return -EFAULT; ret = do_timerfd_settime(ufd, flags, &new, &old); if (ret) return ret; - if (otmr && copy_to_user(otmr, &old, sizeof(old))) + if (otmr && put_itimerspec(&old, otmr)) return -EFAULT;
return ret; }
-SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr) +SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct __kernel_itimerspec __user *, otmr) { struct itimerspec kotmr; int ret = do_timerfd_gettime(ufd, &kotmr); if (ret) return ret; - return copy_to_user(otmr, &kotmr, sizeof(kotmr)) ? -EFAULT: 0; + return put_itimerspec(&kotmr, otmr); }
#ifdef CONFIG_COMPAT diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index ad196d7b9eba..fec7a2cfb1a4 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -802,9 +802,9 @@ asmlinkage long sys_signalfd(int ufd, sigset_t __user *user_mask, size_t sizemas asmlinkage long sys_signalfd4(int ufd, sigset_t __user *user_mask, size_t sizemask, int flags); asmlinkage long sys_timerfd_create(int clockid, int flags); asmlinkage long sys_timerfd_settime(int ufd, int flags, - const struct itimerspec __user *utmr, - struct itimerspec __user *otmr); -asmlinkage long sys_timerfd_gettime(int ufd, struct itimerspec __user *otmr); + const struct __kernel_itimerspec __user *utmr, + struct __kernel_itimerspec __user *otmr); +asmlinkage long sys_timerfd_gettime(int ufd, struct __kernel_itimerspec __user *otmr); asmlinkage long sys_eventfd(unsigned int count); asmlinkage long sys_eventfd2(unsigned int count, int flags); asmlinkage long sys_memfd_create(const char __user *uname_ptr, unsigned int flags);
As 32-bit architectures now have two variants of pselect6, one using 32-bit time_t and one using 64-bit time_t, we unfortunately also need to add a second compat case for the version of pselect6 that now uses a 64-bit time_t but the 32-bit format for fd_set and sigset, which is different from what a 64-bit kernel uses natively.
This is complicated by the way that poll_select_copy_remaining() works, which already copies back the remaining time as either timespec or timeval, depending on whether it is used by select or pselect6. We extend it in a similar fashion to also copy back a __kernel_timespec.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- fs/compat.c | 62 ++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 19 deletions(-)
diff --git a/fs/compat.c b/fs/compat.c index fac162df4b9b..e4fffe221d37 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -1075,7 +1075,7 @@ COMPAT_SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, fla #define __COMPAT_NFDBITS (8 * sizeof(compat_ulong_t))
static int poll_select_copy_remaining(struct timespec64 *end_time, void __user *p, - int timeval, int ret) + int timeval, int compat_time, int ret) { struct timespec64 ts;
@@ -1102,14 +1102,10 @@ static int poll_select_copy_remaining(struct timespec64 *end_time, void __user *
if (!copy_to_user(p, &rtv, sizeof(rtv))) return ret; + } else if (compat_time) { + return compat_put_timespec64(end_time, p); } else { - struct compat_timespec rts; - - rts.tv_sec = ts.tv_sec; - rts.tv_nsec = ts.tv_nsec; - - if (!copy_to_user(p, &rts, sizeof(rts))) - return ret; + return put_timespec64(end_time, p); } /* * If an application puts its timeval in read-only memory, we @@ -1297,7 +1293,7 @@ COMPAT_SYSCALL_DEFINE5(select, int, n, compat_ulong_t __user *, inp, }
ret = compat_core_sys_select(n, inp, outp, exp, to); - ret = poll_select_copy_remaining(&end_time, tvp, 1, ret); + ret = poll_select_copy_remaining(&end_time, tvp, 1, 1, ret);
return ret; } @@ -1340,20 +1336,26 @@ int compat_copy_sigset_from_user(sigset_t *out, const compat_sigset_t __user *in #endif return 0; } +#endif
static long do_compat_pselect(int n, compat_ulong_t __user *inp, compat_ulong_t __user *outp, compat_ulong_t __user *exp, - struct compat_timespec __user *tsp, compat_sigset_t __user *sigmask, - compat_size_t sigsetsize) + void __user *tsp, compat_sigset_t __user *sigmask, + compat_size_t sigsetsize, int compat_time) { sigset_t ksigmask, sigsaved; - struct compat_timespec ts; + struct timespec64 ts; struct timespec64 end_time, *to = NULL; int ret;
if (tsp) { - if (copy_from_user(&ts, tsp, sizeof(ts))) - return -EFAULT; + if (compat_time) { + if (compat_get_timespec64(&ts, tsp)) + return -EFAULT; + } else { + if (get_timespec64(&ts, tsp)) + return -EFAULT; + }
to = &end_time; if (poll_select_set_timeout(to, ts.tv_sec, ts.tv_nsec)) @@ -1369,7 +1371,7 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp, }
ret = compat_core_sys_select(n, inp, outp, exp, to); - ret = poll_select_copy_remaining(&end_time, tsp, 0, ret); + ret = poll_select_copy_remaining(&end_time, tsp, 0, compat_time, ret);
if (ret == -ERESTARTNOHAND) { /* @@ -1388,6 +1390,7 @@ static long do_compat_pselect(int n, compat_ulong_t __user *inp, return ret; }
+#ifdef CONFIG_COMPAT_TIME COMPAT_SYSCALL_DEFINE6(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) @@ -1398,13 +1401,13 @@ COMPAT_SYSCALL_DEFINE6(pselect6, int, n, compat_ulong_t __user *, inp, if (sig) { if (!access_ok(VERIFY_READ, sig, sizeof(compat_uptr_t)+sizeof(compat_size_t)) || - __get_user(up, (compat_uptr_t __user *)sig) || - __get_user(sigsetsize, + __get_user(up, (compat_uptr_t __user *)sig) || + __get_user(sigsetsize, (compat_size_t __user *)(sig+sizeof(up)))) return -EFAULT; } return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(up), - sigsetsize); + sigsetsize, 1); }
COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, @@ -1451,12 +1454,33 @@ COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, } else if (sigmask) sigprocmask(SIG_SETMASK, &sigsaved, NULL);
- ret = poll_select_copy_remaining(&end_time, tsp, 0, ret); + ret = poll_select_copy_remaining(&end_time, tsp, 0, 1, ret);
return ret; } #endif
+#ifdef CONFIG_COMPAT +COMPAT_SYSCALL_DEFINE6(pselect6_time64, int, n, compat_ulong_t __user *, inp, + compat_ulong_t __user *, outp, compat_ulong_t __user *, exp, + struct __kernel_timespec __user *, tsp, void __user *, sig) +{ + compat_size_t sigsetsize = 0; + compat_uptr_t up = 0; + + if (sig) { + if (!access_ok(VERIFY_READ, sig, + sizeof(compat_uptr_t)+sizeof(compat_size_t)) || + __get_user(up, (compat_uptr_t __user *)sig) || + __get_user(sigsetsize, + (compat_size_t __user *)(sig+sizeof(up)))) + return -EFAULT; + } + return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(up), + sigsetsize, 0); +} +#endif + #if defined(CONFIG_FHANDLE) && defined(CONFIG_COMPAT) /* * Exactly like fs/open.c:sys_open_by_handle_at(), except that it
Following directly what we did for sys_pselect6 compat_time handling, this adds the same method for sys_ppoll.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- fs/compat.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-)
diff --git a/fs/compat.c b/fs/compat.c index e4fffe221d37..3e7ccc6e3503 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -1409,19 +1409,26 @@ COMPAT_SYSCALL_DEFINE6(pselect6, int, n, compat_ulong_t __user *, inp, return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(up), sigsetsize, 1); } +#endif
-COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, - unsigned int, nfds, struct compat_timespec __user *, tsp, - const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize) +static int do_compat_ppoll(struct pollfd __user *ufds, + unsigned int nfds, void __user *tsp, + const compat_sigset_t __user * sigmask, compat_size_t sigsetsize, + int compat_time) { sigset_t ksigmask, sigsaved; - struct compat_timespec ts; + struct timespec64 ts; struct timespec64 end_time, *to = NULL; int ret;
if (tsp) { - if (copy_from_user(&ts, tsp, sizeof(ts))) - return -EFAULT; + if (compat_time) { + if (compat_get_timespec64(&ts, tsp)) + return -EFAULT; + } else { + if (get_timespec64(&ts, tsp)) + return -EFAULT; + }
to = &end_time; if (poll_select_set_timeout(to, ts.tv_sec, ts.tv_nsec)) @@ -1454,10 +1461,18 @@ COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, } else if (sigmask) sigprocmask(SIG_SETMASK, &sigsaved, NULL);
- ret = poll_select_copy_remaining(&end_time, tsp, 0, 1, ret); + ret = poll_select_copy_remaining(&end_time, tsp, 0, compat_time, ret);
return ret; } + +#ifdef CONFIG_COMPAT_TIME +COMPAT_SYSCALL_DEFINE5(ppoll, struct pollfd __user *, ufds, + unsigned int, nfds, struct compat_timespec __user *, tsp, + const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize) +{ + return do_compat_ppoll(ufds, nfds, tsp, sigmask, sigsetsize, 1); +} #endif
#ifdef CONFIG_COMPAT @@ -1479,6 +1494,14 @@ COMPAT_SYSCALL_DEFINE6(pselect6_time64, int, n, compat_ulong_t __user *, inp, return do_compat_pselect(n, inp, outp, exp, tsp, compat_ptr(up), sigsetsize, 0); } + +COMPAT_SYSCALL_DEFINE5(ppoll_time64, struct pollfd __user *, ufds, + unsigned int, nfds, struct compat_timespec __user *, tsp, + const compat_sigset_t __user *, sigmask, compat_size_t, sigsetsize) +{ + return do_compat_ppoll(ufds, nfds, tsp, sigmask, sigsetsize, 0); +} + #endif
#if defined(CONFIG_FHANDLE) && defined(CONFIG_COMPAT)
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 fec7a2cfb1a4..19119800b93c 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;
As the 32-bit architectures move these system calls over to 64-bit time_t, we have to provide backwards-compatibility support.
We could use the existing compat_sys_mq_timed{send,receive} functions from ipc/compat_mq.c, but their use of compat_convert_timespec() means that we would have to implement compat_alloc_userspace() for all architectures, which is rather tricky.
As an alternative approach, this splits up the normal sys_mq_timed{send,receive} functions into the main part and the actual system call entry, and then reuses the main part from the compat handler, which has the added benefit of being slightly more efficent for compat than the existing approach, as we avoid copying the time data to the kernel twice.
We could probably share a little mode code here, but this patch for now avoids doing too many changes at once.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/compat_time.h | 2 +- ipc/compat_mq.c | 28 ---------- ipc/mqueue.c | 128 ++++++++++++++++++++++++++++++++++---------- 3 files changed, 101 insertions(+), 57 deletions(-)
diff --git a/include/linux/compat_time.h b/include/linux/compat_time.h index 6d129a7f3843..37c3860d97fa 100644 --- a/include/linux/compat_time.h +++ b/include/linux/compat_time.h @@ -285,7 +285,7 @@ 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, +asmlinkage long 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); diff --git a/ipc/compat_mq.c b/ipc/compat_mq.c index ef6f91cc4490..19d3c3f3ab0d 100644 --- a/ipc/compat_mq.c +++ b/ipc/compat_mq.c @@ -64,34 +64,6 @@ COMPAT_SYSCALL_DEFINE4(mq_open, const char __user *, u_name, return sys_mq_open(u_name, oflag, mode, p); }
-COMPAT_SYSCALL_DEFINE5(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) -{ - struct timespec __user *u_ts; - - if (compat_convert_timespec(&u_ts, u_abs_timeout)) - return -EFAULT; - - return sys_mq_timedsend(mqdes, u_msg_ptr, msg_len, - msg_prio, u_ts); -} - -COMPAT_SYSCALL_DEFINE5(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) -{ - struct timespec __user *u_ts; - - if (compat_convert_timespec(&u_ts, u_abs_timeout)) - return -EFAULT; - - return sys_mq_timedreceive(mqdes, u_msg_ptr, msg_len, - u_msg_prio, u_ts); -} - COMPAT_SYSCALL_DEFINE2(mq_notify, mqd_t, mqdes, const struct compat_sigevent __user *, u_notification) { diff --git a/ipc/mqueue.c b/ipc/mqueue.c index f3f27ad1ca44..0fd4d5b9a959 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c @@ -677,7 +677,19 @@ static int prepare_timeout(const struct __kernel_timespec __user *u_abs_timeout, { if (get_timespec64(ts, u_abs_timeout)) return -EFAULT; - if (!timespec_valid(ts)) + if (!timespec64_valid(ts)) + return -EINVAL; + + *expires = timespec64_to_ktime(*ts); + return 0; +} + +static int compat_prepare_timeout(const struct compat_timespec __user *u_abs_timeout, + ktime_t *expires, struct timespec64 *ts) +{ + if (compat_get_timespec64(ts, u_abs_timeout)) + return -EFAULT; + if (!timespec64_valid(ts)) return -EINVAL;
*expires = timespec64_to_ktime(*ts); @@ -951,9 +963,9 @@ static inline void pipelined_receive(struct mqueue_inode_info *info) sender->state = STATE_READY; }
-SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, - size_t, msg_len, unsigned int, msg_prio, - const struct __kernel_timespec __user *, u_abs_timeout) +static long mq_timedsend(mqd_t mqdes, const char __user * u_msg_ptr, + size_t msg_len, unsigned int msg_prio, + ktime_t *timeout) { struct fd f; struct inode *inode; @@ -961,23 +973,9 @@ SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, struct ext_wait_queue *receiver; struct msg_msg *msg_ptr; struct mqueue_inode_info *info; - ktime_t expires, *timeout = NULL; - struct timespec64 ts; struct posix_msg_tree_node *new_leaf = NULL; int ret = 0;
- if (u_abs_timeout) { - int res = prepare_timeout(u_abs_timeout, &expires, &ts); - if (res) - return res; - timeout = &expires; - } - - if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX)) - return -EINVAL; - - audit_mq_sendrecv(mqdes, msg_len, msg_prio, timeout ? &ts : NULL); - f = fdget(mqdes); if (unlikely(!f.file)) { ret = -EBADF; @@ -1071,19 +1069,12 @@ out: return ret; }
-SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr, - size_t, msg_len, unsigned int __user *, u_msg_prio, +SYSCALL_DEFINE5(mq_timedsend, mqd_t, mqdes, const char __user *, u_msg_ptr, + size_t, msg_len, unsigned int, msg_prio, const struct __kernel_timespec __user *, u_abs_timeout) { - ssize_t ret; - struct msg_msg *msg_ptr; - struct fd f; - struct inode *inode; - struct mqueue_inode_info *info; - struct ext_wait_queue wait; ktime_t expires, *timeout = NULL; struct timespec64 ts; - struct posix_msg_tree_node *new_leaf = NULL;
if (u_abs_timeout) { int res = prepare_timeout(u_abs_timeout, &expires, &ts); @@ -1092,7 +1083,49 @@ SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr, timeout = &expires; }
- audit_mq_sendrecv(mqdes, msg_len, 0, timeout ? &ts : NULL); + if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX)) + return -EINVAL; + + audit_mq_sendrecv(mqdes, msg_len, msg_prio, timeout ? &ts : NULL); + + return mq_timedsend(mqdes, u_msg_ptr, msg_len, msg_prio, timeout); +} + +#ifdef CONFIG_COMPAT_TIME +COMPAT_SYSCALL_DEFINE5(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) +{ + ktime_t expires, *timeout = NULL; + struct timespec64 ts; + + if (u_abs_timeout) { + int res = compat_prepare_timeout(u_abs_timeout, &expires, &ts); + if (res) + return res; + timeout = &expires; + } + + if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX)) + return -EINVAL; + + audit_mq_sendrecv(mqdes, msg_len, msg_prio, timeout ? &ts : NULL); + + return mq_timedsend(mqdes, u_msg_ptr, msg_len, msg_prio, timeout); +} +#endif + +static int mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr, + size_t msg_len, unsigned int __user *u_msg_prio, + ktime_t *timeout) +{ + ssize_t ret; + struct msg_msg *msg_ptr; + struct fd f; + struct inode *inode; + struct mqueue_inode_info *info; + struct ext_wait_queue wait; + struct posix_msg_tree_node *new_leaf = NULL;
f = fdget(mqdes); if (unlikely(!f.file)) { @@ -1174,6 +1207,45 @@ out: return ret; }
+SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr, + size_t, msg_len, unsigned int __user *, u_msg_prio, + const struct __kernel_timespec __user *, u_abs_timeout) +{ + ktime_t expires, *timeout = NULL; + struct timespec64 ts; + + if (u_abs_timeout) { + int res = prepare_timeout(u_abs_timeout, &expires, &ts); + if (res) + return res; + timeout = &expires; + } + + audit_mq_sendrecv(mqdes, msg_len, 0, timeout ? &ts : NULL); + + return mq_timedreceive(mqdes, u_msg_ptr, msg_len, u_msg_prio, timeout); +} + +#ifdef CONFIG_COMPAT_TIME +COMPAT_SYSCALL_DEFINE5(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) +{ + ktime_t expires, *timeout = NULL; + struct timespec64 ts; + + if (u_abs_timeout) { + int res = compat_prepare_timeout(u_abs_timeout, &expires, &ts); + if (res) + return res; + timeout = &expires; + } + + audit_mq_sendrecv(mqdes, msg_len, 0, timeout ? &ts : NULL); + + return mq_timedreceive(mqdes, u_msg_ptr, msg_len, u_msg_prio, timeout); +} +#endif /* * Notes: the case when user wants us to deregister (with NULL as pointer) * and he isn't currently owner of notification, will be silently discarded.
This changes the utimensat() system call to use __kernel_timespec instead of timespec, to allow 32-bit architectures the use of 64-bit time_t in user space, with the compat syscalls providing backwards compatibility support for binaries using utime, utimes, or futimesat.
New libc versions would implement all four system call through the sys_utimensat() kernel interface, and provide backwards compatility for existing source code in user space.
This change is not sufficient to set times beyond 2038 in file systems by itself, a larger follow-up is required to change the timestamp format in inodes to use timespec64, and change all file systems to use that correctly.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- arch/alpha/kernel/osf_sys.c | 2 +- fs/compat.c | 10 +++++----- fs/utimes.c | 19 ++++++++++--------- include/linux/syscalls.h | 2 +- include/linux/time.h | 2 +- init/initramfs.c | 2 +- 6 files changed, 19 insertions(+), 18 deletions(-)
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 6824e68d32ad..04368e8129b7 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -1068,7 +1068,7 @@ SYSCALL_DEFINE3(osf_setitimer, int, which, struct itimerval32 __user *, in, SYSCALL_DEFINE2(osf_utimes, const char __user *, filename, struct timeval32 __user *, tvs) { - struct timespec tv[2]; + struct timespec64 tv[2];
if (tvs) { struct timeval ktvs[2]; diff --git a/fs/compat.c b/fs/compat.c index 3e7ccc6e3503..3f110ebe9c3b 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -62,7 +62,7 @@ COMPAT_SYSCALL_DEFINE2(utime, const char __user *, filename, struct compat_utimbuf __user *, t) { - struct timespec tv[2]; + struct timespec64 tv[2];
if (t) { if (get_user(tv[0].tv_sec, &t->actime) || @@ -76,11 +76,11 @@ COMPAT_SYSCALL_DEFINE2(utime, const char __user *, filename,
COMPAT_SYSCALL_DEFINE4(utimensat, unsigned int, dfd, const char __user *, filename, struct compat_timespec __user *, t, int, flags) { - struct timespec tv[2]; + struct timespec64 tv[2];
if (t) { - if (compat_get_timespec(&tv[0], &t[0]) || - compat_get_timespec(&tv[1], &t[1])) + if (compat_get_timespec64(&tv[0], &t[0]) || + compat_get_timespec64(&tv[1], &t[1])) return -EFAULT;
if (tv[0].tv_nsec == UTIME_OMIT && tv[1].tv_nsec == UTIME_OMIT) @@ -91,7 +91,7 @@ COMPAT_SYSCALL_DEFINE4(utimensat, unsigned int, dfd, const char __user *, filena
COMPAT_SYSCALL_DEFINE3(futimesat, unsigned int, dfd, const char __user *, filename, struct compat_timeval __user *, t) { - struct timespec tv[2]; + struct timespec64 tv[2];
if (t) { if (get_user(tv[0].tv_sec, &t[0].tv_sec) || diff --git a/fs/utimes.c b/fs/utimes.c index aa138d64560a..a269b5be2418 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -26,7 +26,7 @@ */ SYSCALL_DEFINE2(utime, char __user *, filename, struct utimbuf __user *, times) { - struct timespec tv[2]; + struct timespec64 tv[2];
if (times) { if (get_user(tv[0].tv_sec, ×->actime) || @@ -48,7 +48,7 @@ static bool nsec_valid(long nsec) return nsec >= 0 && nsec <= 999999999; }
-static int utimes_common(struct path *path, struct timespec *times) +static int utimes_common(struct path *path, struct timespec64 *times) { int error; struct iattr newattrs; @@ -68,7 +68,7 @@ static int utimes_common(struct path *path, struct timespec *times) if (times[0].tv_nsec == UTIME_OMIT) newattrs.ia_valid &= ~ATTR_ATIME; else if (times[0].tv_nsec != UTIME_NOW) { - newattrs.ia_atime.tv_sec = times[0].tv_sec; + newattrs.ia_atime.tv_sec = (time_t)times[0].tv_sec; newattrs.ia_atime.tv_nsec = times[0].tv_nsec; newattrs.ia_valid |= ATTR_ATIME_SET; } @@ -76,7 +76,7 @@ static int utimes_common(struct path *path, struct timespec *times) if (times[1].tv_nsec == UTIME_OMIT) newattrs.ia_valid &= ~ATTR_MTIME; else if (times[1].tv_nsec != UTIME_NOW) { - newattrs.ia_mtime.tv_sec = times[1].tv_sec; + newattrs.ia_mtime.tv_sec = (time_t)times[1].tv_sec; newattrs.ia_mtime.tv_nsec = times[1].tv_nsec; newattrs.ia_valid |= ATTR_MTIME_SET; } @@ -133,7 +133,7 @@ out: * must be owner or have write permission. * Else, update from *times, must be owner or super user. */ -long do_utimes(int dfd, const char __user *filename, struct timespec *times, +long do_utimes(int dfd, const char __user *filename, struct timespec64 *times, int flags) { int error = -EINVAL; @@ -183,12 +183,13 @@ out: }
SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename, - struct timespec __user *, utimes, int, flags) + struct __kernel_timespec __user *, utimes, int, flags) { - struct timespec tstimes[2]; + struct timespec64 tstimes[2];
if (utimes) { - if (copy_from_user(&tstimes, utimes, sizeof(tstimes))) + if (get_timespec64(&tstimes[0], &utimes[0]) || + get_timespec64(&tstimes[1], &utimes[1])) return -EFAULT;
/* Nothing to do, we must not even check the path. */ @@ -204,7 +205,7 @@ SYSCALL_DEFINE3(futimesat, int, dfd, const char __user *, filename, struct timeval __user *, utimes) { struct timeval times[2]; - struct timespec tstimes[2]; + struct timespec64 tstimes[2];
if (utimes) { if (copy_from_user(×, utimes, sizeof(times))) diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 19119800b93c..756ead542240 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -776,7 +776,7 @@ asmlinkage long sys_newfstatat(int dfd, const char __user *filename, 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, - struct timespec __user *utimes, int flags); + struct __kernel_timespec __user *utimes, int flags); asmlinkage long sys_unshare(unsigned long unshare_flags);
asmlinkage long sys_splice(int fd_in, loff_t __user *off_in, diff --git a/include/linux/time.h b/include/linux/time.h index 5b5a64952df7..b29c49196026 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -139,7 +139,7 @@ extern int do_getitimer(int which, struct itimerval *value);
extern unsigned int alarm_setitimer(unsigned int seconds);
-extern long do_utimes(int dfd, const char __user *filename, struct timespec *times, int flags); +extern long do_utimes(int dfd, const char __user *filename, struct timespec64 *times, int flags);
struct tms; extern void do_sys_times(struct tms *); diff --git a/init/initramfs.c b/init/initramfs.c index 10c808e97023..62f8807b8ce3 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -109,7 +109,7 @@ static void __init free_hash(void)
static long __init do_utime(char *filename, time_t mtime) { - struct timespec t[2]; + struct timespec64 t[2];
t[0].tv_sec = mtime; t[0].tv_nsec = 0;
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 82933be3e9d9..fe315f114b89 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)
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 756ead542240..cfa2f7d91c1e 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 ad428b433d02..0a2b7c308be8 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -1049,7 +1049,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;
@@ -1061,7 +1061,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; }
@@ -1083,7 +1083,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;
@@ -1093,7 +1093,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; }
@@ -1115,7 +1115,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) {
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 cfa2f7d91c1e..bde543571730 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;
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 fe315f114b89..4be28cec4c20 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
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 bde543571730..c7b1ea2e421a 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 0a2b7c308be8..13653247ba6c 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -1256,27 +1256,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 f9123a82cbb6..9ffe7c23be2d 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> @@ -4483,15 +4485,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; @@ -4512,16 +4512,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)
As part of the work to make these system calls handle 64-bit time_t, we move the compat handling closer to the actual system calls. The next step is to combine the actual implementation.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/compat_time.h | 4 + include/linux/posix-timers.h | 2 - kernel/compat.c | 177 +------------------------------------------ kernel/time/posix-timers.c | 174 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 179 insertions(+), 178 deletions(-)
diff --git a/include/linux/compat_time.h b/include/linux/compat_time.h index 37c3860d97fa..afaa1ca7986d 100644 --- a/include/linux/compat_time.h +++ b/include/linux/compat_time.h @@ -120,6 +120,10 @@ 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); +struct compat_timex; +struct timex; +extern int compat_get_timex(struct timex *txc, struct compat_timex __user *utp); +extern int compat_put_timex(struct compat_timex __user *utp, struct timex *txc);
/* * This function convert a timespec if necessary and returns a *user diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index bc44076f7e0b..2d676be022e4 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -134,8 +134,6 @@ bool posix_cpu_timers_can_stop_tick(struct task_struct *tsk); void set_process_cpu_timer(struct task_struct *task, unsigned int clock_idx, cputime_t *newval, cputime_t *oldval);
-long clock_nanosleep_restart(struct restart_block *restart_block); - void update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new); int get_itimerspec(struct itimerspec *it, const struct __kernel_itimerspec __user *uit); diff --git a/kernel/compat.c b/kernel/compat.c index 13653247ba6c..22a20bd37a73 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -33,7 +33,7 @@ #include <asm/uaccess.h>
#ifdef CONFIG_COMPAT_TIME -static int compat_get_timex(struct timex *txc, struct compat_timex __user *utp) +int compat_get_timex(struct timex *txc, struct compat_timex __user *utp) { memset(txc, 0, sizeof(struct timex));
@@ -63,7 +63,7 @@ static int compat_get_timex(struct timex *txc, struct compat_timex __user *utp) return 0; }
-static int compat_put_timex(struct compat_timex __user *utp, struct timex *txc) +int compat_put_timex(struct compat_timex __user *utp, struct timex *txc) { if (!access_ok(VERIFY_WRITE, utp, sizeof(struct compat_timex)) || __put_user(txc->modes, &utp->modes) || @@ -743,180 +743,7 @@ 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) -{ - long err; - mm_segment_t oldfs; - struct itimerspec newts, oldts; - - if (!new) - return -EINVAL; - if (get_compat_itimerspec(&newts, new)) - return -EFAULT; - oldfs = get_fs(); - set_fs(KERNEL_DS); - err = sys_timer_settime(timer_id, flags, - (struct itimerspec __user *) &newts, - (struct itimerspec __user *) &oldts); - set_fs(oldfs); - if (!err && old && put_compat_itimerspec(old, &oldts)) - return -EFAULT; - return err; -} - -COMPAT_SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id, - struct compat_itimerspec __user *, setting) -{ - long err; - mm_segment_t oldfs; - struct itimerspec ts; - - oldfs = get_fs(); - set_fs(KERNEL_DS); - err = sys_timer_gettime(timer_id, - (struct itimerspec __user *) &ts); - set_fs(oldfs); - if (!err && put_compat_itimerspec(setting, &ts)) - return -EFAULT; - return err; -} - -COMPAT_SYSCALL_DEFINE2(clock_settime, clockid_t, which_clock, - struct compat_timespec __user *, tp) -{ - long err; - mm_segment_t oldfs; - struct timespec ts;
- if (compat_get_timespec(&ts, tp)) - return -EFAULT; - oldfs = get_fs(); - set_fs(KERNEL_DS); - err = sys_clock_settime(which_clock, - (struct timespec __user *) &ts); - set_fs(oldfs); - return err; -} - -COMPAT_SYSCALL_DEFINE2(clock_gettime, clockid_t, which_clock, - struct compat_timespec __user *, tp) -{ - long err; - mm_segment_t oldfs; - struct timespec ts; - - oldfs = get_fs(); - set_fs(KERNEL_DS); - err = sys_clock_gettime(which_clock, - (struct timespec __user *) &ts); - set_fs(oldfs); - if (!err && compat_put_timespec(&ts, tp)) - return -EFAULT; - return err; -} - -COMPAT_SYSCALL_DEFINE2(clock_adjtime, clockid_t, which_clock, - struct compat_timex __user *, utp) -{ - struct timex txc; - mm_segment_t oldfs; - int err, ret; - - err = compat_get_timex(&txc, utp); - if (err) - return err; - - oldfs = get_fs(); - set_fs(KERNEL_DS); - ret = sys_clock_adjtime(which_clock, (struct timex __user *) &txc); - set_fs(oldfs); - - err = compat_put_timex(utp, &txc); - if (err) - return err; - - return ret; -} - -COMPAT_SYSCALL_DEFINE2(clock_getres, clockid_t, which_clock, - struct compat_timespec __user *, tp) -{ - long err; - mm_segment_t oldfs; - struct timespec ts; - - oldfs = get_fs(); - set_fs(KERNEL_DS); - err = sys_clock_getres(which_clock, - (struct timespec __user *) &ts); - set_fs(oldfs); - if (!err && tp && compat_put_timespec(&ts, tp)) - return -EFAULT; - return err; -} - -static long compat_clock_nanosleep_restart(struct restart_block *restart) -{ - long err; - mm_segment_t oldfs; - struct timespec tu; - struct compat_timespec __user *rmtp = restart->nanosleep.compat_rmtp; - - restart->nanosleep.rmtp = (struct timespec __user *) &tu; - oldfs = get_fs(); - set_fs(KERNEL_DS); - err = clock_nanosleep_restart(restart); - set_fs(oldfs); - - if ((err == -ERESTART_RESTARTBLOCK) && rmtp && - compat_put_timespec(&tu, rmtp)) - return -EFAULT; - - if (err == -ERESTART_RESTARTBLOCK) { - restart->fn = compat_clock_nanosleep_restart; - restart->nanosleep.compat_rmtp = rmtp; - } - return err; -} - -COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags, - struct compat_timespec __user *, rqtp, - struct compat_timespec __user *, rmtp) -{ - long err; - mm_segment_t oldfs; - struct timespec in, out; - struct restart_block *restart; - - if (compat_get_timespec(&in, rqtp)) - return -EFAULT; - - oldfs = get_fs(); - set_fs(KERNEL_DS); - err = sys_clock_nanosleep(which_clock, flags, - (struct timespec __user *) &in, - (struct timespec __user *) &out); - set_fs(oldfs); - - if ((err == -ERESTART_RESTARTBLOCK) && rmtp && - compat_put_timespec(&out, rmtp)) - return -EFAULT; - - if (err == -ERESTART_RESTARTBLOCK) { - restart = ¤t->restart_block; - restart->fn = compat_clock_nanosleep_restart; - restart->nanosleep.compat_rmtp = rmtp; - } - 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 diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index c99b10725025..4c0d43c7c015 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -39,6 +39,7 @@ #include <asm/uaccess.h> #include <linux/list.h> #include <linux/init.h> +#include <linux/compat.h> #include <linux/compiler.h> #include <linux/hash.h> #include <linux/posix-clock.h> @@ -1146,7 +1147,7 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags, * This will restart clock_nanosleep. This is required only by * compat_clock_nanosleep_restart for now. */ -long clock_nanosleep_restart(struct restart_block *restart_block) +static long clock_nanosleep_restart(struct restart_block *restart_block) { clockid_t which_clock = restart_block->nanosleep.clockid; struct k_clock *kc = clockid_to_kclock(which_clock); @@ -1156,3 +1157,174 @@ long clock_nanosleep_restart(struct restart_block *restart_block)
return kc->nsleep_restart(restart_block); } + +#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) +{ + long err; + mm_segment_t oldfs; + struct itimerspec newts, oldts; + + if (!new) + return -EINVAL; + if (get_compat_itimerspec(&newts, new)) + return -EFAULT; + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = sys_timer_settime(timer_id, flags, + (struct itimerspec __user *) &newts, + (struct itimerspec __user *) &oldts); + set_fs(oldfs); + if (!err && old && put_compat_itimerspec(old, &oldts)) + return -EFAULT; + return err; +} + +COMPAT_SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id, + struct compat_itimerspec __user *, setting) +{ + long err; + mm_segment_t oldfs; + struct itimerspec ts; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = sys_timer_gettime(timer_id, + (struct itimerspec __user *) &ts); + set_fs(oldfs); + if (!err && put_compat_itimerspec(setting, &ts)) + return -EFAULT; + return err; +} + +COMPAT_SYSCALL_DEFINE2(clock_settime, clockid_t, which_clock, + struct compat_timespec __user *, tp) +{ + long err; + mm_segment_t oldfs; + struct timespec ts; + + if (compat_get_timespec(&ts, tp)) + return -EFAULT; + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = sys_clock_settime(which_clock, + (struct timespec __user *) &ts); + set_fs(oldfs); + return err; +} + +COMPAT_SYSCALL_DEFINE2(clock_gettime, clockid_t, which_clock, + struct compat_timespec __user *, tp) +{ + long err; + mm_segment_t oldfs; + struct timespec ts; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = sys_clock_gettime(which_clock, + (struct timespec __user *) &ts); + set_fs(oldfs); + if (!err && compat_put_timespec(&ts, tp)) + return -EFAULT; + return err; +} + +COMPAT_SYSCALL_DEFINE2(clock_adjtime, clockid_t, which_clock, + struct compat_timex __user *, utp) +{ + struct timex txc; + mm_segment_t oldfs; + int err, ret; + + err = compat_get_timex(&txc, utp); + if (err) + return err; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_clock_adjtime(which_clock, (struct timex __user *) &txc); + set_fs(oldfs); + + err = compat_put_timex(utp, &txc); + if (err) + return err; + + return ret; +} + +COMPAT_SYSCALL_DEFINE2(clock_getres, clockid_t, which_clock, + struct compat_timespec __user *, tp) +{ + long err; + mm_segment_t oldfs; + struct timespec ts; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = sys_clock_getres(which_clock, + (struct timespec __user *) &ts); + set_fs(oldfs); + if (!err && tp && compat_put_timespec(&ts, tp)) + return -EFAULT; + return err; +} + +static long compat_clock_nanosleep_restart(struct restart_block *restart) +{ + long err; + mm_segment_t oldfs; + struct timespec tu; + struct compat_timespec __user *rmtp = restart->nanosleep.compat_rmtp; + + restart->nanosleep.rmtp = (struct timespec __user *) &tu; + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = clock_nanosleep_restart(restart); + set_fs(oldfs); + + if ((err == -ERESTART_RESTARTBLOCK) && rmtp && + compat_put_timespec(&tu, rmtp)) + return -EFAULT; + + if (err == -ERESTART_RESTARTBLOCK) { + restart->fn = compat_clock_nanosleep_restart; + restart->nanosleep.compat_rmtp = rmtp; + } + return err; +} + +COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags, + struct compat_timespec __user *, rqtp, + struct compat_timespec __user *, rmtp) +{ + long err; + mm_segment_t oldfs; + struct timespec in, out; + struct restart_block *restart; + + if (compat_get_timespec(&in, rqtp)) + return -EFAULT; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = sys_clock_nanosleep(which_clock, flags, + (struct timespec __user *) &in, + (struct timespec __user *) &out); + set_fs(oldfs); + + if ((err == -ERESTART_RESTARTBLOCK) && rmtp && + compat_put_timespec(&out, rmtp)) + return -EFAULT; + + if (err == -ERESTART_RESTARTBLOCK) { + restart = ¤t->restart_block; + restart->fn = compat_clock_nanosleep_restart; + restart->nanosleep.compat_rmtp = rmtp; + } + return err; +} +#endif
This is one of multiple possible extended definitions for timex, to allow future user space to keep working while defining time_t to 64-bit. The idea is that clock_adjtime() will take the new structure, which has an identical layout between 32-bit and 64-bit tasks, so we can use the compat_sys_clock_adjtime() function to always handle the old 32-bit layout, and use sys_clock_adjtime() to handle the new layout, without introducing an extra compat function.
Alternatives to this approach are:
- define a new __kernel_timex that only differs in the size of the timex->time member, so we can keep using the old definition in source form, but with the new timeval definition. This requires adding an extra system call on 64-bit machines, to handle all three layouts.
- use some of the padding words to store the upper half of the time_t. This allows us to keep using the existing system calls, but requires source-level changes in every user program to actually access the new data.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/uapi/linux/timex.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+)
diff --git a/include/uapi/linux/timex.h b/include/uapi/linux/timex.h index 92685d826444..80c429ecdf4e 100644 --- a/include/uapi/linux/timex.h +++ b/include/uapi/linux/timex.h @@ -92,6 +92,50 @@ struct timex { int :32; int :32; int :32; };
+ +/* + * __kernel_timex is the new structure that uses the same layout + * as timex on 64-bit machines + */ +struct __kernel_timex_timeval { + long long tv_sec; + long long tv_usec; +}; + +struct __kernel_timex { + unsigned int modes; /* mode selector */ + unsigned int :32; /* pad */ + long long offset; /* time offset (usec) */ + long long freq; /* frequency offset (scaled ppm) */ + long long maxerror; /* maximum error (usec) */ + long long esterror; /* estimated error (usec) */ + int status; /* clock command/status */ + unsigned int :32; /* pad */ + long long constant; /* pll time constant */ + long long precision; /* clock precision (usec) (read only) */ + long long tolerance; /* clock frequency tolerance (ppm) + * (read only) + */ + struct __kernel_timex_timeval time;/* (read only, except for ADJ_SETOFFSET) */ + long long tick; /* (modified) usecs between clock ticks */ + + long long ppsfreq; /* pps frequency (scaled ppm) (ro) */ + long long jitter; /* pps jitter (us) (ro) */ + int shift; /* interval duration (s) (shift) (ro) */ + unsigned int :32; /* pad */ + long long stabil; /* pps stability (scaled ppm) (ro) */ + long long jitcnt; /* jitter limit exceeded (ro) */ + long long calcnt; /* calibration intervals (ro) */ + long long errcnt; /* calibration errors (ro) */ + long long stbcnt; /* stability limit exceeded (ro) */ + + int tai; /* TAI offset (ro) */ + + int :32; int :32; int :32; int :32; + int :32; int :32; int :32; int :32; + int :32; int :32; int :32; +}; + /* * Mode codes (timex.mode) */
This is mostly a search&replace of all uses of timex in the kernel, to allow using the new definition. Architectures that do not set CONFIG_COMPAT_TIME will still see the old definition, and nothing changes for them.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- arch/alpha/kernel/osf_sys.c | 2 +- arch/s390/kernel/time.c | 2 +- drivers/ptp/ptp_clock.c | 2 +- include/linux/compat_time.h | 6 +++--- include/linux/posix-clock.h | 2 +- include/linux/posix-timers.h | 2 +- include/linux/syscalls.h | 6 +++--- include/linux/timex.h | 6 +++++- include/uapi/linux/timex.h | 3 ++- kernel/compat.c | 8 ++++---- kernel/time/ntp.c | 16 ++++++++-------- kernel/time/ntp_internal.h | 4 ++-- kernel/time/posix-clock.c | 2 +- kernel/time/posix-timers.c | 26 ++++++++++++++------------ kernel/time/time.c | 8 ++++---- kernel/time/timekeeping.c | 2 +- 16 files changed, 52 insertions(+), 45 deletions(-)
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 04368e8129b7..678ba1b531e4 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -1269,7 +1269,7 @@ struct timex32 {
SYSCALL_DEFINE1(old_adjtimex, struct timex32 __user *, txc_p) { - struct timex txc; + struct __kernel_timex txc; int ret;
/* copy relevant bits of struct timex. */ diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 170ddd2018b3..7941cc48a114 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -305,7 +305,7 @@ static unsigned long long adjust_time(unsigned long long old, unsigned long long delay) { unsigned long long delta, ticks; - struct timex adjust; + struct __kernel_timex adjust;
if (clock > old) { /* It is later than we thought. */ diff --git a/drivers/ptp/ptp_clock.c b/drivers/ptp/ptp_clock.c index 2e481b9e8ea5..b799fb4c4ef6 100644 --- a/drivers/ptp/ptp_clock.c +++ b/drivers/ptp/ptp_clock.c @@ -124,7 +124,7 @@ static int ptp_clock_gettime(struct posix_clock *pc, struct timespec *tp) return err; }
-static int ptp_clock_adjtime(struct posix_clock *pc, struct timex *tx) +static int ptp_clock_adjtime(struct posix_clock *pc, struct __kernel_timex *tx) { struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); struct ptp_clock_info *ops; diff --git a/include/linux/compat_time.h b/include/linux/compat_time.h index afaa1ca7986d..baa4a96a14d6 100644 --- a/include/linux/compat_time.h +++ b/include/linux/compat_time.h @@ -121,9 +121,9 @@ 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); struct compat_timex; -struct timex; -extern int compat_get_timex(struct timex *txc, struct compat_timex __user *utp); -extern int compat_put_timex(struct compat_timex __user *utp, struct timex *txc); +struct __kernel_timex; +extern int compat_get_timex(struct __kernel_timex *txc, struct compat_timex __user *utp); +extern int compat_put_timex(struct compat_timex __user *utp, struct __kernel_timex *txc);
/* * This function convert a timespec if necessary and returns a *user diff --git a/include/linux/posix-clock.h b/include/linux/posix-clock.h index 34c4498b800f..99162e319dde 100644 --- a/include/linux/posix-clock.h +++ b/include/linux/posix-clock.h @@ -57,7 +57,7 @@ struct posix_clock; struct posix_clock_operations { struct module *owner;
- int (*clock_adjtime)(struct posix_clock *pc, struct timex *tx); + int (*clock_adjtime)(struct posix_clock *pc, struct __kernel_timex *tx);
int (*clock_gettime)(struct posix_clock *pc, struct timespec *ts);
diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index 2d676be022e4..28637f929458 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -101,7 +101,7 @@ struct k_clock { int (*clock_set) (const clockid_t which_clock, const struct timespec *tp); int (*clock_get) (const clockid_t which_clock, struct timespec * tp); - int (*clock_adj) (const clockid_t which_clock, struct timex *tx); + int (*clock_adj) (const clockid_t which_clock, struct __kernel_timex *tx); int (*timer_create) (struct k_itimer *timer); int (*nsleep) (const clockid_t which_clock, int flags, struct timespec *, struct timespec __user *); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index c7b1ea2e421a..eb58a31979fc 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -49,7 +49,7 @@ struct __sysctl_args; struct sysinfo; struct timespec; struct timeval; -struct timex; +struct __kernel_timex; struct timezone; struct tms; struct utimbuf; @@ -211,7 +211,7 @@ asmlinkage long sys_gettimeofday(struct timeval __user *tv, struct timezone __user *tz); asmlinkage long sys_settimeofday(struct timeval __user *tv, struct timezone __user *tz); -asmlinkage long sys_adjtimex(struct timex __user *txc_p); +asmlinkage long sys_adjtimex(struct __kernel_timex __user *txc_p);
asmlinkage long sys_times(struct tms __user *tbuf);
@@ -275,7 +275,7 @@ asmlinkage long sys_clock_settime(clockid_t which_clock, asmlinkage long sys_clock_gettime(clockid_t which_clock, struct timespec __user *tp); asmlinkage long sys_clock_adjtime(clockid_t which_clock, - struct timex __user *tx); + struct __kernel_timex __user *tx); asmlinkage long sys_clock_getres(clockid_t which_clock, struct timespec __user *tp); asmlinkage long sys_clock_nanosleep(clockid_t which_clock, int flags, diff --git a/include/linux/timex.h b/include/linux/timex.h index 9d3f1a5b6178..d7374006b054 100644 --- a/include/linux/timex.h +++ b/include/linux/timex.h @@ -53,6 +53,10 @@ #ifndef _LINUX_TIMEX_H #define _LINUX_TIMEX_H
+#ifndef CONFIG_COMPAT_TIME +#define __kernel_timex timex +#endif + #include <uapi/linux/timex.h>
#define ADJ_ADJTIME 0x8000 /* switch between adjtime/adjtimex modes */ @@ -151,7 +155,7 @@ extern unsigned long tick_nsec; /* SHIFTED_HZ period (nsec) */ #define NTP_INTERVAL_FREQ (HZ) #define NTP_INTERVAL_LENGTH (NSEC_PER_SEC/NTP_INTERVAL_FREQ)
-extern int do_adjtimex(struct timex *); +extern int do_adjtimex(struct __kernel_timex *); extern void hardpps(const struct timespec *, const struct timespec *);
int read_current_timer(unsigned long *timer_val); diff --git a/include/uapi/linux/timex.h b/include/uapi/linux/timex.h index 80c429ecdf4e..9b131f107ada 100644 --- a/include/uapi/linux/timex.h +++ b/include/uapi/linux/timex.h @@ -92,7 +92,7 @@ struct timex { int :32; int :32; int :32; };
- +#ifndef __kernel_timex /* * __kernel_timex is the new structure that uses the same layout * as timex on 64-bit machines @@ -135,6 +135,7 @@ struct __kernel_timex { int :32; int :32; int :32; int :32; int :32; int :32; int :32; }; +#endif
/* * Mode codes (timex.mode) diff --git a/kernel/compat.c b/kernel/compat.c index 22a20bd37a73..1837c8ec79cf 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -33,9 +33,9 @@ #include <asm/uaccess.h>
#ifdef CONFIG_COMPAT_TIME -int compat_get_timex(struct timex *txc, struct compat_timex __user *utp) +int compat_get_timex(struct __kernel_timex *txc, struct compat_timex __user *utp) { - memset(txc, 0, sizeof(struct timex)); + memset(txc, 0, sizeof(struct __kernel_timex));
if (!access_ok(VERIFY_READ, utp, sizeof(struct compat_timex)) || __get_user(txc->modes, &utp->modes) || @@ -63,7 +63,7 @@ int compat_get_timex(struct timex *txc, struct compat_timex __user *utp) return 0; }
-int compat_put_timex(struct compat_timex __user *utp, struct timex *txc) +int compat_put_timex(struct compat_timex __user *utp, struct __kernel_timex *txc) { if (!access_ok(VERIFY_WRITE, utp, sizeof(struct compat_timex)) || __put_user(txc->modes, &utp->modes) || @@ -1011,7 +1011,7 @@ COMPAT_SYSCALL_DEFINE1(stime, compat_time_t __user *, tptr)
COMPAT_SYSCALL_DEFINE1(adjtimex, struct compat_timex __user *, utp) { - struct timex txc; + struct __kernel_timex txc; int err, ret;
err = compat_get_timex(&txc, utp); diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index 7a681003001c..eefffbec227b 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c @@ -181,7 +181,7 @@ static inline int is_error_status(int status) && (status & (STA_PPSWANDER|STA_PPSERROR))); }
-static inline void pps_fill_timex(struct timex *txc) +static inline void pps_fill_timex(struct __kernel_timex *txc) { txc->ppsfreq = shift_right((pps_freq >> PPM_SCALE_INV_SHIFT) * PPM_SCALE_INV, NTP_SCALE_SHIFT); @@ -213,7 +213,7 @@ static inline int is_error_status(int status) return status & (STA_UNSYNC|STA_CLOCKERR); }
-static inline void pps_fill_timex(struct timex *txc) +static inline void pps_fill_timex(struct __kernel_timex *txc) { /* PPS is not implemented, so these are zero */ txc->ppsfreq = 0; @@ -542,7 +542,7 @@ void ntp_notify_cmos_timer(void) { } /* * Propagate a new txc->status value into the NTP state: */ -static inline void process_adj_status(struct timex *txc, struct timespec64 *ts) +static inline void process_adj_status(struct __kernel_timex *txc, struct timespec64 *ts) { if ((time_status & STA_PLL) && !(txc->status & STA_PLL)) { time_state = TIME_OK; @@ -564,7 +564,7 @@ static inline void process_adj_status(struct timex *txc, struct timespec64 *ts) }
-static inline void process_adjtimex_modes(struct timex *txc, +static inline void process_adjtimex_modes(struct __kernel_timex *txc, struct timespec64 *ts, s32 *time_tai) { @@ -617,7 +617,7 @@ static inline void process_adjtimex_modes(struct timex *txc, /** * ntp_validate_timex - Ensures the timex is ok for use in do_adjtimex */ -int ntp_validate_timex(struct timex *txc) +int ntp_validate_timex(struct __kernel_timex *txc) { if (txc->modes & ADJ_ADJTIME) { /* singleshot must not be used with any other mode bits */ @@ -662,7 +662,7 @@ int ntp_validate_timex(struct timex *txc) * adjtimex mainly allows reading (and writing, if superuser) of * kernel time-keeping variables. used by xntpd. */ -int __do_adjtimex(struct timex *txc, struct timespec64 *ts, s32 *time_tai) +int __do_adjtimex(struct __kernel_timex *txc, struct timespec64 *ts, s32 *time_tai) { int result;
@@ -684,7 +684,7 @@ int __do_adjtimex(struct timex *txc, struct timespec64 *ts, s32 *time_tai) txc->offset = shift_right(time_offset * NTP_INTERVAL_FREQ, NTP_SCALE_SHIFT); if (!(time_status & STA_NANO)) - txc->offset /= NSEC_PER_USEC; + txc->offset = (long)txc->offset / NSEC_PER_USEC; }
result = time_state; /* mostly `TIME_OK' */ @@ -709,7 +709,7 @@ int __do_adjtimex(struct timex *txc, struct timespec64 *ts, s32 *time_tai) txc->time.tv_sec = (time_t)ts->tv_sec; txc->time.tv_usec = ts->tv_nsec; if (!(time_status & STA_NANO)) - txc->time.tv_usec /= NSEC_PER_USEC; + txc->time.tv_usec = (long)txc->time.tv_usec / NSEC_PER_USEC;
return result; } diff --git a/kernel/time/ntp_internal.h b/kernel/time/ntp_internal.h index bbd102ad9df7..fdee75fd1a49 100644 --- a/kernel/time/ntp_internal.h +++ b/kernel/time/ntp_internal.h @@ -6,7 +6,7 @@ extern void ntp_clear(void); /* Returns how long ticks are at present, in ns / 2^NTP_SCALE_SHIFT. */ extern u64 ntp_tick_length(void); extern int second_overflow(unsigned long secs); -extern int ntp_validate_timex(struct timex *); -extern int __do_adjtimex(struct timex *, struct timespec64 *, s32 *); +extern int ntp_validate_timex(struct __kernel_timex *); +extern int __do_adjtimex(struct __kernel_timex *, struct timespec64 *, s32 *); extern void __hardpps(const struct timespec *, const struct timespec *); #endif /* _LINUX_NTP_INTERNAL_H */ diff --git a/kernel/time/posix-clock.c b/kernel/time/posix-clock.c index ce033c7aa2e8..4724d5926d4b 100644 --- a/kernel/time/posix-clock.c +++ b/kernel/time/posix-clock.c @@ -273,7 +273,7 @@ static void put_clock_desc(struct posix_clock_desc *cd) fput(cd->fp); }
-static int pc_clock_adjtime(clockid_t id, struct timex *tx) +static int pc_clock_adjtime(clockid_t id, struct __kernel_timex *tx) { struct posix_clock_desc cd; int err; diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index 4c0d43c7c015..0b4ae6ad825a 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -218,7 +218,7 @@ static int posix_clock_realtime_set(const clockid_t which_clock, }
static int posix_clock_realtime_adj(const clockid_t which_clock, - struct timex *t) + struct __kernel_timex *t) { return do_adjtimex(t); } @@ -1070,22 +1070,28 @@ SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock, return error; }
-SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock, - struct timex __user *, utx) +static int clock_adjtime(clockid_t which_clock, struct __kernel_timex *ktx) { struct k_clock *kc = clockid_to_kclock(which_clock); - struct timex ktx; - int err;
if (!kc) return -EINVAL; if (!kc->clock_adj) return -EOPNOTSUPP;
+ return kc->clock_adj(which_clock, ktx); +} + +SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock, + struct __kernel_timex __user *, utx) +{ + struct __kernel_timex ktx; + int err; + if (copy_from_user(&ktx, utx, sizeof(ktx))) return -EFAULT;
- err = kc->clock_adj(which_clock, &ktx); + err = clock_adjtime(which_clock, &ktx);
if (err >= 0 && copy_to_user(utx, &ktx, sizeof(ktx))) return -EFAULT; @@ -1236,18 +1242,14 @@ COMPAT_SYSCALL_DEFINE2(clock_gettime, clockid_t, which_clock, COMPAT_SYSCALL_DEFINE2(clock_adjtime, clockid_t, which_clock, struct compat_timex __user *, utp) { - struct timex txc; - mm_segment_t oldfs; + struct __kernel_timex txc; int err, ret;
err = compat_get_timex(&txc, utp); if (err) return err;
- oldfs = get_fs(); - set_fs(KERNEL_DS); - ret = sys_clock_adjtime(which_clock, (struct timex __user *) &txc); - set_fs(oldfs); + ret = clock_adjtime(which_clock, &txc);
err = compat_put_timex(utp, &txc); if (err) diff --git a/kernel/time/time.c b/kernel/time/time.c index 4be28cec4c20..0310e654c042 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -213,19 +213,19 @@ SYSCALL_DEFINE2(settimeofday, struct timeval __user *, tv, return do_sys_settimeofday(tv ? &new_ts : NULL, tz ? &new_tz : NULL); }
-SYSCALL_DEFINE1(adjtimex, struct timex __user *, txc_p) +SYSCALL_DEFINE1(adjtimex, struct __kernel_timex __user *, txc_p) { - struct timex txc; /* Local copy of parameter */ + struct __kernel_timex txc; /* Local copy of parameter */ int ret;
/* Copy the user data space into the kernel copy * structure. But bear in mind that the structures * may change */ - if(copy_from_user(&txc, txc_p, sizeof(struct timex))) + if(copy_from_user(&txc, txc_p, sizeof(struct __kernel_timex))) return -EFAULT; ret = do_adjtimex(&txc); - return copy_to_user(txc_p, &txc, sizeof(struct timex)) ? -EFAULT : ret; + return copy_to_user(txc_p, &txc, sizeof(struct __kernel_timex)) ? -EFAULT : ret; }
/** diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 946acb72179f..e363752f0ccc 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1991,7 +1991,7 @@ ktime_t ktime_get_update_offsets_now(ktime_t *offs_real, ktime_t *offs_boot, /** * do_adjtimex() - Accessor function to NTP __do_adjtimex function */ -int do_adjtimex(struct timex *txc) +int do_adjtimex(struct __kernel_timex *txc) { struct timekeeper *tk = &tk_core.timekeeper; unsigned long flags;
This patch does a lot of things at once, and needs to be split up into several small steps:
- split out base function from non-compat syscalls - convert all nanosleep syscall restart handling to use __kernel_timespec - make compat syscalls use the common base functions - change normal syscalls to use __kernel_timespec
Most syscalls here are done in a simple way, the main complications is from cleaning up the compat handling to not take an extra step of get_fs()/set_fs(), which we would not actually have to do.
The nanosleep handling is still very ugly, maybe we can come up with a better implementation for that in the process as well.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- include/linux/hrtimer.h | 2 +- include/linux/posix-timers.h | 2 +- include/linux/syscalls.h | 19 +-- include/linux/thread_info.h | 6 +- ipc/syscall.c | 2 +- kernel/compat.c | 15 ++- kernel/time/alarmtimer.c | 13 +- kernel/time/hrtimer.c | 20 ++-- kernel/time/posix-cpu-timers.c | 14 ++- kernel/time/posix-timers.c | 263 +++++++++++++++++++++++------------------ 10 files changed, 203 insertions(+), 153 deletions(-)
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 05f6df1fdf5b..204eaa8843dd 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -427,7 +427,7 @@ static inline u64 hrtimer_forward_now(struct hrtimer *timer,
/* Precise sleep: */ extern long hrtimer_nanosleep(struct timespec *rqtp, - struct timespec __user *rmtp, + struct __kernel_timespec __user *rmtp, const enum hrtimer_mode mode, const clockid_t clockid); extern long hrtimer_nanosleep_restart(struct restart_block *restart_block); diff --git a/include/linux/posix-timers.h b/include/linux/posix-timers.h index 28637f929458..17caa82c2ed1 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -104,7 +104,7 @@ struct k_clock { int (*clock_adj) (const clockid_t which_clock, struct __kernel_timex *tx); int (*timer_create) (struct k_itimer *timer); int (*nsleep) (const clockid_t which_clock, int flags, - struct timespec *, struct timespec __user *); + struct timespec *, struct __kernel_timespec __user *); long (*nsleep_restart) (struct restart_block *restart_block); int (*timer_set) (struct k_itimer * timr, int flags, struct itimerspec * new_setting, diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index eb58a31979fc..be9a909b1176 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -216,7 +216,8 @@ asmlinkage long sys_adjtimex(struct __kernel_timex __user *txc_p); asmlinkage long sys_times(struct tms __user *tbuf);
asmlinkage long sys_gettid(void); -asmlinkage long sys_nanosleep(struct timespec __user *rqtp, struct timespec __user *rmtp); +asmlinkage long sys_nanosleep(struct __kernel_timespec __user *rqtp, + struct __kernel_timespec __user *rmtp); asmlinkage long sys_alarm(unsigned int seconds); asmlinkage long sys_getpid(void); asmlinkage long sys_getppid(void); @@ -264,23 +265,23 @@ asmlinkage long sys_timer_create(clockid_t which_clock, struct sigevent __user *timer_event_spec, timer_t __user * created_timer_id); asmlinkage long sys_timer_gettime(timer_t timer_id, - struct itimerspec __user *setting); + struct __kernel_itimerspec __user *setting); asmlinkage long sys_timer_getoverrun(timer_t timer_id); asmlinkage long sys_timer_settime(timer_t timer_id, int flags, - const struct itimerspec __user *new_setting, - struct itimerspec __user *old_setting); + const struct __kernel_itimerspec __user *new_setting, + struct __kernel_itimerspec __user *old_setting); asmlinkage long sys_timer_delete(timer_t timer_id); asmlinkage long sys_clock_settime(clockid_t which_clock, - const struct timespec __user *tp); + const struct __kernel_timespec __user *tp); asmlinkage long sys_clock_gettime(clockid_t which_clock, - struct timespec __user *tp); + struct __kernel_timespec __user *tp); asmlinkage long sys_clock_adjtime(clockid_t which_clock, struct __kernel_timex __user *tx); asmlinkage long sys_clock_getres(clockid_t which_clock, - struct timespec __user *tp); + struct __kernel_timespec __user *tp); asmlinkage long sys_clock_nanosleep(clockid_t which_clock, int flags, - const struct timespec __user *rqtp, - struct timespec __user *rmtp); + const struct __kernel_timespec __user *rqtp, + struct __kernel_timespec __user *rmtp);
asmlinkage long sys_nice(int increment); asmlinkage long sys_sched_setscheduler(pid_t pid, int policy, diff --git a/include/linux/thread_info.h b/include/linux/thread_info.h index 43686bb94374..6039931ac513 100644 --- a/include/linux/thread_info.h +++ b/include/linux/thread_info.h @@ -10,7 +10,7 @@ #include <linux/types.h> #include <linux/bug.h>
-struct timespec; +struct __kernel_timespec; struct compat_timespec;
/* @@ -31,9 +31,11 @@ struct restart_block { /* For nanosleep */ struct { clockid_t clockid; - struct timespec __user *rmtp; #ifdef CONFIG_COMPAT_TIME + struct __kernel_timespec __user *rmtp; struct compat_timespec __user *compat_rmtp; +#else + struct timespec __user *rmtp; #endif u64 expires; } nanosleep; diff --git a/ipc/syscall.c b/ipc/syscall.c index d7b17355d870..106d35e4f719 100644 --- a/ipc/syscall.c +++ b/ipc/syscall.c @@ -34,7 +34,7 @@ SYSCALL_DEFINE6(ipc, unsigned int, call, int, first, unsigned long, second, #else return sys_semtimedop(first, (struct sembuf __user *)ptr, second, - (const struct timespec __user *)fifth); + (const struct __kernel_timespec __user *)fifth); #endif
case SEMGET: diff --git a/kernel/compat.c b/kernel/compat.c index 1837c8ec79cf..7a29df113058 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -253,11 +253,11 @@ int compat_convert_timespec(struct timespec __user **kts, static long compat_nanosleep_restart(struct restart_block *restart) { struct compat_timespec __user *rmtp; - struct timespec rmt; + struct __kernel_timespec rmt; mm_segment_t oldfs; long ret;
- restart->nanosleep.rmtp = (struct timespec __user *) &rmt; + restart->nanosleep.rmtp = (struct __kernel_timespec __user *) &rmt; oldfs = get_fs(); set_fs(KERNEL_DS); ret = hrtimer_nanosleep_restart(restart); @@ -266,7 +266,8 @@ static long compat_nanosleep_restart(struct restart_block *restart) if (ret == -ERESTART_RESTARTBLOCK) { rmtp = restart->nanosleep.compat_rmtp;
- if (rmtp && compat_put_timespec(&rmt, rmtp)) + if (rmtp && put_user(rmt.tv_sec, &rmtp->tv_sec) && + put_user(rmt.tv_nsec, &rmtp->tv_nsec)) return -EFAULT; }
@@ -276,7 +277,8 @@ static long compat_nanosleep_restart(struct restart_block *restart) COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp, struct compat_timespec __user *, rmtp) { - struct timespec tu, rmt; + struct timespec tu; + struct __kernel_timespec rmt; mm_segment_t oldfs; long ret;
@@ -289,7 +291,7 @@ COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp, oldfs = get_fs(); set_fs(KERNEL_DS); ret = hrtimer_nanosleep(&tu, - rmtp ? (struct timespec __user *)&rmt : NULL, + rmtp ? (struct __kernel_timespec __user *)&rmt : NULL, HRTIMER_MODE_REL, CLOCK_MONOTONIC); set_fs(oldfs);
@@ -318,7 +320,8 @@ COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp, restart->fn = compat_nanosleep_restart; restart->nanosleep.compat_rmtp = rmtp;
- if (rmtp && compat_put_timespec(&rmt, rmtp)) + if (rmtp && put_user(rmt.tv_sec, &rmtp->tv_sec) && + put_user(rmt.tv_nsec, &rmtp->tv_nsec)) return -EFAULT; } return ret; diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index 1b001ed1edb9..d980d45603d5 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -679,18 +679,18 @@ static int alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp) * now and the exp value */ static int update_rmtp(ktime_t exp, enum alarmtimer_type type, - struct timespec __user *rmtp) + struct __kernel_timespec __user *rmtp) { - struct timespec rmt; + struct timespec64 rmt; ktime_t rem;
rem = ktime_sub(exp, alarm_bases[type].gettime());
if (rem.tv64 <= 0) return 0; - rmt = ktime_to_timespec(rem); + rmt = ktime_to_timespec64(rem);
- if (copy_to_user(rmtp, &rmt, sizeof(*rmtp))) + if (put_timespec64(&rmt, rmtp)); return -EFAULT;
return 1; @@ -707,7 +707,7 @@ static long __sched alarm_timer_nsleep_restart(struct restart_block *restart) { enum alarmtimer_type type = restart->nanosleep.clockid; ktime_t exp; - struct timespec __user *rmtp; + struct __kernel_timespec __user *rmtp; struct alarm alarm; int ret = 0;
@@ -744,7 +744,8 @@ out: * Handles clock_nanosleep calls against _ALARM clockids */ static int alarm_timer_nsleep(const clockid_t which_clock, int flags, - struct timespec *tsreq, struct timespec __user *rmtp) + struct timespec *tsreq, + struct __kernel_timespec __user *rmtp) { enum alarmtimer_type type = clock2alarm(which_clock); struct alarm alarm; diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index 76d4bd962b19..9ffd943a0d1d 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -1511,17 +1511,17 @@ static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mod return t->task == NULL; }
-static int update_rmtp(struct hrtimer *timer, struct timespec __user *rmtp) +static int update_rmtp(struct hrtimer *timer, struct __kernel_timespec __user *rmtp) { - struct timespec rmt; + struct timespec64 rmt; ktime_t rem;
rem = hrtimer_expires_remaining(timer); if (rem.tv64 <= 0) return 0; - rmt = ktime_to_timespec(rem); + rmt = ktime_to_timespec64(rem);
- if (copy_to_user(rmtp, &rmt, sizeof(*rmtp))) + if (put_timespec64(&rmt, rmtp)) return -EFAULT;
return 1; @@ -1530,7 +1530,7 @@ static int update_rmtp(struct hrtimer *timer, struct timespec __user *rmtp) long __sched hrtimer_nanosleep_restart(struct restart_block *restart) { struct hrtimer_sleeper t; - struct timespec __user *rmtp; + struct __kernel_timespec __user *rmtp; int ret = 0;
hrtimer_init_on_stack(&t.timer, restart->nanosleep.clockid, @@ -1554,7 +1554,7 @@ out: return ret; }
-long hrtimer_nanosleep(struct timespec *rqtp, struct timespec __user *rmtp, +long hrtimer_nanosleep(struct timespec *rqtp, struct __kernel_timespec __user *rmtp, const enum hrtimer_mode mode, const clockid_t clockid) { struct restart_block *restart; @@ -1595,13 +1595,15 @@ out: return ret; }
-SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp, - struct timespec __user *, rmtp) +SYSCALL_DEFINE2(nanosleep, struct __kernel_timespec __user *, rqtp, + struct __kernel_timespec __user *, rmtp) { struct timespec tu; + struct timespec64 tu64;
- if (copy_from_user(&tu, rqtp, sizeof(tu))) + if (get_timespec64(&tu64, rqtp)) return -EFAULT; + tu = timespec64_to_timespec(tu64);
if (!timespec_valid(&tu)) return -EINVAL; diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index 0075da74abf0..0f21086055fe 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -1332,7 +1332,8 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags, static long posix_cpu_nsleep_restart(struct restart_block *restart_block);
static int posix_cpu_nsleep(const clockid_t which_clock, int flags, - struct timespec *rqtp, struct timespec __user *rmtp) + struct timespec *rqtp, + struct __kernel_timespec __user *rmtp) { struct restart_block *restart_block = ¤t->restart_block; struct itimerspec it; @@ -1349,13 +1350,13 @@ static int posix_cpu_nsleep(const clockid_t which_clock, int flags, error = do_cpu_nanosleep(which_clock, flags, rqtp, &it);
if (error == -ERESTART_RESTARTBLOCK) { - + struct timespec64 it_value = timespec_to_timespec64(it.it_value); if (flags & TIMER_ABSTIME) return -ERESTARTNOHAND; /* * Report back to the user the time still remaining. */ - if (rmtp && copy_to_user(rmtp, &it.it_value, sizeof *rmtp)) + if (rmtp && put_timespec64(&it_value, rmtp)) return -EFAULT;
restart_block->fn = posix_cpu_nsleep_restart; @@ -1378,11 +1379,12 @@ static long posix_cpu_nsleep_restart(struct restart_block *restart_block) error = do_cpu_nanosleep(which_clock, TIMER_ABSTIME, &t, &it);
if (error == -ERESTART_RESTARTBLOCK) { - struct timespec __user *rmtp = restart_block->nanosleep.rmtp; + struct __kernel_timespec __user *rmtp = restart_block->nanosleep.rmtp; + struct timespec64 it_value = timespec_to_timespec64(it.it_value); /* * Report back to the user the time still remaining. */ - if (rmtp && copy_to_user(rmtp, &it.it_value, sizeof *rmtp)) + if (rmtp && put_timespec64(&it_value, rmtp)) return -EFAULT;
restart_block->nanosleep.expires = timespec_to_ns(&t); @@ -1411,7 +1413,7 @@ static int process_cpu_timer_create(struct k_itimer *timer) } static int process_cpu_nsleep(const clockid_t which_clock, int flags, struct timespec *rqtp, - struct timespec __user *rmtp) + struct __kernel_timespec __user *rmtp) { return posix_cpu_nsleep(PROCESS_CLOCK, flags, rqtp, rmtp); } diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index 0b4ae6ad825a..fb8b777e5c4f 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -30,6 +30,8 @@ /* These are all the functions necessary to implement * POSIX clocks & timers */ +#include <linux/compat.h> +#include <linux/compat_time.h> #include <linux/mm.h> #include <linux/interrupt.h> #include <linux/slab.h> @@ -39,7 +41,6 @@ #include <asm/uaccess.h> #include <linux/list.h> #include <linux/init.h> -#include <linux/compat.h> #include <linux/compiler.h> #include <linux/hash.h> #include <linux/posix-clock.h> @@ -131,7 +132,7 @@ static struct k_clock posix_clocks[MAX_CLOCKS]; * These ones are defined below. */ static int common_nsleep(const clockid_t, int flags, struct timespec *t, - struct timespec __user *rmtp); + struct __kernel_timespec __user *rmtp); static int common_timer_create(struct k_itimer *new_timer); static void common_timer_get(struct k_itimer *, struct itimerspec *); static int common_timer_set(struct k_itimer *, int, @@ -767,11 +768,8 @@ common_timer_get(struct k_itimer *timr, struct itimerspec *cur_setting) cur_setting->it_value = ktime_to_timespec(remaining); }
-/* Get the time remaining on a POSIX.1b interval timer. */ -SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id, - struct itimerspec __user *, setting) +static int timer_gettime(timer_t timer_id, struct itimerspec *setting) { - struct itimerspec cur_setting; struct k_itimer *timr; struct k_clock *kc; unsigned long flags; @@ -785,11 +783,22 @@ SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id, if (WARN_ON_ONCE(!kc || !kc->timer_get)) ret = -EINVAL; else - kc->timer_get(timr, &cur_setting); + kc->timer_get(timr, setting);
unlock_timer(timr, flags);
- if (!ret && copy_to_user(setting, &cur_setting, sizeof (cur_setting))) + return ret; +} + +/* Get the time remaining on a POSIX.1b interval timer. */ +SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id, + struct __kernel_itimerspec __user *, setting) +{ + struct itimerspec cur_setting; + int ret; + + ret = timer_gettime(timer_id, &cur_setting); + if (!ret && put_itimerspec(&cur_setting, setting)) return -EFAULT;
return ret; @@ -905,28 +914,17 @@ common_timer_set(struct k_itimer *timr, int flags, return 0; }
-/* Set a POSIX.1b interval timer */ -SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags, - const struct itimerspec __user *, new_setting, - struct itimerspec __user *, old_setting) +static int timer_settime(timer_t timer_id, int flags, struct itimerspec *new_spec, + struct itimerspec __user *old_spec) { struct k_itimer *timr; - struct itimerspec new_spec, old_spec; - int error = 0; + int error; unsigned long flag; - struct itimerspec *rtn = old_setting ? &old_spec : NULL; struct k_clock *kc;
- if (!new_setting) - return -EINVAL; - - if (copy_from_user(&new_spec, new_setting, sizeof (new_spec))) - return -EFAULT; - - if (!timespec_valid(&new_spec.it_interval) || - !timespec_valid(&new_spec.it_value)) + if (!timespec_valid(&new_spec->it_interval) || + !timespec_valid(&new_spec->it_value)) return -EINVAL; -retry: timr = lock_timer(timer_id, &flag); if (!timr) return -EINVAL; @@ -935,16 +933,35 @@ retry: if (WARN_ON_ONCE(!kc || !kc->timer_set)) error = -EINVAL; else - error = kc->timer_set(timr, flags, &new_spec, rtn); + error = kc->timer_set(timr, flags, new_spec, old_spec);
unlock_timer(timr, flag); + + return error; +} + +/* Set a POSIX.1b interval timer */ +SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags, + const struct __kernel_itimerspec __user *, new_setting, + struct __kernel_itimerspec __user *, old_setting) +{ + struct itimerspec new_spec, old_spec; + int error; + struct itimerspec *rtn = old_setting ? &old_spec : NULL; + + if (!new_setting) + return -EINVAL; + + if (get_itimerspec(&new_spec, new_setting)) + return -EFAULT; +retry: + error = timer_settime(timer_id, flags, &new_spec, rtn); if (error == TIMER_RETRY) { rtn = NULL; // We already got the old time... goto retry; }
- if (old_setting && !error && - copy_to_user(old_setting, &old_spec, sizeof (old_spec))) + if (old_setting && !error && put_itimerspec(&old_spec, old_setting)); error = -EFAULT;
return error; @@ -1037,8 +1054,7 @@ void exit_itimers(struct signal_struct *sig) } }
-SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock, - const struct timespec __user *, tp) +static int clock_settime(clockid_t which_clock, struct timespec64 *tp) { struct k_clock *kc = clockid_to_kclock(which_clock); struct timespec new_tp; @@ -1046,14 +1062,23 @@ SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock, if (!kc || !kc->clock_set) return -EINVAL;
- if (copy_from_user(&new_tp, tp, sizeof (*tp))) - return -EFAULT; + new_tp = timespec64_to_timespec(*tp);
return kc->clock_set(which_clock, &new_tp); }
-SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock, - struct timespec __user *,tp) +SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock, + const struct __kernel_timespec __user *, tp) +{ + struct timespec64 new_tp64; + + if (get_timespec64(&new_tp64, tp)) + return -EFAULT; + + return clock_settime(which_clock, &new_tp64); +} + +static int clock_gettime(clockid_t which_clock, struct timespec64 *tp) { struct k_clock *kc = clockid_to_kclock(which_clock); struct timespec kernel_tp; @@ -1064,7 +1089,20 @@ SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock,
error = kc->clock_get(which_clock, &kernel_tp);
- if (!error && copy_to_user(tp, &kernel_tp, sizeof (kernel_tp))) + *tp = timespec_to_timespec64(kernel_tp); + + return error; +} + +SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock, + struct __kernel_timespec __user *,tp) +{ + struct timespec64 kernel_tp64; + int error; + + error = clock_gettime(which_clock, &kernel_tp64); + + if (!error && put_timespec64(&kernel_tp64, tp)) error = -EFAULT;
return error; @@ -1099,8 +1137,7 @@ SYSCALL_DEFINE2(clock_adjtime, const clockid_t, which_clock, return err; }
-SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock, - struct timespec __user *, tp) +int clock_getres(const clockid_t which_clock, struct timespec64 __user * tp) { struct k_clock *kc = clockid_to_kclock(which_clock); struct timespec rtn_tp; @@ -1111,7 +1148,21 @@ SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock,
error = kc->clock_getres(which_clock, &rtn_tp);
- if (!error && tp && copy_to_user(tp, &rtn_tp, sizeof (rtn_tp))) + *tp = timespec_to_timespec64(rtn_tp); + + return error; + +} + +SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock, + struct __kernel_timespec __user *, tp) +{ + struct timespec64 rtn_tp64; + int error; + + error = clock_getres(which_clock, &rtn_tp64); + + if (!error && put_timespec64(&rtn_tp64, tp)) error = -EFAULT;
return error; @@ -1121,16 +1172,16 @@ SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock, * nanosleep for monotonic and realtime clocks */ static int common_nsleep(const clockid_t which_clock, int flags, - struct timespec *tsave, struct timespec __user *rmtp) + struct timespec *tsave, + struct __kernel_timespec __user *rmtp) { return hrtimer_nanosleep(tsave, rmtp, flags & TIMER_ABSTIME ? HRTIMER_MODE_ABS : HRTIMER_MODE_REL, which_clock); }
-SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags, - const struct timespec __user *, rqtp, - struct timespec __user *, rmtp) +static int clock_nanosleep(clockid_t which_clock, int flags, struct timespec64 *rqtp, + struct __kernel_timespec __user * rmtp) { struct k_clock *kc = clockid_to_kclock(which_clock); struct timespec t; @@ -1140,8 +1191,7 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags, if (!kc->nsleep) return -ENANOSLEEP_NOTSUP;
- if (copy_from_user(&t, rqtp, sizeof (struct timespec))) - return -EFAULT; + t = timespec64_to_timespec(*rqtp);
if (!timespec_valid(&t)) return -EINVAL; @@ -1149,92 +1199,78 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags, return kc->nsleep(which_clock, flags, &t, rmtp); }
-/* - * This will restart clock_nanosleep. This is required only by - * compat_clock_nanosleep_restart for now. - */ -static long clock_nanosleep_restart(struct restart_block *restart_block) +SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags, + const struct __kernel_timespec __user *, rqtp, + struct __kernel_timespec __user *, rmtp) { - clockid_t which_clock = restart_block->nanosleep.clockid; - struct k_clock *kc = clockid_to_kclock(which_clock); + struct timespec64 t64;
- if (WARN_ON_ONCE(!kc || !kc->nsleep_restart)) - return -EINVAL; + if (get_timespec64(&t64, rqtp)) + return -EFAULT;
- return kc->nsleep_restart(restart_block); + return clock_nanosleep(which_clock, flags, &t64, rmtp); }
#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) + struct compat_itimerspec __user *, new_setting, + struct compat_itimerspec __user *, old_setting) { - long err; - mm_segment_t oldfs; - struct itimerspec newts, oldts; + long error; + struct itimerspec new_spec, old_spec; + struct itimerspec *rtn = old_setting ? &old_spec : NULL;
- if (!new) + if (!new_setting) return -EINVAL; - if (get_compat_itimerspec(&newts, new)) + + if (get_compat_itimerspec(&new_spec, new_setting)) return -EFAULT; - oldfs = get_fs(); - set_fs(KERNEL_DS); - err = sys_timer_settime(timer_id, flags, - (struct itimerspec __user *) &newts, - (struct itimerspec __user *) &oldts); - set_fs(oldfs); - if (!err && old && put_compat_itimerspec(old, &oldts)) +retry: + error = timer_settime(timer_id, flags, &new_spec, rtn); + if (error == TIMER_RETRY) { + rtn = NULL; // We already got the old time... + goto retry; + } + + error = timer_settime(timer_id, flags, &new_spec, &old_spec); + if (!error && old_setting && put_compat_itimerspec(old_setting, &old_spec)) return -EFAULT; - return err; + + return error; }
COMPAT_SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id, struct compat_itimerspec __user *, setting) { long err; - mm_segment_t oldfs; struct itimerspec ts;
- oldfs = get_fs(); - set_fs(KERNEL_DS); - err = sys_timer_gettime(timer_id, - (struct itimerspec __user *) &ts); - set_fs(oldfs); + err = timer_gettime(timer_id, &ts); + if (!err && put_compat_itimerspec(setting, &ts)) return -EFAULT; + return err; }
COMPAT_SYSCALL_DEFINE2(clock_settime, clockid_t, which_clock, struct compat_timespec __user *, tp) { - long err; - mm_segment_t oldfs; - struct timespec ts; + struct timespec64 ts;
- if (compat_get_timespec(&ts, tp)) + if (compat_get_timespec64(&ts, tp)) return -EFAULT; - oldfs = get_fs(); - set_fs(KERNEL_DS); - err = sys_clock_settime(which_clock, - (struct timespec __user *) &ts); - set_fs(oldfs); - return err; + return clock_settime(which_clock, &ts); }
COMPAT_SYSCALL_DEFINE2(clock_gettime, clockid_t, which_clock, struct compat_timespec __user *, tp) { long err; - mm_segment_t oldfs; - struct timespec ts; + struct timespec64 ts;
- oldfs = get_fs(); - set_fs(KERNEL_DS); - err = sys_clock_gettime(which_clock, - (struct timespec __user *) &ts); - set_fs(oldfs); - if (!err && compat_put_timespec(&ts, tp)) + err = clock_gettime(which_clock, &ts); + if (!err && compat_put_timespec64(&ts, tp)) return -EFAULT; return err; } @@ -1261,35 +1297,37 @@ COMPAT_SYSCALL_DEFINE2(clock_adjtime, clockid_t, which_clock, COMPAT_SYSCALL_DEFINE2(clock_getres, clockid_t, which_clock, struct compat_timespec __user *, tp) { - long err; - mm_segment_t oldfs; - struct timespec ts; + int err; + struct timespec64 ts;
- oldfs = get_fs(); - set_fs(KERNEL_DS); - err = sys_clock_getres(which_clock, - (struct timespec __user *) &ts); - set_fs(oldfs); - if (!err && tp && compat_put_timespec(&ts, tp)) + err = clock_getres(which_clock, &ts); + if (!err && tp && compat_put_timespec64(&ts, tp)) return -EFAULT; return err; }
static long compat_clock_nanosleep_restart(struct restart_block *restart) { + clockid_t which_clock = restart->nanosleep.clockid; + struct compat_timespec __user *rmtp = restart->nanosleep.compat_rmtp; + struct k_clock *kc = clockid_to_kclock(which_clock); long err; mm_segment_t oldfs; - struct timespec tu; - struct compat_timespec __user *rmtp = restart->nanosleep.compat_rmtp; + struct __kernel_timespec tu; + + if (WARN_ON_ONCE(!kc || !kc->nsleep_restart)) + return -EINVAL; + + restart->nanosleep.rmtp = (struct __kernel_timespec __user *) &tu;
- restart->nanosleep.rmtp = (struct timespec __user *) &tu; oldfs = get_fs(); set_fs(KERNEL_DS); - err = clock_nanosleep_restart(restart); + err = kc->nsleep_restart(restart); set_fs(oldfs);
if ((err == -ERESTART_RESTARTBLOCK) && rmtp && - compat_put_timespec(&tu, rmtp)) + (put_user(tu.tv_sec, &rmtp->tv_sec) || + put_user(tu.tv_nsec, &rmtp->tv_nsec))) return -EFAULT;
if (err == -ERESTART_RESTARTBLOCK) { @@ -1305,21 +1343,22 @@ COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags, { long err; mm_segment_t oldfs; - struct timespec in, out; + struct timespec64 in; + struct __kernel_timespec out; struct restart_block *restart;
- if (compat_get_timespec(&in, rqtp)) + if (compat_get_timespec64(&in, rqtp)) return -EFAULT;
oldfs = get_fs(); set_fs(KERNEL_DS); - err = sys_clock_nanosleep(which_clock, flags, - (struct timespec __user *) &in, - (struct timespec __user *) &out); + err = clock_nanosleep(which_clock, flags, &in, + (struct __kernel_timespec __user *) &out); set_fs(oldfs);
if ((err == -ERESTART_RESTARTBLOCK) && rmtp && - compat_put_timespec(&out, rmtp)) + (put_user(out.tv_sec, &rmtp->tv_sec) || + put_user(out.tv_nsec, &rmtp->tv_nsec))) return -EFAULT;
if (err == -ERESTART_RESTARTBLOCK) {
32-bit architectures that set CONFIG_COMPAT_TIME need to implement a set of 22 new system calls, but other architectures do not. This ensures we warn about the right ones.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- scripts/checksyscalls.sh | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
diff --git a/scripts/checksyscalls.sh b/scripts/checksyscalls.sh index 5b3add31f9f1..5809cb81a1b8 100755 --- a/scripts/checksyscalls.sh +++ b/scripts/checksyscalls.sh @@ -197,6 +197,31 @@ cat << EOF #define __IGNORE_getpmsg #define __IGNORE_putpmsg #define __IGNORE_vserver + +#if !defined(CONFIG_ARCH_HAS_COMPAT_TIME) || defined(CONFIG_64BIT) +#define __IGNORE_clock_gettime64 +#define __IGNORE_clock_settime64 +#define __IGNORE_clock_adjtime64 +#define __IGNORE_clock_getres64 +#define __IGNORE_clock_nanosleep64 +#define __IGNORE_timer_gettime64 +#define __IGNORE_timer_settime64 +#define __IGNORE_timerfd_gettime64 +#define __IGNORE_timerfd_settime64 +#define __IGNORE_pselect64 +#define __IGNORE_ppoll64 +#define __IGNORE_io_getevents64 +#define __IGNORE_recvmmsg64 +#define __IGNORE_semtimedop64 +#define __IGNORE_mq_timedsend64 +#define __IGNORE_mq_timedreceive64 +#define __IGNORE_utimensat64 +#define __IGNORE_newfstat64 +#define __IGNORE_newfstatat64 +#define __IGNORE_rt_sigtimedwait64 +#define __IGNORE_getrusage64 +#define __IGNORE_waitid64 +#endif EOF }
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.
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 ++-- 5 files changed, 98 insertions(+), 46 deletions(-)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index e434b33b81b8..f87cd65a1105 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); }
This changes the x86 architecture to use the compat system calls for 32-bit time_t handling on both x86-32 and x86-64, so we now use the same system call implementation on both. At the same time, this adds new system calls for 64-bit time_t handling, which can be used by future libc implementations.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- arch/x86/Kconfig | 1 + arch/x86/include/asm/unistd.h | 21 +++++---- arch/x86/syscalls/syscall_32.tbl | 98 ++++++++++++++++++++++++---------------- 3 files changed, 74 insertions(+), 46 deletions(-)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 40ada6ec98a0..196a53f6adaf 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -11,6 +11,7 @@ config X86_32 depends on !64BIT select CLKSRC_I8253 select HAVE_UID16 + select ARCH_HAS_COMPAT_TIME
config X86_64 def_bool y diff --git a/arch/x86/include/asm/unistd.h b/arch/x86/include/asm/unistd.h index 2b19caa4081c..38e8994f0c83 100644 --- a/arch/x86/include/asm/unistd.h +++ b/arch/x86/include/asm/unistd.h @@ -13,25 +13,32 @@ # ifdef CONFIG_X86_32
# include <asm/unistd_32.h> -# define __ARCH_WANT_STAT64 # define __ARCH_WANT_SYS_IPC # define __ARCH_WANT_SYS_OLD_MMAP -# define __ARCH_WANT_SYS_OLD_SELECT - +# ifdef CONFIG_COMPAT_TIME +# define __ARCH_WANT_STAT64 +# define __ARCH_WANT_SYS_OLD_SELECT +# endif # else
# include <asm/unistd_64.h> # include <asm/unistd_64_x32.h> -# define __ARCH_WANT_COMPAT_SYS_TIME # define __ARCH_WANT_COMPAT_SYS_GETDENTS64 # define __ARCH_WANT_COMPAT_SYS_PREADV64 # define __ARCH_WANT_COMPAT_SYS_PWRITEV64
# endif
+# ifdef CONFIG_COMPAT_TIME +# define __ARCH_WANT_COMPAT_SYS_TIME +# define __ARCH_WANT_OLD_STAT +# define __ARCH_WANT_SYS_ALARM +# define __ARCH_WANT_SYS_TIME +# define __ARCH_WANT_SYS_UTIME +# endif + +# define __ARCH_WANT_SYS_NEWFSTATAT # define __ARCH_WANT_OLD_READDIR -# define __ARCH_WANT_OLD_STAT -# define __ARCH_WANT_SYS_ALARM # define __ARCH_WANT_SYS_FADVISE64 # define __ARCH_WANT_SYS_GETHOSTNAME # define __ARCH_WANT_SYS_GETPGRP @@ -45,8 +52,6 @@ # define __ARCH_WANT_SYS_SIGPENDING # define __ARCH_WANT_SYS_SIGPROCMASK # define __ARCH_WANT_SYS_SOCKETCALL -# define __ARCH_WANT_SYS_TIME -# define __ARCH_WANT_SYS_UTIME # define __ARCH_WANT_SYS_WAITPID # define __ARCH_WANT_SYS_FORK # define __ARCH_WANT_SYS_VFORK diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl index ef8187f9d28d..9345d2da491f 100644 --- a/arch/x86/syscalls/syscall_32.tbl +++ b/arch/x86/syscalls/syscall_32.tbl @@ -19,7 +19,7 @@ 10 i386 unlink sys_unlink 11 i386 execve sys_execve stub32_execve 12 i386 chdir sys_chdir -13 i386 time sys_time compat_sys_time +13 i386 time compat_sys_time 14 i386 mknod sys_mknod 15 i386 chmod sys_chmod 16 i386 lchown sys_lchown16 @@ -31,12 +31,12 @@ 22 i386 umount sys_oldumount 23 i386 setuid sys_setuid16 24 i386 getuid sys_getuid16 -25 i386 stime sys_stime compat_sys_stime +25 i386 stime compat_sys_stime 26 i386 ptrace sys_ptrace compat_sys_ptrace 27 i386 alarm sys_alarm 28 i386 oldfstat sys_fstat 29 i386 pause sys_pause -30 i386 utime sys_utime compat_sys_utime +30 i386 utime compat_sys_utime 31 i386 stty 32 i386 gtty 33 i386 access sys_access @@ -83,12 +83,12 @@ 74 i386 sethostname sys_sethostname 75 i386 setrlimit sys_setrlimit compat_sys_setrlimit 76 i386 getrlimit sys_old_getrlimit compat_sys_old_getrlimit -77 i386 getrusage sys_getrusage compat_sys_getrusage -78 i386 gettimeofday sys_gettimeofday compat_sys_gettimeofday -79 i386 settimeofday sys_settimeofday compat_sys_settimeofday +77 i386 getrusage compat_sys_getrusage +78 i386 gettimeofday compat_sys_gettimeofday +79 i386 settimeofday compat_sys_settimeofday 80 i386 getgroups sys_getgroups16 81 i386 setgroups sys_setgroups16 -82 i386 select sys_old_select compat_sys_old_select +82 i386 select compat_sys_old_select 83 i386 symlink sys_symlink 84 i386 oldlstat sys_lstat 85 i386 readlink sys_readlink @@ -110,17 +110,17 @@ 101 i386 ioperm sys_ioperm 102 i386 socketcall sys_socketcall compat_sys_socketcall 103 i386 syslog sys_syslog -104 i386 setitimer sys_setitimer compat_sys_setitimer -105 i386 getitimer sys_getitimer compat_sys_getitimer -106 i386 stat sys_newstat compat_sys_newstat -107 i386 lstat sys_newlstat compat_sys_newlstat -108 i386 fstat sys_newfstat compat_sys_newfstat +104 i386 setitimer compat_sys_setitimer +105 i386 getitimer compat_sys_getitimer +106 i386 stat compat_sys_newstat +107 i386 lstat compat_sys_newlstat +108 i386 fstat compat_sys_newfstat 109 i386 olduname sys_uname 110 i386 iopl sys_iopl 111 i386 vhangup sys_vhangup 112 i386 idle 113 i386 vm86old sys_vm86old sys_ni_syscall -114 i386 wait4 sys_wait4 compat_sys_wait4 +114 i386 wait4 compat_sys_wait4 115 i386 swapoff sys_swapoff 116 i386 sysinfo sys_sysinfo compat_sys_sysinfo 117 i386 ipc sys_ipc compat_sys_ipc @@ -130,7 +130,7 @@ 121 i386 setdomainname sys_setdomainname 122 i386 uname sys_newuname 123 i386 modify_ldt sys_modify_ldt -124 i386 adjtimex sys_adjtimex compat_sys_adjtimex +124 i386 adjtimex compat_sys_adjtimex 125 i386 mprotect sys_mprotect 126 i386 sigprocmask sys_sigprocmask compat_sys_sigprocmask 127 i386 create_module @@ -148,7 +148,7 @@ 139 i386 setfsgid sys_setfsgid16 140 i386 _llseek sys_llseek 141 i386 getdents sys_getdents compat_sys_getdents -142 i386 _newselect sys_select compat_sys_select +142 i386 _newselect compat_sys_select 143 i386 flock sys_flock 144 i386 msync sys_msync 145 i386 readv sys_readv compat_sys_readv @@ -167,8 +167,8 @@ 158 i386 sched_yield sys_sched_yield 159 i386 sched_get_priority_max sys_sched_get_priority_max 160 i386 sched_get_priority_min sys_sched_get_priority_min -161 i386 sched_rr_get_interval sys_sched_rr_get_interval compat_sys_sched_rr_get_interval -162 i386 nanosleep sys_nanosleep compat_sys_nanosleep +161 i386 sched_rr_get_interval compat_sys_sched_rr_get_interval +162 i386 nanosleep compat_sys_nanosleep 163 i386 mremap sys_mremap 164 i386 setresuid sys_setresuid16 165 i386 getresuid sys_getresuid16 @@ -183,7 +183,7 @@ 174 i386 rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction 175 i386 rt_sigprocmask sys_rt_sigprocmask 176 i386 rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending -177 i386 rt_sigtimedwait sys_rt_sigtimedwait compat_sys_rt_sigtimedwait +177 i386 rt_sigtimedwait compat_sys_rt_sigtimedwait 178 i386 rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo 179 i386 rt_sigsuspend sys_rt_sigsuspend 180 i386 pread64 sys_pread64 sys32_pread @@ -246,14 +246,14 @@ 237 i386 fremovexattr sys_fremovexattr 238 i386 tkill sys_tkill 239 i386 sendfile64 sys_sendfile64 -240 i386 futex sys_futex compat_sys_futex +240 i386 futex compat_sys_futex 241 i386 sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity 242 i386 sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity 243 i386 set_thread_area sys_set_thread_area 244 i386 get_thread_area sys_get_thread_area 245 i386 io_setup sys_io_setup compat_sys_io_setup 246 i386 io_destroy sys_io_destroy -247 i386 io_getevents sys_io_getevents compat_sys_io_getevents +247 i386 io_getevents compat_sys_io_getevents 248 i386 io_submit sys_io_submit compat_sys_io_submit 249 i386 io_cancel sys_io_cancel 250 i386 fadvise64 sys_fadvise64 sys32_fadvise64 @@ -266,18 +266,18 @@ 257 i386 remap_file_pages sys_remap_file_pages 258 i386 set_tid_address sys_set_tid_address 259 i386 timer_create sys_timer_create compat_sys_timer_create -260 i386 timer_settime sys_timer_settime compat_sys_timer_settime -261 i386 timer_gettime sys_timer_gettime compat_sys_timer_gettime +260 i386 timer_settime compat_sys_timer_settime +261 i386 timer_gettime compat_sys_timer_gettime 262 i386 timer_getoverrun sys_timer_getoverrun 263 i386 timer_delete sys_timer_delete -264 i386 clock_settime sys_clock_settime compat_sys_clock_settime -265 i386 clock_gettime sys_clock_gettime compat_sys_clock_gettime -266 i386 clock_getres sys_clock_getres compat_sys_clock_getres -267 i386 clock_nanosleep sys_clock_nanosleep compat_sys_clock_nanosleep +264 i386 clock_settime compat_sys_clock_settime +265 i386 clock_gettime compat_sys_clock_gettime +266 i386 clock_getres compat_sys_clock_getres +267 i386 clock_nanosleep compat_sys_clock_nanosleep 268 i386 statfs64 sys_statfs64 compat_sys_statfs64 269 i386 fstatfs64 sys_fstatfs64 compat_sys_fstatfs64 270 i386 tgkill sys_tgkill -271 i386 utimes sys_utimes compat_sys_utimes +271 i386 utimes compat_sys_utimes 272 i386 fadvise64_64 sys_fadvise64_64 sys32_fadvise64_64 273 i386 vserver 274 i386 mbind sys_mbind @@ -285,12 +285,12 @@ 276 i386 set_mempolicy sys_set_mempolicy 277 i386 mq_open sys_mq_open compat_sys_mq_open 278 i386 mq_unlink sys_mq_unlink -279 i386 mq_timedsend sys_mq_timedsend compat_sys_mq_timedsend -280 i386 mq_timedreceive sys_mq_timedreceive compat_sys_mq_timedreceive +279 i386 mq_timedsend compat_sys_mq_timedsend +280 i386 mq_timedreceive compat_sys_mq_timedreceive 281 i386 mq_notify sys_mq_notify compat_sys_mq_notify 282 i386 mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr 283 i386 kexec_load sys_kexec_load compat_sys_kexec_load -284 i386 waitid sys_waitid compat_sys_waitid +284 i386 waitid compat_sys_waitid # 285 sys_setaltroot 286 i386 add_key sys_add_key 287 i386 request_key sys_request_key @@ -305,7 +305,7 @@ 296 i386 mkdirat sys_mkdirat 297 i386 mknodat sys_mknodat 298 i386 fchownat sys_fchownat -299 i386 futimesat sys_futimesat compat_sys_futimesat +299 i386 futimesat compat_sys_futimesat 300 i386 fstatat64 sys_fstatat64 sys32_fstatat 301 i386 unlinkat sys_unlinkat 302 i386 renameat sys_renameat @@ -314,8 +314,8 @@ 305 i386 readlinkat sys_readlinkat 306 i386 fchmodat sys_fchmodat 307 i386 faccessat sys_faccessat -308 i386 pselect6 sys_pselect6 compat_sys_pselect6 -309 i386 ppoll sys_ppoll compat_sys_ppoll +308 i386 pselect6 compat_sys_pselect6 +309 i386 ppoll compat_sys_ppoll 310 i386 unshare sys_unshare 311 i386 set_robust_list sys_set_robust_list compat_sys_set_robust_list 312 i386 get_robust_list sys_get_robust_list compat_sys_get_robust_list @@ -326,13 +326,13 @@ 317 i386 move_pages sys_move_pages compat_sys_move_pages 318 i386 getcpu sys_getcpu 319 i386 epoll_pwait sys_epoll_pwait -320 i386 utimensat sys_utimensat compat_sys_utimensat +320 i386 utimensat compat_sys_utimensat 321 i386 signalfd sys_signalfd compat_sys_signalfd 322 i386 timerfd_create sys_timerfd_create 323 i386 eventfd sys_eventfd 324 i386 fallocate sys_fallocate sys32_fallocate -325 i386 timerfd_settime sys_timerfd_settime compat_sys_timerfd_settime -326 i386 timerfd_gettime sys_timerfd_gettime compat_sys_timerfd_gettime +325 i386 timerfd_settime compat_sys_timerfd_settime +326 i386 timerfd_gettime compat_sys_timerfd_gettime 327 i386 signalfd4 sys_signalfd4 compat_sys_signalfd4 328 i386 eventfd2 sys_eventfd2 329 i386 epoll_create1 sys_epoll_create1 @@ -343,13 +343,13 @@ 334 i386 pwritev sys_pwritev compat_sys_pwritev 335 i386 rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo 336 i386 perf_event_open sys_perf_event_open -337 i386 recvmmsg sys_recvmmsg compat_sys_recvmmsg +337 i386 recvmmsg compat_sys_recvmmsg 338 i386 fanotify_init sys_fanotify_init 339 i386 fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark 340 i386 prlimit64 sys_prlimit64 341 i386 name_to_handle_at sys_name_to_handle_at 342 i386 open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at -343 i386 clock_adjtime sys_clock_adjtime compat_sys_clock_adjtime +343 i386 clock_adjtime compat_sys_clock_adjtime 344 i386 syncfs sys_syncfs 345 i386 sendmmsg sys_sendmmsg compat_sys_sendmmsg 346 i386 setns sys_setns @@ -365,3 +365,25 @@ 356 i386 memfd_create sys_memfd_create 357 i386 bpf sys_bpf 358 i386 execveat sys_execveat stub32_execveat +359 i386 clock_gettime64 sys_clock_gettime +360 i386 clock_settime64 sys_clock_settime +361 i386 clock_adjtime64 sys_clock_adjtime +362 i386 clock_getres64 sys_clock_getres +363 i386 clock_nanosleep64 sys_clock_nanosleep +364 i386 timer_gettime64 sys_timer_gettime +365 i386 timer_settime64 sys_timer_settime +366 i386 timerfd_gettime64 sys_timerfd_gettime +367 i386 timerfd_settime64 sys_timerfd_settime +368 i386 pselect64 sys_pselect6 +369 i386 ppoll64 sys_ppoll +370 i386 io_getevents64 sys_io_getevents +371 i386 recvmmsg64 sys_recvmmsg +372 i386 semtimedop64 sys_semtimedop +373 i386 mq_timedsend64 sys_mq_timedsend +374 i386 mq_timedreceive64 sys_mq_timedreceive +375 i386 utimensat64 sys_utimensat +376 i386 newfstat64 sys_newfstat +377 i386 newfstatat64 sys_newfstatat +378 i386 rt_sigtimedwait64 sys_rt_sigtimedwait +379 i386 getrusage64 sys_getrusage +380 i386 waitid64 sys_waitid
This makes CONFIG_COMPAT_TIME user selectable, with the default remaining enabled so we don't break existing user space.
The idea behind turning off this option is that users that want to build long-term supported kernels can disable all code that is known to stop working in 2038, so they can debug the problems in user space today instead of guessing what might happen.
WARNING: This patch must not be applied before all 32-bit architectures are converted to select CONFIG_ARCH_HAS_COMPAT_TIME and provide the new system call entry points.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- arch/Kconfig | 3 ++- kernel/sys_ni.c | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-)
diff --git a/arch/Kconfig b/arch/Kconfig index 630d3d289569..510ec4ed5f6b 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -549,7 +549,8 @@ config ARCH_HAS_COMPAT_TIME def_bool COMPAT
config COMPAT_TIME - def_bool ARCH_HAS_COMPAT_TIME + bool "Provide backwards-compatibility with 32-bit time_t" + default 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 diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 7995ef5868d8..0b0549750fb1 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -243,3 +243,41 @@ cond_syscall(sys_bpf);
/* execveat */ cond_syscall(sys_execveat); + +/* compat time handling for 32-bit time_t */ +cond_syscall(compat_sys_getrusage); +cond_syscall(compat_sys_wait4); +cond_syscall(compat_sys_waitid); +cond_syscall(compat_sys_time); +cond_syscall(compat_sys_stime); +cond_syscall(compat_sys_gettimeofday); +cond_syscall(compat_sys_settimeofday); +cond_syscall(compat_sys_setitimer); +cond_syscall(compat_sys_getitimer); +cond_syscall(compat_sys_utime); +cond_syscall(compat_sys_utimes); +cond_syscall(compat_sys_futimesat); +cond_syscall(compat_sys_utimensat); +cond_syscall(compat_sys_newstat); +cond_syscall(compat_sys_newlstat); +cond_syscall(compat_sys_newfstat); +cond_syscall(sys_stat64); +cond_syscall(sys_lstat64); +cond_syscall(sys_fstat64); +cond_syscall(sys_fstatat64); +cond_syscall(compat_sys_adjtimex); +cond_syscall(compat_sys_old_select); +cond_syscall(compat_sys_select); +cond_syscall(compat_sys_pselect6); +cond_syscall(compat_sys_ppoll); +cond_syscall(compat_sys_sched_rr_get_interval); +cond_syscall(compat_sys_nanosleep); +cond_syscall(compat_sys_rt_sigtimedwait); +cond_syscall(compat_sys_io_getevents); +cond_syscall(compat_sys_timer_settime); +cond_syscall(compat_sys_timer_gettime); +cond_syscall(compat_sys_clock_settime); +cond_syscall(compat_sys_clock_gettime); +cond_syscall(compat_sys_clock_getres); +cond_syscall(compat_sys_clock_nanosleep); +cond_syscall(compat_sys_clock_adjtime);