]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
timers: Prevent union confusion from unexpected restart_syscall()
authorJann Horn <jannh@google.com>
Thu, 5 Jan 2023 13:44:03 +0000 (14:44 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 11 Mar 2023 15:26:41 +0000 (16:26 +0100)
[ Upstream commit 9f76d59173d9d146e96c66886b671c1915a5c5e5 ]

The nanosleep syscalls use the restart_block mechanism, with a quirk:
The `type` and `rmtp`/`compat_rmtp` fields are set up unconditionally on
syscall entry, while the rest of the restart_block is only set up in the
unlikely case that the syscall is actually interrupted by a signal (or
pseudo-signal) that doesn't have a signal handler.

If the restart_block was set up by a previous syscall (futex(...,
FUTEX_WAIT, ...) or poll()) and hasn't been invalidated somehow since then,
this will clobber some of the union fields used by futex_wait_restart() and
do_restart_poll().

If userspace afterwards wrongly calls the restart_syscall syscall,
futex_wait_restart()/do_restart_poll() will read struct fields that have
been clobbered.

This doesn't actually lead to anything particularly interesting because
none of the union fields contain trusted kernel data, and
futex(..., FUTEX_WAIT, ...) and poll() aren't syscalls where it makes much
sense to apply seccomp filters to their arguments.

So the current consequences are just of the "if userspace does bad stuff,
it can damage itself, and that's not a problem" flavor.

But still, it seems like a hazard for future developers, so invalidate the
restart_block when partly setting it up in the nanosleep syscalls.

Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/r/20230105134403.754986-1-jannh@google.com
Signed-off-by: Sasha Levin <sashal@kernel.org>
kernel/time/hrtimer.c
kernel/time/posix-stubs.c
kernel/time/posix-timers.c

index 94dd37e8890d86df213114775ad39321a8234e92..7a84c54219f353ae79fec769f09654ec694cb5ae 100644 (file)
@@ -1564,6 +1564,7 @@ SYSCALL_DEFINE2(nanosleep, struct timespec __user *, rqtp,
        if (!timespec64_valid(&tu))
                return -EINVAL;
 
+       current->restart_block.fn = do_no_restart_syscall;
        current->restart_block.nanosleep.type = rmtp ? TT_NATIVE : TT_NONE;
        current->restart_block.nanosleep.rmtp = rmtp;
        return hrtimer_nanosleep(&tu, HRTIMER_MODE_REL, CLOCK_MONOTONIC);
@@ -1582,6 +1583,7 @@ COMPAT_SYSCALL_DEFINE2(nanosleep, struct compat_timespec __user *, rqtp,
        if (!timespec64_valid(&tu))
                return -EINVAL;
 
+       current->restart_block.fn = do_no_restart_syscall;
        current->restart_block.nanosleep.type = rmtp ? TT_COMPAT : TT_NONE;
        current->restart_block.nanosleep.compat_rmtp = rmtp;
        return hrtimer_nanosleep(&tu, HRTIMER_MODE_REL, CLOCK_MONOTONIC);
index 06f34feb635ed4ddb4b736f6e9ece10f2686d4f6..20117340c2493a02b9c511b69a0b182618019fc8 100644 (file)
@@ -136,6 +136,7 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
                return -EINVAL;
        if (flags & TIMER_ABSTIME)
                rmtp = NULL;
+       current->restart_block.fn = do_no_restart_syscall;
        current->restart_block.nanosleep.type = rmtp ? TT_NATIVE : TT_NONE;
        current->restart_block.nanosleep.rmtp = rmtp;
        return hrtimer_nanosleep(&t64, flags & TIMER_ABSTIME ?
@@ -222,6 +223,7 @@ COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags,
                return -EINVAL;
        if (flags & TIMER_ABSTIME)
                rmtp = NULL;
+       current->restart_block.fn = do_no_restart_syscall;
        current->restart_block.nanosleep.type = rmtp ? TT_COMPAT : TT_NONE;
        current->restart_block.nanosleep.compat_rmtp = rmtp;
        return hrtimer_nanosleep(&t64, flags & TIMER_ABSTIME ?
index f46694850b445e632418a28785d0d126fb26c20e..8b90abd6907308d0c9e69a3926c271ae5819dfba 100644 (file)
@@ -1227,6 +1227,7 @@ SYSCALL_DEFINE4(clock_nanosleep, const clockid_t, which_clock, int, flags,
                return -EINVAL;
        if (flags & TIMER_ABSTIME)
                rmtp = NULL;
+       current->restart_block.fn = do_no_restart_syscall;
        current->restart_block.nanosleep.type = rmtp ? TT_NATIVE : TT_NONE;
        current->restart_block.nanosleep.rmtp = rmtp;
 
@@ -1253,6 +1254,7 @@ COMPAT_SYSCALL_DEFINE4(clock_nanosleep, clockid_t, which_clock, int, flags,
                return -EINVAL;
        if (flags & TIMER_ABSTIME)
                rmtp = NULL;
+       current->restart_block.fn = do_no_restart_syscall;
        current->restart_block.nanosleep.type = rmtp ? TT_COMPAT : TT_NONE;
        current->restart_block.nanosleep.compat_rmtp = rmtp;