]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/commitdiff
time/posix-timers: Move the compat copyouts to the nanosleep implementations
authorAl Viro <viro@zeniv.linux.org.uk>
Wed, 7 Jun 2017 08:42:31 +0000 (09:42 +0100)
committerThomas Gleixner <tglx@linutronix.de>
Tue, 13 Jun 2017 22:00:42 +0000 (00:00 +0200)
Turn restart_block.nanosleep.{rmtp,compat_rmtp} into a tagged union (kind =
1 -> native, kind = 2 -> compat, kind = 0 -> nothing) and make the places
doing actual copyout handle compat as well as native (that will become a
helper in the next commit).  Result: compat wrappers, messing with
reassignments, etc. are gone.

[ tglx: Folded in a variant of Peter Zijlstras enum patch ]

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: John Stultz <john.stultz@linaro.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20170607084241.28657-6-viro@ZenIV.linux.org.uk
include/linux/posix-timers.h
include/linux/restart_block.h
kernel/compat.c
kernel/time/alarmtimer.c
kernel/time/hrtimer.c
kernel/time/posix-cpu-timers.c
kernel/time/posix-stubs.c
kernel/time/posix-timers.c

index 667095dbcd37dcbffb4184d3b47f7769b7472040..29f1b7f09ced6bf51385a02a9601fe25f671ea86 100644 (file)
@@ -110,8 +110,6 @@ void posix_cpu_timers_exit_group(struct task_struct *task);
 void set_process_cpu_timer(struct task_struct *task, unsigned int clock_idx,
                           u64 *newval, u64 *oldval);
 
-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);
index 0d905d8ec553fae64d5dd16f8669b46795c7b2be..19df8422606c4d3f891c3454dabe57c605c1d46f 100644 (file)
@@ -11,6 +11,14 @@ struct timespec;
 struct compat_timespec;
 struct pollfd;
 
+enum timespec_type {
+       TT_NONE         = 0,
+       TT_NATIVE       = 1,
+#ifdef CONFIG_COMPAT
+       TT_COMPAT       = 2,
+#endif
+};
+
 /*
  * System call restart block.
  */
@@ -29,10 +37,13 @@ struct restart_block {
                /* For nanosleep */
                struct {
                        clockid_t clockid;
-                       struct timespec __user *rmtp;
+                       enum timespec_type type;
+                       union {
+                               struct timespec __user *rmtp;
 #ifdef CONFIG_COMPAT
-                       struct compat_timespec __user *compat_rmtp;
+                               struct compat_timespec __user *compat_rmtp;
 #endif
+                       };
                        u64 expires;
                } nanosleep;
                /* For poll */
index cc9ba9d29b473eb420713b7e0c110ff7b762e5c4..23afa26f574bc4a93da642c1de3a7dfefb1c91f6 100644 (file)
@@ -213,82 +213,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);
-       current->restart_block.nanosleep.rmtp =
-                               rmtp ? (struct timespec __user *)&rmt : NULL;
-       ret = hrtimer_nanosleep(&tu64, 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 = &current->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)
 {
@@ -821,61 +745,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 = &current->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
index d859a3601ddd1f4be63058655c10a748e4364e38..57bcf94ee132001fdb062769e1e6fd31721bc8c2 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/posix-timers.h>
 #include <linux/workqueue.h>
 #include <linux/freezer.h>
+#include <linux/compat.h>
 
 #include "posix-timers.h"
 
@@ -691,7 +692,7 @@ static enum alarmtimer_restart alarmtimer_nsleep_wakeup(struct alarm *alarm,
 static int alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp,
                                enum alarmtimer_type type)
 {
-       struct timespec __user *rmtp;
+       struct restart_block *restart;
        alarm->data = (void *)current;
        do {
                set_current_state(TASK_INTERRUPTIBLE);
@@ -709,8 +710,8 @@ static int alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp,
 
        if (freezing(current))
                alarmtimer_freezerset(absexp, type);
-       rmtp = current->restart_block.nanosleep.rmtp;
-       if (rmtp) {
+       restart = &current->restart_block;
+       if (restart->nanosleep.type != TT_NONE) {
                struct timespec rmt;
                ktime_t rem;
 
@@ -720,7 +721,14 @@ static int alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp,
                        return 0;
                rmt = ktime_to_timespec(rem);
 
-               if (copy_to_user(rmtp, &rmt, sizeof(*rmtp)))
+#ifdef CONFIG_COMPAT
+               if (restart->nanosleep.type == TT_COMPAT) {
+                       if (compat_put_timespec(&rmt,
+                                               restart->nanosleep.compat_rmtp))
+                               return -EFAULT;
+               } else
+#endif
+               if (copy_to_user(restart->nanosleep.rmtp, &rmt, sizeof(rmt)))
                        return -EFAULT;
        }
        return -ERESTART_RESTARTBLOCK;
index baa7b846b6e347c46eeea8e00970d966f36c854a..5370da8fc0a4d8c9af6947587a56893bcdfc3c34 100644 (file)
@@ -51,6 +51,7 @@
 #include <linux/sched/debug.h>
 #include <linux/timer.h>
 #include <linux/freezer.h>
+#include <linux/compat.h>
 
 #include <linux/uaccess.h>
 
@@ -1441,7 +1442,8 @@ EXPORT_SYMBOL_GPL(hrtimer_init_sleeper);
 
 static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode)
 {
-       struct timespec __user *rmtp;
+       struct restart_block *restart;
+
        hrtimer_init_sleeper(t, current);
 
        do {
@@ -1461,15 +1463,23 @@ static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mod
        if (!t->task)
                return 0;
 
-       rmtp = current->restart_block.nanosleep.rmtp;
-       if (rmtp) {
-               struct timespec rmt;
+       restart = &current->restart_block;
+       if (restart->nanosleep.type != TT_NONE) {
                ktime_t rem = hrtimer_expires_remaining(&t->timer);
+               struct timespec rmt;
+
                if (rem <= 0)
                        return 0;
                rmt = ktime_to_timespec(rem);
 
-               if (copy_to_user(rmtp, &rmt, sizeof(*rmtp)))
+#ifdef CONFIG_COMPAT
+               if (restart->nanosleep.type == TT_COMPAT) {
+                       if (compat_put_timespec(&rmt,
+                                               restart->nanosleep.compat_rmtp))
+                               return -EFAULT;
+               } else
+#endif
+               if (copy_to_user(restart->nanosleep.rmtp, &rmt, sizeof(rmt)))
                        return -EFAULT;
        }
        return -ERESTART_RESTARTBLOCK;
@@ -1535,10 +1545,32 @@ SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp,
        if (!timespec64_valid(&tu64))
                return -EINVAL;
 
+       current->restart_block.nanosleep.type = rmtp ? TT_NATIVE : TT_NONE;
        current->restart_block.nanosleep.rmtp = rmtp;
        return hrtimer_nanosleep(&tu64, HRTIMER_MODE_REL, CLOCK_MONOTONIC);
 }
 
+#ifdef CONFIG_COMPAT
+
+COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp,
+                      struct compat_timespec __user *, rmtp)
+{
+       struct timespec64 tu64;
+       struct timespec tu;
+
+       if (compat_get_timespec(&tu, rqtp))
+               return -EFAULT;
+
+       tu64 = timespec_to_timespec64(tu);
+       if (!timespec64_valid(&tu64))
+               return -EINVAL;
+
+       current->restart_block.nanosleep.type = rmtp ? TT_COMPAT : TT_NONE;
+       current->restart_block.nanosleep.compat_rmtp = rmtp;
+       return hrtimer_nanosleep(&tu64, HRTIMER_MODE_REL, CLOCK_MONOTONIC);
+}
+#endif
+
 /*
  * Functions related to boot-time initialization:
  */
index ec6258c9cde520dd48dbcf0bf605db2d514b3cd5..1563ca22cf1f57686ed3269518729359114e87be 100644 (file)
@@ -12,6 +12,7 @@
 #include <trace/events/timer.h>
 #include <linux/tick.h>
 #include <linux/workqueue.h>
+#include <linux/compat.h>
 
 #include "posix-timers.h"
 
@@ -1243,10 +1244,9 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags,
        timer.it_process = current;
        if (!error) {
                static struct itimerspec64 zero_it;
-               struct restart_block *restart = &current->restart_block;
-               struct timespec __user *rmtp;
+               struct restart_block *restart;
 
-               memset(&it, 0, sizeof it);
+               memset(&it, 0, sizeof(it));
                it.it_value = *rqtp;
 
                spin_lock_irq(&timer.it_lock);
@@ -1311,12 +1311,20 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags,
                /*
                 * Report back to the user the time still remaining.
                 */
-               rmtp = restart->nanosleep.rmtp;
-               if (rmtp) {
+               restart = &current->restart_block;
+               if (restart->nanosleep.type != TT_NONE) {
                        struct timespec ts;
 
                        ts = timespec64_to_timespec(it.it_value);
-                       if (copy_to_user(rmtp, &ts, sizeof(*rmtp)))
+#ifdef CONFIG_COMPAT
+                       if (restart->nanosleep.type == TT_COMPAT) {
+                               if (compat_put_timespec(&ts,
+                                               restart->nanosleep.compat_rmtp))
+                                       return -EFAULT;
+                       } else
+#endif
+                       if (copy_to_user(restart->nanosleep.rmtp, &ts,
+                                       sizeof(ts)))
                                return -EFAULT;
                }
                restart->nanosleep.expires = timespec64_to_ns(rqtp);
index 156a5e6f3bd2a4a38830265d91530358fb244876..749b76f2d7574e7b6a9448dcd309cdaeff9df7a1 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/ktime.h>
 #include <linux/timekeeping.h>
 #include <linux/posix-timers.h>
+#include <linux/compat.h>
 
 asmlinkage long sys_ni_posix_timers(void)
 {
@@ -110,25 +111,53 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
        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))
-                       return -EINVAL;
-               if (flags & TIMER_ABSTIME)
-                       rmtp = NULL;
-               current->restart_block.nanosleep.rmtp = rmtp;
-               return hrtimer_nanosleep(&t64, flags & TIMER_ABSTIME ?
-                                        HRTIMER_MODE_ABS : HRTIMER_MODE_REL,
-                                        which_clock);
+               break;
        default:
                return -EINVAL;
        }
+
+       if (copy_from_user(&t, rqtp, sizeof (struct timespec)))
+               return -EFAULT;
+       t64 = timespec_to_timespec64(t);
+       if (!timespec64_valid(&t64))
+               return -EINVAL;
+       if (flags & TIMER_ABSTIME)
+               rmtp = NULL;
+       current->restart_block.nanosleep.type = rmtp ? TT_NATIVE : TT_NONE;
+       current->restart_block.nanosleep.rmtp = rmtp;
+       return hrtimer_nanosleep(&t64, flags & TIMER_ABSTIME ?
+                                HRTIMER_MODE_ABS : HRTIMER_MODE_REL,
+                                which_clock);
 }
 
 #ifdef CONFIG_COMPAT
-long clock_nanosleep_restart(struct restart_block *restart_block)
+COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags,
+                      struct compat_timespec __user *, rqtp,
+                      struct compat_timespec __user *, rmtp)
 {
-       return hrtimer_nanosleep_restart(restart_block);
+       struct timespec64 t64;
+       struct timespec t;
+
+       switch (which_clock) {
+       case CLOCK_REALTIME:
+       case CLOCK_MONOTONIC:
+       case CLOCK_BOOTTIME:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (compat_get_timespec(&t, rqtp))
+               return -EFAULT;
+       t64 = timespec_to_timespec64(t);
+       if (!timespec64_valid(&t64))
+               return -EINVAL;
+       if (flags & TIMER_ABSTIME)
+               rmtp = NULL;
+       current->restart_block.nanosleep.type = rmtp ? TT_COMPAT : TT_NONE;
+       current->restart_block.nanosleep.compat_rmtp = rmtp;
+       return hrtimer_nanosleep(&t64, flags & TIMER_ABSTIME ?
+                                HRTIMER_MODE_ABS : HRTIMER_MODE_REL,
+                                which_clock);
 }
 #endif
index a3e5c01b430e6c5d2b8fcdfb40db991b3a210b33..bec86b6b981484abc0a1d1b178307921f691abc1 100644 (file)
@@ -49,6 +49,7 @@
 #include <linux/workqueue.h>
 #include <linux/export.h>
 #include <linux/hashtable.h>
+#include <linux/compat.h>
 
 #include "timekeeping.h"
 #include "posix-timers.h"
@@ -1069,25 +1070,40 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
                return -EINVAL;
        if (flags & TIMER_ABSTIME)
                rmtp = NULL;
+       current->restart_block.nanosleep.type = rmtp ? TT_NATIVE : TT_NONE;
        current->restart_block.nanosleep.rmtp = rmtp;
 
        return kc->nsleep(which_clock, flags, &t64);
 }
 
-/*
- * 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)
+#ifdef CONFIG_COMPAT
+COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags,
+                      struct compat_timespec __user *, rqtp,
+                      struct compat_timespec __user *, rmtp)
 {
-       clockid_t which_clock = restart_block->nanosleep.clockid;
        const struct k_clock *kc = clockid_to_kclock(which_clock);
+       struct timespec64 t64;
+       struct timespec t;
 
-       if (WARN_ON_ONCE(!kc || !kc->nsleep_restart))
+       if (!kc)
                return -EINVAL;
+       if (!kc->nsleep)
+               return -ENANOSLEEP_NOTSUP;
+
+       if (compat_get_timespec(&t, rqtp))
+               return -EFAULT;
 
-       return kc->nsleep_restart(restart_block);
+       t64 = timespec_to_timespec64(t);
+       if (!timespec64_valid(&t64))
+               return -EINVAL;
+       if (flags & TIMER_ABSTIME)
+               rmtp = NULL;
+       current->restart_block.nanosleep.type = rmtp ? TT_COMPAT : TT_NONE;
+       current->restart_block.nanosleep.compat_rmtp = rmtp;
+
+       return kc->nsleep(which_clock, flags, &t64);
 }
+#endif
 
 static const struct k_clock clock_realtime = {
        .clock_getres           = posix_get_hrtimer_res,