Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -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);
+13 −2
Original line number Diff line number Diff line
@@ -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;
			enum timespec_type type;
			union {
				struct timespec __user *rmtp;
#ifdef CONFIG_COMPAT
				struct compat_timespec __user *compat_rmtp;
#endif
			};
			u64 expires;
		} nanosleep;
		/* For poll */
+0 −131
Original line number Diff line number Diff line
@@ -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
+12 −4
Original line number Diff line number Diff line
@@ -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;
+37 −5
Original line number Diff line number Diff line
@@ -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:
 */
Loading