The series aims at isolating data conversions of time_t based structures: struct timespec and struct itimerspec at user space boundaries. This helps to later change the underlying types to handle y2038 changes to these.
The series is an update to Arnd Bergmann's previous series: http://sourceware.org/ml/libc-alpha/2015-05/msg00070.html
The series particularly aims at changing kernel clock and timer interfaces.
The changes include a. Add data conversion apis for native and compat modes. b. Refactor nanosleep and clock_nanosleep logic: 1. Move nanosleep and its compat version to a new file nanosleep.c. Alternatively, these can be moved into hrtimer.c. 2. Refactor common functions for nanosleep: same functions are used for posix timers and posix stubs. 3. Change the posix clock callbacks to take advantage of these common functions. b. Move compat syscalls to the same files as the regular syscalls. c. Use data conversion apis in the regular and compat syscall paths. d. Remove set_fs()/get_fs() calls in the compat syscall path and use the same logic as in the regular syscall path.
Deepa Dinamani (8): time: add get_timespec64 and put_timespec64 nanosleep: Move native and compat syscalls kernel: compat: Move clock and timer compat syscalls nanosleep: Use get_timespec64() and set_timespec64() posix-timers: Use get_timepsec64() and put_timespec64() time: introduce {get,put}_itimerspec64 posix_clocks: Use get_itimerspec64() and put_itimerspec64() timerfd: Use get_itimerspec64() and put_itimerspec64()
fs/timerfd.c | 43 +++--- include/linux/compat.h | 6 + include/linux/hrtimer.h | 5 +- include/linux/posix-timers.h | 1 - include/linux/time.h | 18 +++ kernel/compat.c | 288 ++++++++++------------------------------ kernel/time/Makefile | 2 +- kernel/time/alarmtimer.c | 26 ++-- kernel/time/hrtimer.c | 34 +---- kernel/time/nanosleep.c | 126 ++++++++++++++++++ kernel/time/nanosleep.h | 19 +++ kernel/time/posix-cpu-timers.c | 27 ++-- kernel/time/posix-stubs.c | 155 +++++++++++++++++----- kernel/time/posix-timers.c | 292 +++++++++++++++++++++++++++++++---------- kernel/time/posix-timers.h | 5 +- kernel/time/time.c | 58 ++++++++ 16 files changed, 694 insertions(+), 411 deletions(-) create mode 100644 kernel/time/nanosleep.c create mode 100644 kernel/time/nanosleep.h
Add helper functions to convert between struct timespec64 and struct timespec at userspace boundaries.
This is a preparatory patch to use timespec64 as the basic type internally in the kernel as timespec is not y2038 safe on 32 bit systems. The patch helps the cause by containing all data conversions at the userspace boundaries within these functions.
Suggested-by: Arnd Bergmann arnd@arndb.de Signed-off-by: Deepa Dinamani deepa.kernel@gmail.com --- include/linux/compat.h | 2 ++ include/linux/time.h | 5 +++++ kernel/compat.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ kernel/time/time.c | 28 ++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+)
diff --git a/include/linux/compat.h b/include/linux/compat.h index 1c5f3152cbb5..72be68953411 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -156,6 +156,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 *, const void __user *); +extern int compat_put_timespec64(const struct timespec64 *, void __user *);
/* * This function convert a timespec if necessary and returns a *user diff --git a/include/linux/time.h b/include/linux/time.h index c0543f5f25de..36afb579495f 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -8,6 +8,11 @@
extern struct timezone sys_tz;
+int get_timespec64(struct timespec64 *ts, + const struct timespec __user *uts); +int put_timespec64(const struct timespec64 *ts, + struct timespec __user *uts); + #define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
static inline int timespec_equal(const struct timespec *a, diff --git a/kernel/compat.c b/kernel/compat.c index 933bcb31ae10..c7a01a01222d 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -154,6 +154,50 @@ 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 *ts64, + const struct compat_timespec __user *cts) +{ + struct compat_timespec ts; + int ret; + + ret = copy_from_user(&ts, cts, sizeof(ts)); + if (ret) + return -EFAULT; + + ts64->tv_sec = ts.tv_sec; + ts64->tv_nsec = ts.tv_nsec; + + return 0; +} + +static int __compat_put_timespec64(const struct timespec64 *ts64, + struct compat_timespec __user *cts) +{ + struct compat_timespec ts = { + .tv_sec = ts64->tv_sec, + .tv_nsec = ts64->tv_nsec + }; + return copy_to_user(cts, &ts, sizeof(ts)) ? -EFAULT : 0; +} + +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); + int compat_get_timeval(struct timeval *tv, const void __user *utv) { if (COMPAT_USE_64BIT_TIME) diff --git a/kernel/time/time.c b/kernel/time/time.c index 49c73c6ed648..fdd55c7681ec 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -786,3 +786,31 @@ struct timespec64 timespec64_add_safe(const struct timespec64 lhs,
return res; } + +int get_timespec64(struct timespec64 *ts, + const struct timespec __user *uts) +{ + struct timespec kts; + int ret; + + ret = copy_from_user(&kts, uts, sizeof(kts)); + if (ret) + return -EFAULT; + + ts->tv_sec = kts.tv_sec; + ts->tv_nsec = kts.tv_nsec; + + return 0; +} +EXPORT_SYMBOL_GPL(get_timespec64); + +int put_timespec64(const struct timespec64 *ts, + struct timespec __user *uts) +{ + struct 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);
Move nanosleep syscall to a new file that is compiled unconditionally. This helps share common nanosleep code between posix-timers and posix-stubs. The latter part is addressed in subsequent patches in the series.
In this series, this also servers as a preparatory patch to eliminate the use of set_fs()/get_fs() in the compat syscall path.
Note that the clock_nanosleep compat syscalls have to be moved twice, once into each file: posix_timers.c and posix-stubs.c.
Signed-off-by: Deepa Dinamani deepa.kernel@gmail.com --- kernel/compat.c | 131 --------------------------------------------- kernel/time/hrtimer.c | 17 +----- kernel/time/nanosleep.c | 94 ++++++++++++++++++++++++++++++++ kernel/time/posix-stubs.c | 56 +++++++++++++++++++ kernel/time/posix-timers.c | 58 ++++++++++++++++++++ 5 files changed, 209 insertions(+), 147 deletions(-) create mode 100644 kernel/time/nanosleep.c
diff --git a/kernel/compat.c b/kernel/compat.c index c7a01a01222d..89d10cf47e9c 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -257,82 +257,6 @@ int compat_convert_timespec(struct timespec __user **kts, return 0; }
-static long compat_nanosleep_restart(struct restart_block *restart) -{ - struct compat_timespec __user *rmtp; - struct timespec rmt; - mm_segment_t oldfs; - long ret; - - restart->nanosleep.rmtp = (struct timespec __user *) &rmt; - oldfs = get_fs(); - set_fs(KERNEL_DS); - ret = hrtimer_nanosleep_restart(restart); - set_fs(oldfs); - - if (ret == -ERESTART_RESTARTBLOCK) { - rmtp = restart->nanosleep.compat_rmtp; - - if (rmtp && compat_put_timespec(&rmt, rmtp)) - return -EFAULT; - } - - return ret; -} - -COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp, - struct compat_timespec __user *, rmtp) -{ - struct timespec tu, rmt; - struct timespec64 tu64; - mm_segment_t oldfs; - long ret; - - if (compat_get_timespec(&tu, rqtp)) - return -EFAULT; - - tu64 = timespec_to_timespec64(tu); - if (!timespec64_valid(&tu64)) - return -EINVAL; - - oldfs = get_fs(); - set_fs(KERNEL_DS); - ret = hrtimer_nanosleep(&tu64, - rmtp ? (struct timespec __user *)&rmt : NULL, - HRTIMER_MODE_REL, CLOCK_MONOTONIC); - set_fs(oldfs); - - /* - * hrtimer_nanosleep() can only return 0 or - * -ERESTART_RESTARTBLOCK here because: - * - * - we call it with HRTIMER_MODE_REL and therefor exclude the - * -ERESTARTNOHAND return path. - * - * - we supply the rmtp argument from the task stack (due to - * the necessary compat conversion. So the update cannot - * fail, which excludes the -EFAULT return path as well. If - * it fails nevertheless we have a bigger problem and wont - * reach this place anymore. - * - * - if the return value is 0, we do not have to update rmtp - * because there is no remaining time. - * - * We check for -ERESTART_RESTARTBLOCK nevertheless if the - * core implementation decides to return random nonsense. - */ - if (ret == -ERESTART_RESTARTBLOCK) { - struct restart_block *restart = ¤t->restart_block; - - restart->fn = compat_nanosleep_restart; - restart->nanosleep.compat_rmtp = rmtp; - - if (rmtp && compat_put_timespec(&rmt, rmtp)) - return -EFAULT; - } - return ret; -} - static inline long get_compat_itimerval(struct itimerval *o, struct compat_itimerval __user *i) { @@ -865,61 +789,6 @@ COMPAT_SYSCALL_DEFINE2(clock_getres, clockid_t, which_clock, 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; -} - /* * We currently only need the following fields from the sigevent * structure: sigev_value, sigev_signo, sig_notify and (sometimes diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index ac053bb5296e..e95628910b00 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -37,6 +37,7 @@ #include <linux/hrtimer.h> #include <linux/notifier.h> #include <linux/syscalls.h> +#include <linux/compat.h> #include <linux/kallsyms.h> #include <linux/interrupt.h> #include <linux/tick.h> @@ -1544,22 +1545,6 @@ long hrtimer_nanosleep(struct timespec64 *rqtp, struct timespec __user *rmtp, return ret; }
-SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp, - struct timespec __user *, rmtp) -{ - struct timespec64 tu64; - struct timespec tu; - - if (copy_from_user(&tu, rqtp, sizeof(tu))) - return -EFAULT; - - tu64 = timespec_to_timespec64(tu); - if (!timespec64_valid(&tu64)) - return -EINVAL; - - return hrtimer_nanosleep(&tu64, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC); -} - /* * Functions related to boot-time initialization: */ diff --git a/kernel/time/nanosleep.c b/kernel/time/nanosleep.c new file mode 100644 index 000000000000..2b6e6980b65d --- /dev/null +++ b/kernel/time/nanosleep.c @@ -0,0 +1,94 @@ +SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp, + struct timespec __user *, rmtp) +{ + struct timespec64 tu64; + struct timespec tu; + + if (copy_from_user(&tu, rqtp, sizeof(tu))) + return -EFAULT; + + tu64 = timespec_to_timespec64(tu); + if (!timespec64_valid(&tu64)) + return -EINVAL; + + return hrtimer_nanosleep(&tu64, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC); +} + +#ifdef CONFIG_COMPAT +static long compat_nanosleep_restart(struct restart_block *restart) +{ + struct compat_timespec __user *rmtp; + struct timespec rmt; + mm_segment_t oldfs; + long ret; + + restart->nanosleep.rmtp = (struct timespec __user *) &rmt; + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = hrtimer_nanosleep_restart(restart); + set_fs(oldfs); + + if (ret == -ERESTART_RESTARTBLOCK) { + rmtp = restart->nanosleep.compat_rmtp; + + if (rmtp && compat_put_timespec(&rmt, rmtp)) + return -EFAULT; + } + + return ret; +} + +COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp, + struct compat_timespec __user *, rmtp) +{ + struct timespec tu, rmt; + struct timespec64 tu64; + mm_segment_t oldfs; + long ret; + + if (compat_get_timespec(&tu, rqtp)) + return -EFAULT; + + tu64 = timespec_to_timespec64(tu); + if (!timespec64_valid(&tu64)) + return -EINVAL; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + ret = hrtimer_nanosleep(&tu64, + rmtp ? (struct timespec __user *)&rmt : NULL, + HRTIMER_MODE_REL, CLOCK_MONOTONIC); + set_fs(oldfs); + + /* + * hrtimer_nanosleep() can only return 0 or + * -ERESTART_RESTARTBLOCK here because: + * + * - we call it with HRTIMER_MODE_REL and therefor exclude the + * -ERESTARTNOHAND return path. + * + * - we supply the rmtp argument from the task stack (due to + * the necessary compat conversion. So the update cannot + * fail, which excludes the -EFAULT return path as well. If + * it fails nevertheless we have a bigger problem and wont + * reach this place anymore. + * + * - if the return value is 0, we do not have to update rmtp + * because there is no remaining time. + * + * We check for -ERESTART_RESTARTBLOCK nevertheless if the + * core implementation decides to return random nonsense. + */ + if (ret == -ERESTART_RESTARTBLOCK) { + struct restart_block *restart = ¤t->restart_block; + + restart->fn = compat_nanosleep_restart; + restart->nanosleep.compat_rmtp = rmtp; + + if (rmtp && compat_put_timespec(&rmt, rmtp)) + return -EFAULT; + } + return ret; +} +#endif + diff --git a/kernel/time/posix-stubs.c b/kernel/time/posix-stubs.c index c0cd53eb018a..3031a28921ba 100644 --- a/kernel/time/posix-stubs.c +++ b/kernel/time/posix-stubs.c @@ -14,6 +14,7 @@ #include <linux/sched.h> #include <linux/errno.h> #include <linux/syscalls.h> +#include <linux/compat.h> #include <linux/ktime.h> #include <linux/timekeeping.h> #include <linux/posix-timers.h> @@ -128,4 +129,59 @@ long clock_nanosleep_restart(struct restart_block *restart_block) { return hrtimer_nanosleep_restart(restart_block); } + +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 diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index 6e7a70b1bf37..cf32adccd062 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -45,6 +45,7 @@ #include <linux/posix-clock.h> #include <linux/posix-timers.h> #include <linux/syscalls.h> +#include <linux/compat.h> #include <linux/wait.h> #include <linux/workqueue.h> #include <linux/export.h> @@ -1176,3 +1177,60 @@ static const struct k_clock *clockid_to_kclock(const clockid_t id) return NULL; return posix_clocks[id]; } + +#ifdef CONFIG_COMPAT +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
Move the compat syscall handling to the files that handle the native syscalls.
The move helps for code reusability by helping same functions be used in both compat and native syscall handling paths.
In this series, this servers as a preparatory patch to eliminate the use of set_fs()/get_fs() in the compat syscall path.
Note that the clock compat syscalls have to be moved twice, once into each file: posix_timers.c and posix-stubs.c.
Signed-off-by: Deepa Dinamani deepa.kernel@gmail.com --- kernel/compat.c | 91 --------------------------------------------- kernel/time/posix-stubs.c | 55 +++++++++++++++++++++++++++ kernel/time/posix-timers.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 91 deletions(-)
diff --git a/kernel/compat.c b/kernel/compat.c index 89d10cf47e9c..d6559c0c69b0 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -675,80 +675,6 @@ COMPAT_SYSCALL_DEFINE3(timer_create, clockid_t, which_clock, return sys_timer_create(which_clock, event, created_timer_id); }
-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) { @@ -772,23 +698,6 @@ COMPAT_SYSCALL_DEFINE2(clock_adjtime, clockid_t, which_clock, 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; -} - /* * 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-stubs.c b/kernel/time/posix-stubs.c index 3031a28921ba..cd1b9a2e2618 100644 --- a/kernel/time/posix-stubs.c +++ b/kernel/time/posix-stubs.c @@ -125,6 +125,61 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags, }
#ifdef CONFIG_COMPAT +#define COMPAT_SYS_NI(name) SYSCALL_ALIAS(compat_sys_##name, sys_ni_posix_timers) +COMPAT_SYS_NI(timer_gettime); +COMPAT_SYS_NI(timer_settime); + +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_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; +} + long clock_nanosleep_restart(struct restart_block *restart_block) { return hrtimer_nanosleep_restart(restart_block); diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index cf32adccd062..009a9145d64d 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -1233,4 +1233,96 @@ COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags, } return err; } + +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_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; +} + #endif
Usage of these apis and their compat versions makes the sys_nanosleep() and sys_compat_nanosleep() implementations simpler.
This patch also serves as a preparatory patch for changing syscalls to use new time_t data types to support the y2038 effort by eliminating the processing of user pointers down the call stack.
Signed-off-by: Deepa Dinamani deepa.kernel@gmail.com --- include/linux/hrtimer.h | 5 +- kernel/time/Makefile | 2 +- kernel/time/alarmtimer.c | 26 +++------ kernel/time/hrtimer.c | 17 ++---- kernel/time/nanosleep.c | 130 +++++++++++++++++++++++++---------------- kernel/time/nanosleep.h | 19 ++++++ kernel/time/posix-cpu-timers.c | 27 ++++----- kernel/time/posix-stubs.c | 93 +++++++++++------------------ kernel/time/posix-timers.c | 105 +++++++++++++++------------------ kernel/time/posix-timers.h | 5 +- 10 files changed, 211 insertions(+), 218 deletions(-) create mode 100644 kernel/time/nanosleep.h
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 8c5b10eb7265..e1e6ca9a4db4 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -453,10 +453,11 @@ static inline u64 hrtimer_forward_now(struct hrtimer *timer,
/* Precise sleep: */ extern long hrtimer_nanosleep(struct timespec64 *rqtp, - struct timespec __user *rmtp, + struct timespec64 *rmtp, const enum hrtimer_mode mode, const clockid_t clockid); -extern long hrtimer_nanosleep_restart(struct restart_block *restart_block); +extern long hrtimer_nanosleep_restart(struct restart_block *restart_block, + struct timespec64 *rmtp);
extern void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *tsk); diff --git a/kernel/time/Makefile b/kernel/time/Makefile index 938dbf33ef49..0dee7cfc792b 100644 --- a/kernel/time/Makefile +++ b/kernel/time/Makefile @@ -1,4 +1,4 @@ -obj-y += time.o timer.o hrtimer.o +obj-y += time.o timer.o hrtimer.o nanosleep.o obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o obj-y += timeconv.o timecounter.o alarmtimer.o
diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c index d8a7a7e214de..567c9ca47974 100644 --- a/kernel/time/alarmtimer.c +++ b/kernel/time/alarmtimer.c @@ -710,28 +710,23 @@ static int alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp) * update_rmtp - Update remaining timespec value * @exp: expiration time * @type: timer type - * @rmtp: user pointer to remaining timepsec value + * @rmtp: pointer to remaining timespec value * * Helper function that fills in rmtp value with time between * now and the exp value */ -static int update_rmtp(ktime_t exp, enum alarmtimer_type type, - struct timespec __user *rmtp) +static int update_rmtp(ktime_t exp, enum alarmtimer_type type, + struct timespec64 *rmtp) { - struct timespec rmt; ktime_t rem;
rem = ktime_sub(exp, alarm_bases[type].gettime());
if (rem <= 0) return 0; - rmt = ktime_to_timespec(rem); - - if (copy_to_user(rmtp, &rmt, sizeof(*rmtp))) - return -EFAULT; + *rmtp = ktime_to_timespec64(rem);
return 1; - }
/** @@ -740,12 +735,12 @@ static int update_rmtp(ktime_t exp, enum alarmtimer_type type, * * Handles restarted clock_nanosleep calls */ -static long __sched alarm_timer_nsleep_restart(struct restart_block *restart) +static long __sched alarm_timer_nsleep_restart(struct restart_block *restart, + struct timespec64 *rmtp) { enum alarmtimer_type type = restart->nanosleep.clockid; - ktime_t exp; - struct timespec __user *rmtp; struct alarm alarm; + ktime_t exp; int ret = 0;
exp = restart->nanosleep.expires; @@ -757,14 +752,12 @@ static long __sched alarm_timer_nsleep_restart(struct restart_block *restart) if (freezing(current)) alarmtimer_freezerset(exp, type);
- rmtp = restart->nanosleep.rmtp; if (rmtp) { ret = update_rmtp(exp, type, rmtp); if (ret <= 0) goto out; }
- /* The other values in restart are already filled in */ ret = -ERESTART_RESTARTBLOCK; out: @@ -782,7 +775,7 @@ static long __sched alarm_timer_nsleep_restart(struct restart_block *restart) */ static int alarm_timer_nsleep(const clockid_t which_clock, int flags, struct timespec64 *tsreq, - struct timespec __user *rmtp) + struct timespec64 *rmtp) { enum alarmtimer_type type = clock2alarm(which_clock); struct restart_block *restart; @@ -827,10 +820,8 @@ static int alarm_timer_nsleep(const clockid_t which_clock, int flags, }
restart = ¤t->restart_block; - restart->fn = alarm_timer_nsleep_restart; restart->nanosleep.clockid = type; restart->nanosleep.expires = exp; - restart->nanosleep.rmtp = rmtp; ret = -ERESTART_RESTARTBLOCK;
out: @@ -850,6 +841,7 @@ const struct k_clock alarm_clock = { .timer_remaining = alarm_timer_remaining, .timer_try_to_cancel = alarm_timer_try_to_cancel, .nsleep = alarm_timer_nsleep, + .nsleep_restart = alarm_timer_nsleep_restart, }; #endif /* CONFIG_POSIX_TIMERS */
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c index e95628910b00..a53857ca28b4 100644 --- a/kernel/time/hrtimer.c +++ b/kernel/time/hrtimer.c @@ -1461,26 +1461,22 @@ 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 timespec64 *rmtp) { - struct timespec rmt; ktime_t rem;
rem = hrtimer_expires_remaining(timer); if (rem <= 0) return 0; - rmt = ktime_to_timespec(rem); - - if (copy_to_user(rmtp, &rmt, sizeof(*rmtp))) - return -EFAULT; + *rmtp = ktime_to_timespec64(rem);
return 1; }
-long __sched hrtimer_nanosleep_restart(struct restart_block *restart) +long __sched hrtimer_nanosleep_restart(struct restart_block *restart, + struct timespec64 *rmtp) { struct hrtimer_sleeper t; - struct timespec __user *rmtp; int ret = 0;
hrtimer_init_on_stack(&t.timer, restart->nanosleep.clockid, @@ -1490,7 +1486,6 @@ long __sched hrtimer_nanosleep_restart(struct restart_block *restart) if (do_nanosleep(&t, HRTIMER_MODE_ABS)) goto out;
- rmtp = restart->nanosleep.rmtp; if (rmtp) { ret = update_rmtp(&t.timer, rmtp); if (ret <= 0) @@ -1504,7 +1499,7 @@ long __sched hrtimer_nanosleep_restart(struct restart_block *restart) return ret; }
-long hrtimer_nanosleep(struct timespec64 *rqtp, struct timespec __user *rmtp, +long hrtimer_nanosleep(struct timespec64 *rqtp, struct timespec64 *rmtp, const enum hrtimer_mode mode, const clockid_t clockid) { struct restart_block *restart; @@ -1534,9 +1529,7 @@ long hrtimer_nanosleep(struct timespec64 *rqtp, struct timespec __user *rmtp, }
restart = ¤t->restart_block; - restart->fn = hrtimer_nanosleep_restart; restart->nanosleep.clockid = t.timer.base->clockid; - restart->nanosleep.rmtp = rmtp; restart->nanosleep.expires = hrtimer_get_expires_tv64(&t.timer);
ret = -ERESTART_RESTARTBLOCK; diff --git a/kernel/time/nanosleep.c b/kernel/time/nanosleep.c index 2b6e6980b65d..dd7d792b008b 100644 --- a/kernel/time/nanosleep.c +++ b/kernel/time/nanosleep.c @@ -1,64 +1,67 @@ -SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp, - struct timespec __user *, rmtp) +#include <linux/syscalls.h> +#include <linux/compat.h> + +#include "nanosleep.h" + +long nanosleep_process_return(long ret, + const struct timespec64 *rmtp_kernel, + struct timespec __user *rmtp, + long (*fn)(struct restart_block *)) { - struct timespec64 tu64; - struct timespec tu; + struct restart_block *restart = ¤t->restart_block;
- if (copy_from_user(&tu, rqtp, sizeof(tu))) + if ((ret == -ERESTART_RESTARTBLOCK) && rmtp && + put_timespec64(rmtp_kernel, rmtp)) return -EFAULT;
- tu64 = timespec_to_timespec64(tu); - if (!timespec64_valid(&tu64)) - return -EINVAL; + if (ret == -ERESTART_RESTARTBLOCK) { + restart->nanosleep.rmtp = rmtp; + restart->fn = fn; + }
- return hrtimer_nanosleep(&tu64, rmtp, HRTIMER_MODE_REL, CLOCK_MONOTONIC); + return ret; }
-#ifdef CONFIG_COMPAT -static long compat_nanosleep_restart(struct restart_block *restart) +long nanosleep_restart(struct restart_block *restart_block) { - struct compat_timespec __user *rmtp; - struct timespec rmt; - mm_segment_t oldfs; + struct timespec __user *rmtp = restart_block->nanosleep.rmtp; + struct timespec64 rmt; long ret;
- restart->nanosleep.rmtp = (struct timespec __user *) &rmt; - oldfs = get_fs(); - set_fs(KERNEL_DS); - ret = hrtimer_nanosleep_restart(restart); - set_fs(oldfs); - - if (ret == -ERESTART_RESTARTBLOCK) { - rmtp = restart->nanosleep.compat_rmtp; - - if (rmtp && compat_put_timespec(&rmt, rmtp)) - return -EFAULT; - } + ret = hrtimer_nanosleep_restart(restart_block, + rmtp ? &rmt : NULL);
- return ret; + return nanosleep_process_return(ret, &rmt, + rmtp, + nanosleep_restart); }
-COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp, - struct compat_timespec __user *, rmtp) +SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp, + struct timespec __user *, rmtp) { - struct timespec tu, rmt; - struct timespec64 tu64; - mm_segment_t oldfs; - long ret; + struct timespec64 in; + struct timespec64 out; + int err;
- if (compat_get_timespec(&tu, rqtp)) + if (get_timespec64(&in, rqtp)) return -EFAULT;
- tu64 = timespec_to_timespec64(tu); - if (!timespec64_valid(&tu64)) + if (!timespec64_valid(&in)) return -EINVAL;
- oldfs = get_fs(); - set_fs(KERNEL_DS); - ret = hrtimer_nanosleep(&tu64, - rmtp ? (struct timespec __user *)&rmt : NULL, + err = hrtimer_nanosleep(&in, rmtp ? &out : NULL, HRTIMER_MODE_REL, CLOCK_MONOTONIC); - set_fs(oldfs); + + return nanosleep_process_return(err, &out, rmtp, nanosleep_restart); +} + +#ifdef CONFIG_COMPAT +long compat_nanosleep_process_return(long err, + struct timespec64 *rmt, + struct compat_timespec __user *rmtp, + long (*fn)(struct restart_block *)) +{ + struct restart_block *restart = ¤t->restart_block;
/* * hrtimer_nanosleep() can only return 0 or @@ -79,16 +82,45 @@ COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp, * We check for -ERESTART_RESTARTBLOCK nevertheless if the * core implementation decides to return random nonsense. */ - if (ret == -ERESTART_RESTARTBLOCK) { - struct restart_block *restart = ¤t->restart_block; + if ((err == -ERESTART_RESTARTBLOCK) && rmtp && + compat_put_timespec64(rmt, rmtp)) + return -EFAULT;
- restart->fn = compat_nanosleep_restart; + if (err == -ERESTART_RESTARTBLOCK) { + restart->fn = fn; restart->nanosleep.compat_rmtp = rmtp; - - if (rmtp && compat_put_timespec(&rmt, rmtp)) - return -EFAULT; } - return ret; + return err; } -#endif
+long compat_nanosleep_restart(struct restart_block *restart) +{ + struct compat_timespec __user *rmtp = restart->nanosleep.compat_rmtp; + struct timespec64 kernel_rmt; + long ret; + + ret = hrtimer_nanosleep_restart(restart, rmtp ? &kernel_rmt : NULL); + + return compat_nanosleep_process_return(ret, &kernel_rmt, rmtp, + compat_nanosleep_restart); +} + +COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp, + struct compat_timespec __user *, rmtp) +{ + struct timespec64 tu, rmt; + long ret; + + if (compat_get_timespec64(&tu, rqtp)) + return -EFAULT; + + if (!timespec64_valid(&tu)) + return -EINVAL; + + ret = hrtimer_nanosleep(&tu, rmtp ? &rmt : NULL, + HRTIMER_MODE_REL, CLOCK_MONOTONIC); + + return compat_nanosleep_process_return(ret, &rmt, rmtp, + compat_nanosleep_restart); +} +#endif diff --git a/kernel/time/nanosleep.h b/kernel/time/nanosleep.h new file mode 100644 index 000000000000..68c924e0af14 --- /dev/null +++ b/kernel/time/nanosleep.h @@ -0,0 +1,19 @@ +#include <linux/compat.h> + +long nanosleep_restart(struct restart_block *restart_block); + +long nanosleep_process_return(long ret, + const struct timespec64 *rmtp_kernel, + struct timespec __user *rmtp, + long (*fn)(struct restart_block *)); + + +#ifdef CONFIG_COMPAT +long compat_nanosleep_restart(struct restart_block *restart); + +long compat_nanosleep_process_return(long err, + struct timespec64 *rmt, + struct compat_timespec __user *rmtp, + long (*fn)(struct restart_block *)); + +#endif diff --git a/kernel/time/posix-cpu-timers.c b/kernel/time/posix-cpu-timers.c index cb4a4eb44279..24df407e2a6d 100644 --- a/kernel/time/posix-cpu-timers.c +++ b/kernel/time/posix-cpu-timers.c @@ -1310,14 +1310,14 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags, return error; }
-static long posix_cpu_nsleep_restart(struct restart_block *restart_block); +static long posix_cpu_nsleep_restart(struct restart_block *restart_block, + struct timespec64 *rmtp);
static int posix_cpu_nsleep(const clockid_t which_clock, int flags, - struct timespec64 *rqtp, struct timespec __user *rmtp) + struct timespec64 *rqtp, struct timespec64 *rmtp) { struct restart_block *restart_block = ¤t->restart_block; struct itimerspec64 it; - struct timespec ts; int error;
/* @@ -1337,24 +1337,20 @@ static int posix_cpu_nsleep(const clockid_t which_clock, int flags, /* * Report back to the user the time still remaining. */ - ts = timespec64_to_timespec(it.it_value); - if (rmtp && copy_to_user(rmtp, &ts, sizeof(*rmtp))) - return -EFAULT; + *rmtp = it.it_value;
- restart_block->fn = posix_cpu_nsleep_restart; restart_block->nanosleep.clockid = which_clock; - restart_block->nanosleep.rmtp = rmtp; restart_block->nanosleep.expires = timespec64_to_ns(rqtp); } return error; }
-static long posix_cpu_nsleep_restart(struct restart_block *restart_block) +static long posix_cpu_nsleep_restart(struct restart_block *restart_block, + struct timespec64 *rmtp) { clockid_t which_clock = restart_block->nanosleep.clockid; struct itimerspec64 it; struct timespec64 t; - struct timespec tmp; int error;
t = ns_to_timespec64(restart_block->nanosleep.expires); @@ -1362,14 +1358,10 @@ 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; /* * Report back to the user the time still remaining. */ - tmp = timespec64_to_timespec(it.it_value); - if (rmtp && copy_to_user(rmtp, &tmp, sizeof(*rmtp))) - return -EFAULT; - + *rmtp = it.it_value; restart_block->nanosleep.expires = timespec64_to_ns(&t); } return error; @@ -1396,11 +1388,12 @@ static int process_cpu_timer_create(struct k_itimer *timer) } static int process_cpu_nsleep(const clockid_t which_clock, int flags, struct timespec64 *rqtp, - struct timespec __user *rmtp) + struct timespec64 *rmtp) { return posix_cpu_nsleep(PROCESS_CLOCK, flags, rqtp, rmtp); } -static long process_cpu_nsleep_restart(struct restart_block *restart_block) +static long process_cpu_nsleep_restart(struct restart_block *restart_block, + struct timespec64 *tp) { return -EINVAL; } diff --git a/kernel/time/posix-stubs.c b/kernel/time/posix-stubs.c index cd1b9a2e2618..61daf3576e85 100644 --- a/kernel/time/posix-stubs.c +++ b/kernel/time/posix-stubs.c @@ -11,6 +11,7 @@
#include <linux/linkage.h> #include <linux/kernel.h> +#include <linux/compat.h> #include <linux/sched.h> #include <linux/errno.h> #include <linux/syscalls.h> @@ -19,6 +20,8 @@ #include <linux/timekeeping.h> #include <linux/posix-timers.h>
+#include "nanosleep.h" + asmlinkage long sys_ni_posix_timers(void) { pr_err_once("process %d (%s) attempted a POSIX timer syscall " @@ -100,28 +103,44 @@ SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock, struct timespec __us } }
-SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags, - const struct timespec __user *, rqtp, - struct timespec __user *, rmtp) +static long do_clock_nanosleep(const clockid_t which_clock, + int flags, + struct timespec64 *rqtp, + struct timespec64 *rmtp) { - struct timespec64 t64; - struct timespec t; + long ret;
switch (which_clock) { case CLOCK_REALTIME: case CLOCK_MONOTONIC: case CLOCK_BOOTTIME: - if (copy_from_user(&t, rqtp, sizeof (struct timespec))) - return -EFAULT; - t64 = timespec_to_timespec64(t); - if (!timespec64_valid(&t64)) + if (!timespec64_valid(rqtp)) return -EINVAL; - return hrtimer_nanosleep(&t64, rmtp, flags & TIMER_ABSTIME ? + ret = hrtimer_nanosleep(rqtp, rmtp, flags & TIMER_ABSTIME ? HRTIMER_MODE_ABS : HRTIMER_MODE_REL, which_clock); + break; default: return -EINVAL; } + + return ret; +} + +SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags, + const struct timespec __user *, rqtp, + struct timespec __user *, rmtp) +{ + struct timespec64 in, out; + struct timespec64 *rtn = rmtp ? &out : NULL; + long ret; + + if (get_timespec64(&in, rqtp)) + return -EFAULT; + + ret = do_clock_nanosleep(which_clock, flags, &in, rtn); + + return nanosleep_process_return(ret, rtn, rmtp, nanosleep_restart); }
#ifdef CONFIG_COMPAT @@ -180,63 +199,19 @@ COMPAT_SYSCALL_DEFINE2(clock_getres, clockid_t, which_clock, return err; }
-long clock_nanosleep_restart(struct restart_block *restart_block) -{ - return hrtimer_nanosleep_restart(restart_block); -} - -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) { + struct timespec64 in, out; long err; - mm_segment_t oldfs; - struct timespec in, 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); - set_fs(oldfs); - - if ((err == -ERESTART_RESTARTBLOCK) && rmtp && - compat_put_timespec(&out, rmtp)) - return -EFAULT; + err = do_clock_nanosleep(which_clock, flags, &in, &out);
- if (err == -ERESTART_RESTARTBLOCK) { - restart = ¤t->restart_block; - restart->fn = compat_clock_nanosleep_restart; - restart->nanosleep.compat_rmtp = rmtp; - } - return err; + return compat_nanosleep_process_return(err, &out, rmtp, + compat_nanosleep_restart); } #endif diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index 009a9145d64d..58c2f9c2c2c8 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -37,6 +37,7 @@ #include <linux/mutex.h> #include <linux/sched/task.h>
+#include <linux/compat.h> #include <linux/uaccess.h> #include <linux/list.h> #include <linux/init.h> @@ -53,6 +54,7 @@
#include "timekeeping.h" #include "posix-timers.h" +#include "nanosleep.h"
/* * Management arrays for POSIX timers. Timers are now kept in static hash table @@ -1027,49 +1029,52 @@ 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 timespec64 *tsave, struct timespec __user *rmtp) + struct timespec64 *tsave, struct timespec64 *rmtp) { return hrtimer_nanosleep(tsave, rmtp, flags & TIMER_ABSTIME ? HRTIMER_MODE_ABS : HRTIMER_MODE_REL, which_clock); }
+long clock_nanosleep_restart(struct restart_block *restart_block) +{ + clockid_t which_clock = restart_block->nanosleep.clockid; + const struct k_clock *kc = clockid_to_kclock(which_clock); + struct timespec64 rmt; + long ret; + + if (WARN_ON_ONCE(!kc || !kc->nsleep_restart)) + return -EINVAL; + + ret = kc->nsleep_restart(restart_block, + restart_block->nanosleep.rmtp ? &rmt : NULL); + return nanosleep_process_return(ret, &rmt, + restart_block->nanosleep.rmtp, + clock_nanosleep_restart); +} + SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags, const struct timespec __user *, rqtp, struct timespec __user *, rmtp) { const struct k_clock *kc = clockid_to_kclock(which_clock); - struct timespec64 t64; - struct timespec t; + struct timespec64 in, out; + long err;
if (!kc) return -EINVAL; if (!kc->nsleep) return -ENANOSLEEP_NOTSUP;
- if (copy_from_user(&t, rqtp, sizeof (struct timespec))) + if (get_timespec64(&in, rqtp)) return -EFAULT;
- t64 = timespec_to_timespec64(t); - if (!timespec64_valid(&t64)) + if (!timespec64_valid(&in)) return -EINVAL;
- return kc->nsleep(which_clock, flags, &t64, rmtp); -} - -/* - * 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) -{ - clockid_t which_clock = restart_block->nanosleep.clockid; - const struct k_clock *kc = clockid_to_kclock(which_clock); - - if (WARN_ON_ONCE(!kc || !kc->nsleep_restart)) - return -EINVAL; - - return kc->nsleep_restart(restart_block); + err = kc->nsleep(which_clock, flags, &in, rmtp ? &out : NULL); + return nanosleep_process_return(err, &out, rmtp, + clock_nanosleep_restart); }
static const struct k_clock clock_realtime = { @@ -1181,57 +1186,40 @@ static const struct k_clock *clockid_to_kclock(const clockid_t id) #ifdef CONFIG_COMPAT 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; + clockid_t which_clock = restart->nanosleep.clockid; + const struct k_clock *kc = clockid_to_kclock(which_clock); + struct timespec64 rmt; + long err;
- restart->nanosleep.rmtp = (struct timespec __user *) &tu; - oldfs = get_fs(); - set_fs(KERNEL_DS); - err = clock_nanosleep_restart(restart); - set_fs(oldfs); + if (WARN_ON_ONCE(!kc || !kc->nsleep_restart)) + return -EINVAL;
- if ((err == -ERESTART_RESTARTBLOCK) && rmtp && - compat_put_timespec(&tu, rmtp)) - return -EFAULT; + err = kc->nsleep_restart(restart, rmtp ? &rmt : NULL);
- if (err == -ERESTART_RESTARTBLOCK) { - restart->fn = compat_clock_nanosleep_restart; - restart->nanosleep.compat_rmtp = rmtp; - } - return err; + return compat_nanosleep_process_return(err, &rmt, rmtp, + compat_clock_nanosleep_restart); }
COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags, struct compat_timespec __user *, rqtp, struct compat_timespec __user *, rmtp) { + const struct k_clock *kc = clockid_to_kclock(which_clock); + struct timespec64 in, out; long err; - mm_segment_t oldfs; - struct timespec in, 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); - set_fs(oldfs); - - if ((err == -ERESTART_RESTARTBLOCK) && rmtp && - compat_put_timespec(&out, rmtp)) - return -EFAULT; + if (!kc) + return -EINVAL; + if (!kc->nsleep) + return -ENANOSLEEP_NOTSUP; + err = kc->nsleep(which_clock, flags, &in, rmtp ? &out : NULL);
- if (err == -ERESTART_RESTARTBLOCK) { - restart = ¤t->restart_block; - restart->fn = compat_clock_nanosleep_restart; - restart->nanosleep.compat_rmtp = rmtp; - } - return err; + return compat_nanosleep_process_return(err, &out, rmtp, + compat_clock_nanosleep_restart); }
COMPAT_SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags, @@ -1324,5 +1312,4 @@ COMPAT_SYSCALL_DEFINE2(clock_getres, clockid_t, which_clock, return -EFAULT; return err; } - #endif diff --git a/kernel/time/posix-timers.h b/kernel/time/posix-timers.h index b086f5ba2f5b..85a3f34593df 100644 --- a/kernel/time/posix-timers.h +++ b/kernel/time/posix-timers.h @@ -10,8 +10,9 @@ struct k_clock { int (*clock_adj)(const clockid_t which_clock, struct timex *tx); int (*timer_create)(struct k_itimer *timer); int (*nsleep)(const clockid_t which_clock, int flags, - struct timespec64 *, struct timespec __user *); - long (*nsleep_restart)(struct restart_block *restart_block); + struct timespec64 *, struct timespec64 *); + long (*nsleep_restart)(struct restart_block *restart_block, + struct timespec64 *); int (*timer_set)(struct k_itimer *timr, int flags, struct itimerspec64 *new_setting, struct itimerspec64 *old_setting);
Usage of these apis and their compat versions makes the syscalls: clock_gettime, clock_settime, clock_getres and their compat implementations simpler.
This is a preparatory patch to isolate data conversions to struct timespec64 at userspace boundaries. This helps contain the changes needed to transition to new y2038 safe types.
Signed-off-by: Deepa Dinamani deepa.kernel@gmail.com --- kernel/time/posix-stubs.c | 111 ++++++++++++++++++++++++--------------------- kernel/time/posix-timers.c | 75 +++++++++++++----------------- 2 files changed, 91 insertions(+), 95 deletions(-)
diff --git a/kernel/time/posix-stubs.c b/kernel/time/posix-stubs.c index 61daf3576e85..da3fa8f9181a 100644 --- a/kernel/time/posix-stubs.c +++ b/kernel/time/posix-stubs.c @@ -53,40 +53,52 @@ SYS_NI(alarm); SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock, const struct timespec __user *, tp) { - struct timespec64 new_tp64; - struct timespec new_tp; + struct timespec64 new_tp;
if (which_clock != CLOCK_REALTIME) return -EINVAL; - if (copy_from_user(&new_tp, tp, sizeof (*tp))) + if (get_timespec64(&new_tp, tp)) return -EFAULT;
- new_tp64 = timespec_to_timespec64(new_tp); - return do_sys_settimeofday64(&new_tp64, NULL); + return do_sys_settimeofday64(&new_tp, NULL); }
-SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock, - struct timespec __user *,tp) +int do_clock_gettime(clockid_t which_clock, struct timespec64 *tp) { - struct timespec64 kernel_tp64; - struct timespec kernel_tp; - switch (which_clock) { - case CLOCK_REALTIME: ktime_get_real_ts64(&kernel_tp64); break; - case CLOCK_MONOTONIC: ktime_get_ts64(&kernel_tp64); break; - case CLOCK_BOOTTIME: get_monotonic_boottime64(&kernel_tp64); break; - default: return -EINVAL; + case CLOCK_REALTIME: + ktime_get_real_ts64(tp); + break; + case CLOCK_MONOTONIC: + ktime_get_ts64(tp); + break; + case CLOCK_BOOTTIME: + get_monotonic_boottime64(tp); + break; + default: + return -EINVAL; }
- kernel_tp = timespec64_to_timespec(kernel_tp64); - if (copy_to_user(tp, &kernel_tp, sizeof (kernel_tp))) + return 0; +} +SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock, + struct timespec __user *, tp) +{ + int ret; + struct timespec64 kernel_tp; + + ret = do_clock_gettime(which_clock, &kernel_tp); + if (ret) + return ret; + + if (put_timespec64(&kernel_tp, tp)) return -EFAULT; return 0; }
SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock, struct timespec __user *, tp) { - struct timespec rtn_tp = { + struct timespec64 rtn_tp = { .tv_sec = 0, .tv_nsec = hrtimer_resolution, }; @@ -95,7 +107,7 @@ SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock, struct timespec __us case CLOCK_REALTIME: case CLOCK_MONOTONIC: case CLOCK_BOOTTIME: - if (copy_to_user(tp, &rtn_tp, sizeof(rtn_tp))) + if (put_timespec64(&rtn_tp, tp)) return -EFAULT; return 0; default: @@ -151,52 +163,49 @@ COMPAT_SYS_NI(timer_settime); 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 new_tp;
- if (compat_get_timespec(&ts, tp)) + if (which_clock != CLOCK_REALTIME) + return -EINVAL; + if (compat_get_timespec64(&new_tp, 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 do_sys_settimeofday64(&new_tp, NULL); }
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)) + int ret; + struct timespec64 kernel_tp; + + ret = do_clock_gettime(which_clock, &kernel_tp); + if (ret) + return ret; + + if (compat_put_timespec64(&kernel_tp, tp)) return -EFAULT; - return err; + return 0; }
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; + struct timespec64 rtn_tp = { + .tv_sec = 0, + .tv_nsec = hrtimer_resolution, + }; + + switch (which_clock) { + case CLOCK_REALTIME: + case CLOCK_MONOTONIC: + case CLOCK_BOOTTIME: + if (compat_put_timespec64(&rtn_tp, tp)) + return -EFAULT; + return 0; + default: + return -EINVAL; + } }
COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags, diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index 58c2f9c2c2c8..58ed4e759485 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -949,34 +949,30 @@ SYSCALL_DEFINE2(clock_settime, const clockid_t, which_clock, const struct timespec __user *, tp) { const struct k_clock *kc = clockid_to_kclock(which_clock); - struct timespec64 new_tp64; - struct timespec new_tp; + struct timespec64 new_tp;
if (!kc || !kc->clock_set) return -EINVAL;
- if (copy_from_user(&new_tp, tp, sizeof (*tp))) + if (get_timespec64(&new_tp, tp)) return -EFAULT; - new_tp64 = timespec_to_timespec64(new_tp);
- return kc->clock_set(which_clock, &new_tp64); + return kc->clock_set(which_clock, &new_tp); }
SYSCALL_DEFINE2(clock_gettime, const clockid_t, which_clock, struct timespec __user *,tp) { const struct k_clock *kc = clockid_to_kclock(which_clock); - struct timespec64 kernel_tp64; - struct timespec kernel_tp; + struct timespec64 kernel_tp; int error;
if (!kc) return -EINVAL;
- error = kc->clock_get(which_clock, &kernel_tp64); - kernel_tp = timespec64_to_timespec(kernel_tp64); + error = kc->clock_get(which_clock, &kernel_tp);
- if (!error && copy_to_user(tp, &kernel_tp, sizeof (kernel_tp))) + if (!error && put_timespec64(&kernel_tp, tp)) error = -EFAULT;
return error; @@ -1009,17 +1005,15 @@ SYSCALL_DEFINE2(clock_getres, const clockid_t, which_clock, struct timespec __user *, tp) { const struct k_clock *kc = clockid_to_kclock(which_clock); - struct timespec64 rtn_tp64; - struct timespec rtn_tp; + struct timespec64 rtn_tp; int error;
if (!kc) return -EINVAL;
- error = kc->clock_getres(which_clock, &rtn_tp64); - rtn_tp = timespec64_to_timespec(rtn_tp64); + error = kc->clock_getres(which_clock, &rtn_tp);
- if (!error && tp && copy_to_user(tp, &rtn_tp, sizeof (rtn_tp))) + if (!error && tp && put_timespec64(&rtn_tp, tp)) error = -EFAULT;
return error; @@ -1265,51 +1259,44 @@ COMPAT_SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id, COMPAT_SYSCALL_DEFINE2(clock_settime, clockid_t, which_clock, struct compat_timespec __user *, tp) { - long err; - mm_segment_t oldfs; - struct timespec ts; + const struct k_clock *kc = clockid_to_kclock(which_clock); + 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 kc->clock_set(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; + const struct k_clock *kc = clockid_to_kclock(which_clock); + struct timespec64 ts; + int err; + + if (!kc) + return -EINVAL; + + err = kc->clock_get(which_clock, &ts); + + if (!err && compat_put_timespec64(&ts, tp)) + err = -EFAULT;
- 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_getres, clockid_t, which_clock, struct compat_timespec __user *, tp) { - long err; - mm_segment_t oldfs; - struct timespec ts; + const struct k_clock *kc = clockid_to_kclock(which_clock); + struct timespec64 ts; + int err;
- 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 = kc->clock_getres(which_clock, &ts); + if (!err && tp && compat_put_timespec64(&ts, tp)) return -EFAULT; + return err; } #endif
As we change the user space type for the timerfd and posix timer functions to newer data types, we need some form of conversion helpers to avoid duplicating that logic.
Suggested-by: Arnd Bergmann arnd@arndb.de Signed-off-by: Deepa Dinamani deepa.kernel@gmail.com --- include/linux/compat.h | 4 ++++ include/linux/posix-timers.h | 1 - include/linux/time.h | 13 +++++++++++++ kernel/compat.c | 22 ++++++++++++++++++++++ kernel/time/time.c | 30 ++++++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/include/linux/compat.h b/include/linux/compat.h index 72be68953411..c6547d26f8d3 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -158,6 +158,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 *, const void __user *); extern int compat_put_timespec64(const struct timespec64 *, void __user *); +extern int get_compat_itimerspec64(struct itimerspec64 *its, + const struct compat_itimerspec __user *uits); +extern int put_compat_itimerspec64(const struct itimerspec64 *its, + struct compat_itimerspec __user *uits);
/* * 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 667095dbcd37..e508948ab4b9 100644 --- a/include/linux/posix-timers.h +++ b/include/linux/posix-timers.h @@ -115,5 +115,4 @@ long clock_nanosleep_restart(struct restart_block *restart_block); void update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new);
void posixtimer_rearm(struct siginfo *info); - #endif diff --git a/include/linux/time.h b/include/linux/time.h index 36afb579495f..f9858d7e6361 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -12,6 +12,10 @@ int get_timespec64(struct timespec64 *ts, const struct timespec __user *uts); int put_timespec64(const struct timespec64 *ts, struct timespec __user *uts); +int get_itimerspec64(struct itimerspec64 *it, + const struct itimerspec __user *uit); +int put_itimerspec64(const struct itimerspec64 *it, + struct itimerspec __user *uit);
#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
@@ -275,4 +279,13 @@ static __always_inline void timespec_add_ns(struct timespec *a, u64 ns) a->tv_nsec = ns; }
+static inline bool itimerspec64_valid(const struct itimerspec64 *its) +{ + if (!timespec64_valid(&(its->it_interval)) || + !timespec64_valid(&(its->it_value))) + return false; + + return true; +} + #endif diff --git a/kernel/compat.c b/kernel/compat.c index d6559c0c69b0..1b5bd5b9343a 100644 --- a/kernel/compat.c +++ b/kernel/compat.c @@ -657,6 +657,28 @@ int put_compat_itimerspec(struct compat_itimerspec __user *dst, return 0; }
+int get_compat_itimerspec64(struct itimerspec64 *its, + const struct compat_itimerspec __user *uits) +{ + + if (__compat_get_timespec64(&its->it_interval, &uits->it_interval) || + __compat_get_timespec64(&its->it_value, &uits->it_value)) + return -EFAULT; + return 0; +} +EXPORT_SYMBOL_GPL(get_compat_itimerspec64); + +int put_compat_itimerspec64(const struct itimerspec64 *its, + struct compat_itimerspec __user *uits) +{ + if (__compat_put_timespec64(&its->it_interval, &uits->it_interval) || + __compat_put_timespec64(&its->it_value, &uits->it_value)) + return -EFAULT; + return 0; +} +EXPORT_SYMBOL_GPL(put_compat_itimerspec64); + + COMPAT_SYSCALL_DEFINE3(timer_create, clockid_t, which_clock, struct compat_sigevent __user *, timer_event_spec, timer_t __user *, created_timer_id) diff --git a/kernel/time/time.c b/kernel/time/time.c index fdd55c7681ec..16bdf7a3687e 100644 --- a/kernel/time/time.c +++ b/kernel/time/time.c @@ -814,3 +814,33 @@ int put_timespec64(const struct timespec64 *ts, return copy_to_user(uts, &kts, sizeof(kts)) ? -EFAULT : 0; } EXPORT_SYMBOL_GPL(put_timespec64); + +int get_itimerspec64(struct itimerspec64 *it, + const struct itimerspec __user *uit) +{ + int ret; + + ret = get_timespec64(&it->it_interval, &uit->it_interval); + if (ret) + return ret; + + ret = get_timespec64(&it->it_value, &uit->it_value); + + return ret; +} +EXPORT_SYMBOL_GPL(get_itimerspec64); + +int put_itimerspec64(const struct itimerspec64 *it, + struct itimerspec __user *uit) +{ + int ret; + + ret = put_timespec64(&it->it_interval, &uit->it_interval); + if (ret) + return ret; + + ret = put_timespec64(&it->it_value, &uit->it_value); + + return ret; +} +EXPORT_SYMBOL_GPL(put_itimerspec64);
Usage of these apis and their compat versions makes the syscalls: timer_settime and timer_gettime and their compat implementations simpler.
This patch also serves as a preparatory patch for changing syscalls to use new time_t data types to support the y2038 effort by isolating the processing of user pointers through these apis.
Signed-off-by: Deepa Dinamani deepa.kernel@gmail.com --- kernel/time/posix-timers.c | 138 +++++++++++++++++++++++++++------------------ 1 file changed, 84 insertions(+), 54 deletions(-)
diff --git a/kernel/time/posix-timers.c b/kernel/time/posix-timers.c index 58ed4e759485..c642c6a959ff 100644 --- a/kernel/time/posix-timers.c +++ b/kernel/time/posix-timers.c @@ -690,32 +690,54 @@ void common_timer_get(struct k_itimer *timr, struct itimerspec64 *cur_setting) } }
+/* common set of checks before we invoke timer_gettime and + * timer_settime apis. + */ +static int common_prep_timer(const struct k_clock **kc_ptr, + struct k_itimer **timr_ptr, + timer_t timer_id, + long *flags, + struct itimerspec64 *it) +{ + int error = 0; + const struct k_clock *kc; + struct k_itimer *timr; + + if (it && !itimerspec64_valid(it)) + return -EINVAL; + + *timr_ptr = timr = lock_timer(timer_id, flags); + if (!timr) + return -EINVAL; + + *kc_ptr = kc = timr->kclock; + + if (WARN_ON_ONCE(!kc || !kc->timer_set)) + error = -EINVAL; + + return error; +} + /* Get the time remaining on a POSIX.1b interval timer. */ SYSCALL_DEFINE2(timer_gettime, timer_t, timer_id, struct itimerspec __user *, setting) { - struct itimerspec64 cur_setting64; - struct itimerspec cur_setting; + struct itimerspec64 cur_setting; struct k_itimer *timr; const struct k_clock *kc; unsigned long flags; int ret = 0;
- timr = lock_timer(timer_id, &flags); - if (!timr) - return -EINVAL; - - memset(&cur_setting64, 0, sizeof(cur_setting64)); - kc = timr->kclock; - if (WARN_ON_ONCE(!kc || !kc->timer_get)) - ret = -EINVAL; + memset(&cur_setting, 0, sizeof(cur_setting)); + ret = common_prep_timer(&kc, &timr, timer_id, &flags, NULL); + if (ret) + return ret; else - kc->timer_get(timr, &cur_setting64); + kc->timer_get(timr, &cur_setting);
unlock_timer(timr, flags);
- cur_setting = itimerspec64_to_itimerspec(&cur_setting64); - if (!ret && copy_to_user(setting, &cur_setting, sizeof (cur_setting))) + if (!ret && put_itimerspec64(&cur_setting, setting)) return -EFAULT;
return ret; @@ -813,9 +835,8 @@ SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags, const struct itimerspec __user *, new_setting, struct itimerspec __user *, old_setting) { - struct itimerspec64 new_spec64, old_spec64; - struct itimerspec64 *rtn = old_setting ? &old_spec64 : NULL; - struct itimerspec new_spec, old_spec; + struct itimerspec64 new_spec, old_spec; + struct itimerspec64 *rtn = old_setting ? &old_spec : NULL; struct k_itimer *timr; unsigned long flag; const struct k_clock *kc; @@ -824,23 +845,15 @@ SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags, if (!new_setting) return -EINVAL;
- if (copy_from_user(&new_spec, new_setting, sizeof (new_spec))) - return -EFAULT; - new_spec64 = itimerspec_to_itimerspec64(&new_spec); + error = get_itimerspec64(&new_spec, new_setting); + if (error) + return error;
- if (!timespec64_valid(&new_spec64.it_interval) || - !timespec64_valid(&new_spec64.it_value)) - return -EINVAL; retry: - timr = lock_timer(timer_id, &flag); - if (!timr) - return -EINVAL; + error = common_prep_timer(&kc, &timr, timer_id, &flag, &new_spec);
- kc = timr->kclock; - if (WARN_ON_ONCE(!kc || !kc->timer_set)) - error = -EINVAL; - else - error = kc->timer_set(timr, flags, &new_spec64, rtn); + if (!error) + error = kc->timer_set(timr, flags, &new_spec, rtn);
unlock_timer(timr, flag); if (error == TIMER_RETRY) { @@ -848,9 +861,8 @@ SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags, goto retry; }
- old_spec = itimerspec64_to_itimerspec(&old_spec64); if (old_setting && !error && - copy_to_user(old_setting, &old_spec, sizeof (old_spec))) + put_itimerspec64(&old_spec, old_setting)) error = -EFAULT;
return error; @@ -1220,21 +1232,33 @@ COMPAT_SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags, struct compat_itimerspec __user *, new, struct compat_itimerspec __user *, old) { + struct itimerspec64 newts, oldts; + struct itimerspec64 *rtn = old ? &oldts : NULL; + struct k_itimer *timr; + unsigned long flag; + const struct k_clock *kc; 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)) + err = get_compat_itimerspec64(&newts, new); + if (err) + return err; + +retry: + + err = common_prep_timer(&kc, &timr, timer_id, &flag, &newts); + + if (!err) + err = kc->timer_set(timr, flags, &newts, rtn); + + unlock_timer(timr, flag); + if (err == TIMER_RETRY) { + rtn = NULL; // We already got the old time... + goto retry; + } + + if (!err && old && put_compat_itimerspec64(&oldts, old)) return -EFAULT; return err; } @@ -1242,18 +1266,24 @@ COMPAT_SYSCALL_DEFINE4(timer_settime, timer_t, timer_id, int, flags, 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)) + struct itimerspec64 ts; + struct k_itimer *timr; + const struct k_clock *kc; + unsigned long flags; + int ret = 0; + + memset(&ts, 0, sizeof(ts)); + ret = common_prep_timer(&kc, &timr, timer_id, &flags, NULL); + if (ret) + return ret; + + kc->timer_get(timr, &ts); + + unlock_timer(timr, flags); + + if (!ret && put_compat_itimerspec64(&ts, setting)) return -EFAULT; - return err; + return ret; }
COMPAT_SYSCALL_DEFINE2(clock_settime, clockid_t, which_clock,
Usage of these apis and their compat versions makes the syscalls: timerfd_settime and timerfd_gettime and their compat implementations simpler.
This patch also serves as a preparatory patch for changing syscalls to use new time_t data types to support the y2038 effort by isolating the processing of user pointers through these apis.
Signed-off-by: Deepa Dinamani deepa.kernel@gmail.com --- fs/timerfd.c | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-)
diff --git a/fs/timerfd.c b/fs/timerfd.c index c543cdb5f8ed..ece0c02d7e63 100644 --- a/fs/timerfd.c +++ b/fs/timerfd.c @@ -169,7 +169,7 @@ static ktime_t timerfd_get_remaining(struct timerfd_ctx *ctx) }
static int timerfd_setup(struct timerfd_ctx *ctx, int flags, - const struct itimerspec *ktmr) + const struct itimerspec64 *ktmr) { enum hrtimer_mode htmode; ktime_t texp; @@ -178,10 +178,10 @@ static int timerfd_setup(struct timerfd_ctx *ctx, int flags, htmode = (flags & TFD_TIMER_ABSTIME) ? HRTIMER_MODE_ABS: HRTIMER_MODE_REL;
- texp = timespec_to_ktime(ktmr->it_value); + texp = timespec64_to_ktime(ktmr->it_value); ctx->expired = 0; ctx->ticks = 0; - ctx->tintv = timespec_to_ktime(ktmr->it_interval); + ctx->tintv = timespec64_to_ktime(ktmr->it_interval);
if (isalarm(ctx)) { alarm_init(&ctx->t.alarm, @@ -432,16 +432,15 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags) }
static int do_timerfd_settime(int ufd, int flags, - const struct itimerspec *new, - struct itimerspec *old) + const struct itimerspec64 *new, + struct itimerspec64 *old) { struct fd f; struct timerfd_ctx *ctx; int ret;
if ((flags & ~TFD_SETTIME_FLAGS) || - !timespec_valid(&new->it_value) || - !timespec_valid(&new->it_interval)) + !itimerspec64_valid(new)) return -EINVAL;
ret = timerfd_fget(ufd, &f); @@ -487,8 +486,8 @@ static int do_timerfd_settime(int ufd, int flags, hrtimer_forward_now(&ctx->t.tmr, ctx->tintv); }
- old->it_value = ktime_to_timespec(timerfd_get_remaining(ctx)); - old->it_interval = ktime_to_timespec(ctx->tintv); + old->it_value = ktime_to_timespec64(timerfd_get_remaining(ctx)); + old->it_interval = ktime_to_timespec64(ctx->tintv);
/* * Re-program the timer to the new value ... @@ -500,7 +499,7 @@ static int do_timerfd_settime(int ufd, int flags, return ret; }
-static int do_timerfd_gettime(int ufd, struct itimerspec *t) +static int do_timerfd_gettime(int ufd, struct itimerspec64 *t) { struct fd f; struct timerfd_ctx *ctx; @@ -525,8 +524,8 @@ static int do_timerfd_gettime(int ufd, struct itimerspec *t) hrtimer_restart(&ctx->t.tmr); } } - t->it_value = ktime_to_timespec(timerfd_get_remaining(ctx)); - t->it_interval = ktime_to_timespec(ctx->tintv); + t->it_value = ktime_to_timespec64(timerfd_get_remaining(ctx)); + t->it_interval = ktime_to_timespec64(ctx->tintv); spin_unlock_irq(&ctx->wqh.lock); fdput(f); return 0; @@ -536,15 +535,15 @@ SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags, const struct itimerspec __user *, utmr, struct itimerspec __user *, otmr) { - struct itimerspec new, old; + struct itimerspec64 new, old; int ret;
- if (copy_from_user(&new, utmr, sizeof(new))) + if (get_itimerspec64(&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_itimerspec64(&old, otmr)) return -EFAULT;
return ret; @@ -552,11 +551,11 @@ SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags,
SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct itimerspec __user *, otmr) { - struct itimerspec kotmr; + struct itimerspec64 kotmr; int ret = do_timerfd_gettime(ufd, &kotmr); if (ret) return ret; - return copy_to_user(otmr, &kotmr, sizeof(kotmr)) ? -EFAULT: 0; + return put_itimerspec64(&kotmr, otmr) ? -EFAULT : 0; }
#ifdef CONFIG_COMPAT @@ -564,15 +563,15 @@ COMPAT_SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags, const struct compat_itimerspec __user *, utmr, struct compat_itimerspec __user *, otmr) { - struct itimerspec new, old; + struct itimerspec64 new, old; int ret;
- if (get_compat_itimerspec(&new, utmr)) + if (get_compat_itimerspec64(&new, utmr)) return -EFAULT; ret = do_timerfd_settime(ufd, flags, &new, &old); if (ret) return ret; - if (otmr && put_compat_itimerspec(otmr, &old)) + if (otmr && put_compat_itimerspec64(&old, otmr)) return -EFAULT; return ret; } @@ -580,10 +579,10 @@ COMPAT_SYSCALL_DEFINE4(timerfd_settime, int, ufd, int, flags, COMPAT_SYSCALL_DEFINE2(timerfd_gettime, int, ufd, struct compat_itimerspec __user *, otmr) { - struct itimerspec kotmr; + struct itimerspec64 kotmr; int ret = do_timerfd_gettime(ufd, &kotmr); if (ret) return ret; - return put_compat_itimerspec(otmr, &kotmr) ? -EFAULT: 0; + return put_compat_itimerspec64(&kotmr, otmr) ? -EFAULT : 0; } #endif
On Sun, Jun 18, 2017 at 11:45:07PM -0700, Deepa Dinamani wrote:
The series aims at isolating data conversions of time_t based structures: struct timespec and struct itimerspec at user space boundaries. This helps to later change the underlying types to handle y2038 changes to these.
The series is an update to Arnd Bergmann's previous series: http://sourceware.org/ml/libc-alpha/2015-05/msg00070.html
The series particularly aims at changing kernel clock and timer interfaces.
The changes include a. Add data conversion apis for native and compat modes. b. Refactor nanosleep and clock_nanosleep logic:
- Move nanosleep and its compat version to a new file nanosleep.c. Alternatively, these can be moved into hrtimer.c.
- Refactor common functions for nanosleep: same functions are used for posix timers and posix stubs.
- Change the posix clock callbacks to take advantage of these common functions.
b. Move compat syscalls to the same files as the regular syscalls. c. Use data conversion apis in the regular and compat syscall paths. d. Remove set_fs()/get_fs() calls in the compat syscall path and use the same logic as in the regular syscall path.
Check the stuff already in tip/timers/core; at the very least it overlaps considerably with your series. And your variant is a lot more convoluted - there's no need to have a separate compat restart, etc.
Check the stuff already in tip/timers/core; at the very least it overlaps considerably with your series. And your variant is a lot more convoluted - there's no need to have a separate compat restart, etc.
Thanks, I see there were updates last week to the tree.
For the nanosleep part, I see that you guys addressed the problem differently.
These were my thoughts when I did the patch.
1. I wanted to maintain a strict layering approach i.e., I wanted all the syscall and restart syscalls to be handled from k_clock layer. This means instead of each timer having to fill in its own restart function, isn't it better for the behaviour to be mandated at posix timers layer? Also, this way we can keep track of all the return values from the internal layers and process them all the same way.
2. The new type argument to the restart_block for timespec will be superfluous after year 2038 as compat_timespec is not longer relevant then. We can actually do away with it at that time. And, in the meanwhile, there will be 2 compat versions for 64 bit time_t which will be same as native format and the 32 bit format. Although this can handled because the 64 bit time_t compat and native syscalls have same entry, it seems like we are adding a field for just backward compatibility, just like the compat_timespec that is already in the struct.
3. I was also aiming for user pointers to be not touched by timer specific code as it can get messy if not handled properly with 2 compat time_t versions.
Do you guys see any benefit in doing it the way patch 4/8 in the current series does?
-Deepa
On Mon, Jun 19, 2017 at 12:31:00PM -0700, Deepa Dinamani wrote:
- I was also aiming for user pointers to be not touched by timer
specific code as it can get messy if not handled properly with 2 compat time_t versions.
So have one helper that deals with all copyout and have it used by all of them. IMO all that code should treat userland representation as completely opaque. Just switch nanosleep_copyout() to take timespec64 instead of timespec (for kernel-side object) and that'll do it, wouldn't it?
Do you guys see any benefit in doing it the way patch 4/8 in the current series does?
Well, if you want to keep more restart functions and more boilerplate on compat side...
On Mon, Jun 19, 2017 at 12:46 PM, Al Viro viro@zeniv.linux.org.uk wrote:
On Mon, Jun 19, 2017 at 12:31:00PM -0700, Deepa Dinamani wrote:
- I was also aiming for user pointers to be not touched by timer
specific code as it can get messy if not handled properly with 2 compat time_t versions.
So have one helper that deals with all copyout and have it used by all of them. IMO all that code should treat userland representation as completely opaque. Just switch nanosleep_copyout() to take timespec64 instead of timespec (for kernel-side object) and that'll do it, wouldn't it?
Yes, that would work. If that is preferred, then I will just do that and rebase the patches.
Do you guys see any benefit in doing it the way patch 4/8 in the current series does?
Well, if you want to keep more restart functions and more boilerplate on compat side...
Yes, there is no good way of achieving everything because of backward compatibility.
Thanks, Deepa
On Mon, Jun 19, 2017 at 01:52:05PM -0700, Deepa Dinamani wrote:
On Mon, Jun 19, 2017 at 12:46 PM, Al Viro viro@zeniv.linux.org.uk wrote:
On Mon, Jun 19, 2017 at 12:31:00PM -0700, Deepa Dinamani wrote:
- I was also aiming for user pointers to be not touched by timer
specific code as it can get messy if not handled properly with 2 compat time_t versions.
So have one helper that deals with all copyout and have it used by all of them. IMO all that code should treat userland representation as completely opaque. Just switch nanosleep_copyout() to take timespec64 instead of timespec (for kernel-side object) and that'll do it, wouldn't it?
Yes, that would work. If that is preferred, then I will just do that and rebase the patches.
Please, do. Note that quite a few things in that series won't be needed anymore (e.g. compat syscalls are already moved to native ones, etc.).
Might make sense to take it to #kernel - lower latency that way...