diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2022-01-27 21:15:32 +0300 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2022-03-18 17:44:19 +0300 |
commit | 336d4b814bf078fa698488632c19beca47308896 (patch) | |
tree | 9e757560447836890ef250488bfc899e55da1423 /kernel/signal.c | |
parent | 355f841a3f8ca980c9682937a5257d3a1f6fc09d (diff) | |
download | linux-336d4b814bf078fa698488632c19beca47308896.tar.xz |
ptrace: Move setting/clearing ptrace_message into ptrace_stop
Today ptrace_message is easy to overlook as it not a core part of
ptrace_stop. It has been overlooked so much that there are places
that set ptrace_message and don't clear it, and places that never set
it. So if you get an unlucky sequence of events the ptracer may be
able to read a ptrace_message that does not apply to the current
ptrace stop.
Move setting of ptrace_message into ptrace_stop so that it always gets
set before the stop, and always gets cleared after the stop. This
prevents non-sense from being reported to userspace and makes
ptrace_message more visible in the ptrace helper functions so that
kernel developers can see it.
Link: https://lkml.kernel.org/r/87bky67qfv.fsf_-_@email.froward.int.ebiederm.org
Acked-by: Oleg Nesterov <oleg@redhat.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Diffstat (limited to 'kernel/signal.c')
-rw-r--r-- | kernel/signal.c | 21 |
1 files changed, 12 insertions, 9 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index c2dee5420567..a49ac7149256 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2191,7 +2191,8 @@ static void do_notify_parent_cldstop(struct task_struct *tsk, * If we actually decide not to stop at all because the tracer * is gone, we keep current->exit_code unless clear_code. */ -static void ptrace_stop(int exit_code, int why, int clear_code, kernel_siginfo_t *info) +static void ptrace_stop(int exit_code, int why, int clear_code, + unsigned long message, kernel_siginfo_t *info) __releases(¤t->sighand->siglock) __acquires(¤t->sighand->siglock) { @@ -2237,6 +2238,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, kernel_siginfo_t */ smp_wmb(); + current->ptrace_message = message; current->last_siginfo = info; current->exit_code = exit_code; @@ -2315,6 +2317,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, kernel_siginfo_t */ spin_lock_irq(¤t->sighand->siglock); current->last_siginfo = NULL; + current->ptrace_message = 0; /* LISTENING can be set only during STOP traps, clear it */ current->jobctl &= ~JOBCTL_LISTENING; @@ -2327,7 +2330,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, kernel_siginfo_t recalc_sigpending_tsk(current); } -static void ptrace_do_notify(int signr, int exit_code, int why) +static void ptrace_do_notify(int signr, int exit_code, int why, unsigned long message) { kernel_siginfo_t info; @@ -2338,17 +2341,17 @@ static void ptrace_do_notify(int signr, int exit_code, int why) info.si_uid = from_kuid_munged(current_user_ns(), current_uid()); /* Let the debugger run. */ - ptrace_stop(exit_code, why, 1, &info); + ptrace_stop(exit_code, why, 1, message, &info); } -void ptrace_notify(int exit_code) +void ptrace_notify(int exit_code, unsigned long message) { BUG_ON((exit_code & (0x7f | ~0xffff)) != SIGTRAP); if (unlikely(task_work_pending(current))) task_work_run(); spin_lock_irq(¤t->sighand->siglock); - ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED); + ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED, message); spin_unlock_irq(¤t->sighand->siglock); } @@ -2504,10 +2507,10 @@ static void do_jobctl_trap(void) signr = SIGTRAP; WARN_ON_ONCE(!signr); ptrace_do_notify(signr, signr | (PTRACE_EVENT_STOP << 8), - CLD_STOPPED); + CLD_STOPPED, 0); } else { WARN_ON_ONCE(!signr); - ptrace_stop(signr, CLD_STOPPED, 0, NULL); + ptrace_stop(signr, CLD_STOPPED, 0, 0, NULL); current->exit_code = 0; } } @@ -2561,7 +2564,7 @@ static int ptrace_signal(int signr, kernel_siginfo_t *info, enum pid_type type) * comment in dequeue_signal(). */ current->jobctl |= JOBCTL_STOP_DEQUEUED; - ptrace_stop(signr, CLD_TRAPPED, 0, info); + ptrace_stop(signr, CLD_TRAPPED, 0, 0, info); /* We're back. Did the debugger cancel the sig? */ signr = current->exit_code; @@ -2891,7 +2894,7 @@ static void signal_delivered(struct ksignal *ksig, int stepping) if (current->sas_ss_flags & SS_AUTODISARM) sas_ss_reset(current); if (stepping) - ptrace_notify(SIGTRAP); + ptrace_notify(SIGTRAP, 0); } void signal_setup_done(int failed, struct ksignal *ksig, int stepping) |