From nobody Mon Jun 22 22:47:01 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id ABE80C433F5 for ; Tue, 15 Mar 2022 23:22:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S245103AbiCOXXZ (ORCPT ); Tue, 15 Mar 2022 19:23:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39518 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236358AbiCOXXW (ORCPT ); Tue, 15 Mar 2022 19:23:22 -0400 Received: from out01.mta.xmission.com (out01.mta.xmission.com [166.70.13.231]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 98FB82B1AD; Tue, 15 Mar 2022 16:22:09 -0700 (PDT) Received: from in01.mta.xmission.com ([166.70.13.51]:52822) by out01.mta.xmission.com with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.93) (envelope-from ) id 1nUGUS-005BCm-MN; Tue, 15 Mar 2022 17:22:08 -0600 Received: from ip68-227-174-4.om.om.cox.net ([68.227.174.4]:37860 helo=email.froward.int.ebiederm.org.xmission.com) by in01.mta.xmission.com with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.93) (envelope-from ) id 1nUGUQ-009ET2-FY; Tue, 15 Mar 2022 17:22:08 -0600 From: "Eric W. Biederman" To: Cc: Linus Torvalds , Alexey Gladkov , Kyle Huey , Oleg Nesterov , Kees Cook , Al Viro , , Jens Axboe References: <87a6ha4zsd.fsf@email.froward.int.ebiederm.org> <87bl1kunjj.fsf@email.froward.int.ebiederm.org> <87r19opkx1.fsf_-_@email.froward.int.ebiederm.org> <87o82gdlu9.fsf_-_@email.froward.int.ebiederm.org> <87tubyx0rg.fsf_-_@email.froward.int.ebiederm.org> Date: Tue, 15 Mar 2022 18:21:08 -0500 In-Reply-To: <87tubyx0rg.fsf_-_@email.froward.int.ebiederm.org> (Eric W. Biederman's message of "Tue, 15 Mar 2022 18:18:59 -0500") Message-ID: <87bky67qfv.fsf_-_@email.froward.int.ebiederm.org> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 X-XM-SPF: eid=1nUGUQ-009ET2-FY;;;mid=<87bky67qfv.fsf_-_@email.froward.int.ebiederm.org>;;;hst=in01.mta.xmission.com;;;ip=68.227.174.4;;;frm=ebiederm@xmission.com;;;spf=neutral X-XM-AID: U2FsdGVkX18ENc95f7uuEALRb2HBI4YzCX++Cz4s214= X-SA-Exim-Connect-IP: 68.227.174.4 X-SA-Exim-Mail-From: ebiederm@xmission.com Subject: [PATCH 1/2] ptrace: Move setting/clearing ptrace_message into ptrace_stop X-SA-Exim-Version: 4.2.1 (built Sat, 08 Feb 2020 21:53:50 +0000) X-SA-Exim-Scanned: Yes (on in01.mta.xmission.com) Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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. Signed-off-by: "Eric W. Biederman" Acked-by: Oleg Nesterov Reviewed-by: Kees Cook --- include/linux/ptrace.h | 9 +++------ include/uapi/linux/ptrace.h | 2 +- kernel/signal.c | 21 ++++++++++++--------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 5310f43e4762..3e6b46e2b7be 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -60,7 +60,7 @@ extern int ptrace_writedata(struct task_struct *tsk, char= __user *src, unsigned extern void ptrace_disable(struct task_struct *); extern int ptrace_request(struct task_struct *child, long request, unsigned long addr, unsigned long data); -extern void ptrace_notify(int exit_code); +extern void ptrace_notify(int exit_code, unsigned long message); extern void __ptrace_link(struct task_struct *child, struct task_struct *new_parent, const struct cred *ptracer_cred); @@ -155,8 +155,7 @@ static inline bool ptrace_event_enabled(struct task_str= uct *task, int event) static inline void ptrace_event(int event, unsigned long message) { if (unlikely(ptrace_event_enabled(current, event))) { - current->ptrace_message =3D message; - ptrace_notify((event << 8) | SIGTRAP); + ptrace_notify((event << 8) | SIGTRAP, message); } else if (event =3D=3D PTRACE_EVENT_EXEC) { /* legacy EXEC report via SIGTRAP */ if ((current->ptrace & (PT_PTRACED|PT_SEIZED)) =3D=3D PT_PTRACED) @@ -424,8 +423,7 @@ static inline int ptrace_report_syscall(unsigned long m= essage) if (!(ptrace & PT_PTRACED)) return 0; =20 - current->ptrace_message =3D message; - ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0)); + ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0), message); =20 /* * this isn't the same as continuing with a signal, but it will do @@ -437,7 +435,6 @@ static inline int ptrace_report_syscall(unsigned long m= essage) current->exit_code =3D 0; } =20 - current->ptrace_message =3D 0; return fatal_signal_pending(current); } =20 diff --git a/include/uapi/linux/ptrace.h b/include/uapi/linux/ptrace.h index b7af92e07d1f..195ae64a8c87 100644 --- a/include/uapi/linux/ptrace.h +++ b/include/uapi/linux/ptrace.h @@ -114,7 +114,7 @@ struct ptrace_rseq_configuration { =20 /* * These values are stored in task->ptrace_message - * by ptrace_report_syscall_* to describe the current syscall-stop. + * by ptrace_stop to describe the current syscall-stop. */ #define PTRACE_EVENTMSG_SYSCALL_ENTRY 1 #define PTRACE_EVENTMSG_SYSCALL_EXIT 2 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_stru= ct *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_sig= info_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 c= lear_code, kernel_siginfo_t */ smp_wmb(); =20 + current->ptrace_message =3D message; current->last_siginfo =3D info; current->exit_code =3D exit_code; =20 @@ -2315,6 +2317,7 @@ static void ptrace_stop(int exit_code, int why, int c= lear_code, kernel_siginfo_t */ spin_lock_irq(¤t->sighand->siglock); current->last_siginfo =3D NULL; + current->ptrace_message =3D 0; =20 /* LISTENING can be set only during STOP traps, clear it */ current->jobctl &=3D ~JOBCTL_LISTENING; @@ -2327,7 +2330,7 @@ static void ptrace_stop(int exit_code, int why, int c= lear_code, kernel_siginfo_t recalc_sigpending_tsk(current); } =20 -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 l= ong message) { kernel_siginfo_t info; =20 @@ -2338,17 +2341,17 @@ static void ptrace_do_notify(int signr, int exit_co= de, int why) info.si_uid =3D from_kuid_munged(current_user_ns(), current_uid()); =20 /* Let the debugger run. */ - ptrace_stop(exit_code, why, 1, &info); + ptrace_stop(exit_code, why, 1, message, &info); } =20 -void ptrace_notify(int exit_code) +void ptrace_notify(int exit_code, unsigned long message) { BUG_ON((exit_code & (0x7f | ~0xffff)) !=3D SIGTRAP); if (unlikely(task_work_pending(current))) task_work_run(); =20 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); } =20 @@ -2504,10 +2507,10 @@ static void do_jobctl_trap(void) signr =3D 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 =3D 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 |=3D JOBCTL_STOP_DEQUEUED; - ptrace_stop(signr, CLD_TRAPPED, 0, info); + ptrace_stop(signr, CLD_TRAPPED, 0, 0, info); =20 /* We're back. Did the debugger cancel the sig? */ signr =3D current->exit_code; @@ -2891,7 +2894,7 @@ static void signal_delivered(struct ksignal *ksig, in= t stepping) if (current->sas_ss_flags & SS_AUTODISARM) sas_ss_reset(current); if (stepping) - ptrace_notify(SIGTRAP); + ptrace_notify(SIGTRAP, 0); } =20 void signal_setup_done(int failed, struct ksignal *ksig, int stepping) --=20 2.29.2 From nobody Mon Jun 22 22:47:01 2026 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3DBD6C433F5 for ; Tue, 15 Mar 2022 23:22:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1352563AbiCOXXu (ORCPT ); Tue, 15 Mar 2022 19:23:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:39814 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S236358AbiCOXXs (ORCPT ); Tue, 15 Mar 2022 19:23:48 -0400 Received: from out02.mta.xmission.com (out02.mta.xmission.com [166.70.13.232]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1AF7E5D66B; Tue, 15 Mar 2022 16:22:36 -0700 (PDT) Received: from in02.mta.xmission.com ([166.70.13.52]:53612) by out02.mta.xmission.com with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.93) (envelope-from ) id 1nUGUt-009u8G-8K; Tue, 15 Mar 2022 17:22:35 -0600 Received: from ip68-227-174-4.om.om.cox.net ([68.227.174.4]:37864 helo=email.froward.int.ebiederm.org.xmission.com) by in02.mta.xmission.com with esmtpsa (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.93) (envelope-from ) id 1nUGUr-00EZK3-3o; Tue, 15 Mar 2022 17:22:34 -0600 From: "Eric W. Biederman" To: Cc: Linus Torvalds , Alexey Gladkov , Kyle Huey , Oleg Nesterov , Kees Cook , Al Viro , , Jens Axboe References: <87a6ha4zsd.fsf@email.froward.int.ebiederm.org> <87bl1kunjj.fsf@email.froward.int.ebiederm.org> <87r19opkx1.fsf_-_@email.froward.int.ebiederm.org> <87o82gdlu9.fsf_-_@email.froward.int.ebiederm.org> <87tubyx0rg.fsf_-_@email.froward.int.ebiederm.org> Date: Tue, 15 Mar 2022 18:22:26 -0500 In-Reply-To: <87tubyx0rg.fsf_-_@email.froward.int.ebiederm.org> (Eric W. Biederman's message of "Tue, 15 Mar 2022 18:18:59 -0500") Message-ID: <875yoe7qdp.fsf_-_@email.froward.int.ebiederm.org> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 X-XM-SPF: eid=1nUGUr-00EZK3-3o;;;mid=<875yoe7qdp.fsf_-_@email.froward.int.ebiederm.org>;;;hst=in02.mta.xmission.com;;;ip=68.227.174.4;;;frm=ebiederm@xmission.com;;;spf=neutral X-XM-AID: U2FsdGVkX18bmJhKt0zs1ZeySq/C8PKKAxniIbz05Ho= X-SA-Exim-Connect-IP: 68.227.174.4 X-SA-Exim-Mail-From: ebiederm@xmission.com Subject: [PATCH 2/2] ptrace: Return the signal to continue with from ptrace_stop X-SA-Exim-Version: 4.2.1 (built Sat, 08 Feb 2020 21:53:50 +0000) X-SA-Exim-Scanned: Yes (on in02.mta.xmission.com) Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The signal a task should continue with after a ptrace stop is inconsistently read, cleared, and sent. Solve this by reading and clearing the signal to be sent in ptrace_stop. In an ideal world everything except ptrace_signal would share a common implementation of continuing with the signal, so ptracers could count on the signal they ask to continue with actually being delivered. For now retain bug compatibility and just return with the signal number the ptracer requested the code continue with. Signed-off-by: "Eric W. Biederman" Acked-by: Oleg Nesterov Reviewed-by: Kees Cook --- include/linux/ptrace.h | 12 ++++++------ kernel/signal.c | 31 ++++++++++++++++++------------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 3e6b46e2b7be..15b3d176b6b4 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -60,7 +60,7 @@ extern int ptrace_writedata(struct task_struct *tsk, char= __user *src, unsigned extern void ptrace_disable(struct task_struct *); extern int ptrace_request(struct task_struct *child, long request, unsigned long addr, unsigned long data); -extern void ptrace_notify(int exit_code, unsigned long message); +extern int ptrace_notify(int exit_code, unsigned long message); extern void __ptrace_link(struct task_struct *child, struct task_struct *new_parent, const struct cred *ptracer_cred); @@ -419,21 +419,21 @@ extern void sigaction_compat_abi(struct k_sigaction *= act, struct k_sigaction *oa static inline int ptrace_report_syscall(unsigned long message) { int ptrace =3D current->ptrace; + int signr; =20 if (!(ptrace & PT_PTRACED)) return 0; =20 - ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0), message); + signr =3D ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0), + message); =20 /* * this isn't the same as continuing with a signal, but it will do * for normal use. strace only continues with a signal if the * stopping signal is not SIGTRAP. -brl */ - if (current->exit_code) { - send_sig(current->exit_code, current, 1); - current->exit_code =3D 0; - } + if (signr) + send_sig(signr, current, 1); =20 return fatal_signal_pending(current); } diff --git a/kernel/signal.c b/kernel/signal.c index a49ac7149256..e53ee84b9021 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2188,15 +2188,17 @@ static void do_notify_parent_cldstop(struct task_st= ruct *tsk, * That makes it a way to test a stopped process for * being ptrace-stopped vs being job-control-stopped. * - * If we actually decide not to stop at all because the tracer - * is gone, we keep current->exit_code unless clear_code. + * Returns the signal the ptracer requested the code resume + * with. If the code did not stop because the tracer is gone, + * the stop signal remains unchanged unless clear_code. */ -static void ptrace_stop(int exit_code, int why, int clear_code, +static int 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) { bool gstop_done =3D false; + bool read_code =3D true; =20 if (arch_ptrace_stop_needed()) { /* @@ -2305,8 +2307,9 @@ static void ptrace_stop(int exit_code, int why, int c= lear_code, =20 /* tasklist protects us from ptrace_freeze_traced() */ __set_current_state(TASK_RUNNING); + read_code =3D false; if (clear_code) - current->exit_code =3D 0; + exit_code =3D 0; read_unlock(&tasklist_lock); } =20 @@ -2316,8 +2319,10 @@ static void ptrace_stop(int exit_code, int why, int = clear_code, * any signal-sending on another CPU that wants to examine it. */ spin_lock_irq(¤t->sighand->siglock); + if (read_code) exit_code =3D current->exit_code; current->last_siginfo =3D NULL; current->ptrace_message =3D 0; + current->exit_code =3D 0; =20 /* LISTENING can be set only during STOP traps, clear it */ current->jobctl &=3D ~JOBCTL_LISTENING; @@ -2328,9 +2333,10 @@ static void ptrace_stop(int exit_code, int why, int = clear_code, * This sets TIF_SIGPENDING, but never clears it. */ recalc_sigpending_tsk(current); + return exit_code; } =20 -static void ptrace_do_notify(int signr, int exit_code, int why, unsigned l= ong message) +static int ptrace_do_notify(int signr, int exit_code, int why, unsigned lo= ng message) { kernel_siginfo_t info; =20 @@ -2341,18 +2347,21 @@ static void ptrace_do_notify(int signr, int exit_co= de, int why, unsigned long me info.si_uid =3D from_kuid_munged(current_user_ns(), current_uid()); =20 /* Let the debugger run. */ - ptrace_stop(exit_code, why, 1, message, &info); + return ptrace_stop(exit_code, why, 1, message, &info); } =20 -void ptrace_notify(int exit_code, unsigned long message) +int ptrace_notify(int exit_code, unsigned long message) { + int signr; + BUG_ON((exit_code & (0x7f | ~0xffff)) !=3D SIGTRAP); if (unlikely(task_work_pending(current))) task_work_run(); =20 spin_lock_irq(¤t->sighand->siglock); - ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED, message); + signr =3D ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED, message); spin_unlock_irq(¤t->sighand->siglock); + return signr; } =20 /** @@ -2511,7 +2520,6 @@ static void do_jobctl_trap(void) } else { WARN_ON_ONCE(!signr); ptrace_stop(signr, CLD_STOPPED, 0, 0, NULL); - current->exit_code =3D 0; } } =20 @@ -2564,15 +2572,12 @@ static int ptrace_signal(int signr, kernel_siginfo_= t *info, enum pid_type type) * comment in dequeue_signal(). */ current->jobctl |=3D JOBCTL_STOP_DEQUEUED; - ptrace_stop(signr, CLD_TRAPPED, 0, 0, info); + signr =3D ptrace_stop(signr, CLD_TRAPPED, 0, 0, info); =20 /* We're back. Did the debugger cancel the sig? */ - signr =3D current->exit_code; if (signr =3D=3D 0) return signr; =20 - current->exit_code =3D 0; - /* * Update the siginfo structure if the signal has * changed. If the debugger wanted something --=20 2.29.2