Commit edbeda46 authored by Al Viro's avatar Al Viro Committed by Thomas Gleixner

time/posix-timers: Move the compat copyouts to the nanosleep implementations

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: default avatarAl Viro <viro@zeniv.linux.org.uk>
Signed-off-by: default avatarThomas 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
parent 99e6c0e6
...@@ -110,8 +110,6 @@ void posix_cpu_timers_exit_group(struct task_struct *task); ...@@ -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, void set_process_cpu_timer(struct task_struct *task, unsigned int clock_idx,
u64 *newval, u64 *oldval); 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 update_rlimit_cpu(struct task_struct *task, unsigned long rlim_new);
void posixtimer_rearm(struct siginfo *info); void posixtimer_rearm(struct siginfo *info);
......
...@@ -11,6 +11,14 @@ struct timespec; ...@@ -11,6 +11,14 @@ struct timespec;
struct compat_timespec; struct compat_timespec;
struct pollfd; struct pollfd;
enum timespec_type {
TT_NONE = 0,
TT_NATIVE = 1,
#ifdef CONFIG_COMPAT
TT_COMPAT = 2,
#endif
};
/* /*
* System call restart block. * System call restart block.
*/ */
...@@ -29,10 +37,13 @@ struct restart_block { ...@@ -29,10 +37,13 @@ struct restart_block {
/* For nanosleep */ /* For nanosleep */
struct { struct {
clockid_t clockid; clockid_t clockid;
enum timespec_type type;
union {
struct timespec __user *rmtp; struct timespec __user *rmtp;
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
struct compat_timespec __user *compat_rmtp; struct compat_timespec __user *compat_rmtp;
#endif #endif
};
u64 expires; u64 expires;
} nanosleep; } nanosleep;
/* For poll */ /* For poll */
......
...@@ -213,82 +213,6 @@ int compat_convert_timespec(struct timespec __user **kts, ...@@ -213,82 +213,6 @@ int compat_convert_timespec(struct timespec __user **kts,
return 0; 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, static inline long get_compat_itimerval(struct itimerval *o,
struct compat_itimerval __user *i) struct compat_itimerval __user *i)
{ {
...@@ -821,61 +745,6 @@ COMPAT_SYSCALL_DEFINE2(clock_getres, clockid_t, which_clock, ...@@ -821,61 +745,6 @@ COMPAT_SYSCALL_DEFINE2(clock_getres, clockid_t, which_clock,
return err; 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 * We currently only need the following fields from the sigevent
* structure: sigev_value, sigev_signo, sig_notify and (sometimes * structure: sigev_value, sigev_signo, sig_notify and (sometimes
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/posix-timers.h> #include <linux/posix-timers.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/compat.h>
#include "posix-timers.h" #include "posix-timers.h"
...@@ -691,7 +692,7 @@ static enum alarmtimer_restart alarmtimer_nsleep_wakeup(struct alarm *alarm, ...@@ -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, static int alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp,
enum alarmtimer_type type) enum alarmtimer_type type)
{ {
struct timespec __user *rmtp; struct restart_block *restart;
alarm->data = (void *)current; alarm->data = (void *)current;
do { do {
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
...@@ -709,8 +710,8 @@ static int alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp, ...@@ -709,8 +710,8 @@ static int alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp,
if (freezing(current)) if (freezing(current))
alarmtimer_freezerset(absexp, type); alarmtimer_freezerset(absexp, type);
rmtp = current->restart_block.nanosleep.rmtp; restart = &current->restart_block;
if (rmtp) { if (restart->nanosleep.type != TT_NONE) {
struct timespec rmt; struct timespec rmt;
ktime_t rem; ktime_t rem;
...@@ -720,7 +721,14 @@ static int alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp, ...@@ -720,7 +721,14 @@ static int alarmtimer_do_nsleep(struct alarm *alarm, ktime_t absexp,
return 0; return 0;
rmt = ktime_to_timespec(rem); 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 -EFAULT;
} }
return -ERESTART_RESTARTBLOCK; return -ERESTART_RESTARTBLOCK;
......
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#include <linux/sched/debug.h> #include <linux/sched/debug.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/compat.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
...@@ -1441,7 +1442,8 @@ EXPORT_SYMBOL_GPL(hrtimer_init_sleeper); ...@@ -1441,7 +1442,8 @@ EXPORT_SYMBOL_GPL(hrtimer_init_sleeper);
static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mode) 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); hrtimer_init_sleeper(t, current);
do { do {
...@@ -1461,15 +1463,23 @@ static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mod ...@@ -1461,15 +1463,23 @@ static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mod
if (!t->task) if (!t->task)
return 0; return 0;
rmtp = current->restart_block.nanosleep.rmtp; restart = &current->restart_block;
if (rmtp) { if (restart->nanosleep.type != TT_NONE) {
struct timespec rmt;
ktime_t rem = hrtimer_expires_remaining(&t->timer); ktime_t rem = hrtimer_expires_remaining(&t->timer);
struct timespec rmt;
if (rem <= 0) if (rem <= 0)
return 0; return 0;
rmt = ktime_to_timespec(rem); 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 -EFAULT;
} }
return -ERESTART_RESTARTBLOCK; return -ERESTART_RESTARTBLOCK;
...@@ -1535,10 +1545,32 @@ SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp, ...@@ -1535,10 +1545,32 @@ SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp,
if (!timespec64_valid(&tu64)) if (!timespec64_valid(&tu64))
return -EINVAL; return -EINVAL;
current->restart_block.nanosleep.type = rmtp ? TT_NATIVE : TT_NONE;
current->restart_block.nanosleep.rmtp = rmtp; current->restart_block.nanosleep.rmtp = rmtp;
return hrtimer_nanosleep(&tu64, HRTIMER_MODE_REL, CLOCK_MONOTONIC); 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: * Functions related to boot-time initialization:
*/ */
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <trace/events/timer.h> #include <trace/events/timer.h>
#include <linux/tick.h> #include <linux/tick.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/compat.h>
#include "posix-timers.h" #include "posix-timers.h"
...@@ -1243,10 +1244,9 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags, ...@@ -1243,10 +1244,9 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags,
timer.it_process = current; timer.it_process = current;
if (!error) { if (!error) {
static struct itimerspec64 zero_it; static struct itimerspec64 zero_it;
struct restart_block *restart = &current->restart_block; struct restart_block *restart;
struct timespec __user *rmtp;
memset(&it, 0, sizeof it); memset(&it, 0, sizeof(it));
it.it_value = *rqtp; it.it_value = *rqtp;
spin_lock_irq(&timer.it_lock); spin_lock_irq(&timer.it_lock);
...@@ -1311,12 +1311,20 @@ static int do_cpu_nanosleep(const clockid_t which_clock, int flags, ...@@ -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. * Report back to the user the time still remaining.
*/ */
rmtp = restart->nanosleep.rmtp; restart = &current->restart_block;
if (rmtp) { if (restart->nanosleep.type != TT_NONE) {
struct timespec ts; struct timespec ts;
ts = timespec64_to_timespec(it.it_value); 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; return -EFAULT;
} }
restart->nanosleep.expires = timespec64_to_ns(rqtp); restart->nanosleep.expires = timespec64_to_ns(rqtp);
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/ktime.h> #include <linux/ktime.h>
#include <linux/timekeeping.h> #include <linux/timekeeping.h>
#include <linux/posix-timers.h> #include <linux/posix-timers.h>
#include <linux/compat.h>
asmlinkage long sys_ni_posix_timers(void) asmlinkage long sys_ni_posix_timers(void)
{ {
...@@ -110,6 +111,11 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags, ...@@ -110,6 +111,11 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
case CLOCK_REALTIME: case CLOCK_REALTIME:
case CLOCK_MONOTONIC: case CLOCK_MONOTONIC:
case CLOCK_BOOTTIME: case CLOCK_BOOTTIME:
break;
default:
return -EINVAL;
}
if (copy_from_user(&t, rqtp, sizeof (struct timespec))) if (copy_from_user(&t, rqtp, sizeof (struct timespec)))
return -EFAULT; return -EFAULT;
t64 = timespec_to_timespec64(t); t64 = timespec_to_timespec64(t);
...@@ -117,18 +123,41 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags, ...@@ -117,18 +123,41 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
return -EINVAL; return -EINVAL;
if (flags & TIMER_ABSTIME) if (flags & TIMER_ABSTIME)
rmtp = NULL; rmtp = NULL;
current->restart_block.nanosleep.type = rmtp ? TT_NATIVE : TT_NONE;
current->restart_block.nanosleep.rmtp = rmtp; current->restart_block.nanosleep.rmtp = rmtp;
return hrtimer_nanosleep(&t64, flags & TIMER_ABSTIME ? return hrtimer_nanosleep(&t64, flags & TIMER_ABSTIME ?
HRTIMER_MODE_ABS : HRTIMER_MODE_REL, HRTIMER_MODE_ABS : HRTIMER_MODE_REL,
which_clock); which_clock);
default:
return -EINVAL;
}
} }
#ifdef CONFIG_COMPAT #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 #endif
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/hashtable.h> #include <linux/hashtable.h>
#include <linux/compat.h>
#include "timekeeping.h" #include "timekeeping.h"
#include "posix-timers.h" #include "posix-timers.h"
...@@ -1069,25 +1070,40 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags, ...@@ -1069,25 +1070,40 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
return -EINVAL; return -EINVAL;
if (flags & TIMER_ABSTIME) if (flags & TIMER_ABSTIME)
rmtp = NULL; rmtp = NULL;
current->restart_block.nanosleep.type = rmtp ? TT_NATIVE : TT_NONE;
current->restart_block.nanosleep.rmtp = rmtp; current->restart_block.nanosleep.rmtp = rmtp;
return kc->nsleep(which_clock, flags, &t64); return kc->nsleep(which_clock, flags, &t64);
} }
/* #ifdef CONFIG_COMPAT
* This will restart clock_nanosleep. This is required only by COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags,
* compat_clock_nanosleep_restart for now. struct compat_timespec __user *, rqtp,
*/ struct compat_timespec __user *, rmtp)
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); 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; 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 = { static const struct k_clock clock_realtime = {
.clock_getres = posix_get_hrtimer_res, .clock_getres = posix_get_hrtimer_res,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment