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: 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
This commit is contained in:
Родитель
99e6c0e6ec
Коммит
edbeda4632
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
131
kernel/compat.c
131
kernel/compat.c
|
@ -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 = ¤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)
|
||||
{
|
||||
|
@ -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 = ¤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
|
||||
|
|
|
@ -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 = ¤t->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;
|
||||
|
|
|
@ -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 = ¤t->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:
|
||||
*/
|
||||
|
|
|
@ -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 = ¤t->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 = ¤t->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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
return kc->nsleep_restart(restart_block);
|
||||
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 kc->nsleep(which_clock, flags, &t64);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct k_clock clock_realtime = {
|
||||
.clock_getres = posix_get_hrtimer_res,
|
||||
|
|
Загрузка…
Ссылка в новой задаче