A process can be marked for death by setting SIGNAL_GROUP_EXIT and group_exit_code, long before do_exit is called. Unfortunately because of PTRACE_EVENT_EXIT residing in do_exit this same tactic can not be used for task death. Correct this by adding a new task field task->ptrace_code that holds the code for ptrace stops. This allows task->exit_code to be set to the exit code long before the PTRACE_EVENT_EXIT ptrace stop. Signed-off-by: "Eric W. Biederman" <ebiederm@xxxxxxxxxxxx> --- fs/proc/array.c | 3 +++ include/linux/sched.h | 1 + kernel/exit.c | 2 +- kernel/ptrace.c | 12 ++++++------ kernel/signal.c | 18 +++++++++--------- 5 files changed, 20 insertions(+), 16 deletions(-) diff --git a/fs/proc/array.c b/fs/proc/array.c index 43a7abde9e42..3042015c11ad 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -519,6 +519,9 @@ static int do_task_stat(struct seq_file *m, struct pid_namespace *ns, cgtime = sig->cgtime; rsslim = READ_ONCE(sig->rlim[RLIMIT_RSS].rlim_cur); + if (task_is_traced(task) && !(task->jobctl & JOBCTL_LISTENING)) + exit_code = task->ptrace_code; + /* add up live thread stats at the group level */ if (whole) { struct task_struct *t = task; diff --git a/include/linux/sched.h b/include/linux/sched.h index 52f2fdffa3ab..c3d732bf7833 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1174,6 +1174,7 @@ struct task_struct { /* Ptrace state: */ unsigned long ptrace_message; kernel_siginfo_t *last_siginfo; + int ptrace_code; struct task_io_accounting ioac; #ifdef CONFIG_PSI diff --git a/kernel/exit.c b/kernel/exit.c index 7121db37c411..aedefe5eb0eb 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -1134,7 +1134,7 @@ static int *task_stopped_code(struct task_struct *p, bool ptrace) { if (ptrace) { if (task_is_traced(p) && !(p->jobctl & JOBCTL_LISTENING)) - return &p->exit_code; + return &p->ptrace_code; } else { if (p->signal->flags & SIGNAL_STOP_STOPPED) return &p->signal->group_exit_code; diff --git a/kernel/ptrace.c b/kernel/ptrace.c index eea265082e97..8bbd73ab9a34 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -172,7 +172,7 @@ void __ptrace_unlink(struct task_struct *child) static bool looks_like_a_spurious_pid(struct task_struct *task) { - if (task->exit_code != ((PTRACE_EVENT_EXEC << 8) | SIGTRAP)) + if (task->ptrace_code != ((PTRACE_EVENT_EXEC << 8) | SIGTRAP)) return false; if (task_pid_vnr(task) == task->ptrace_message) @@ -573,7 +573,7 @@ static int ptrace_detach(struct task_struct *child, unsigned int data) * tasklist_lock avoids the race with wait_task_stopped(), see * the comment in ptrace_resume(). */ - child->exit_code = data; + child->ptrace_code = data; __ptrace_detach(current, child); write_unlock_irq(&tasklist_lock); @@ -863,11 +863,11 @@ static int ptrace_resume(struct task_struct *child, long request, } /* - * Change ->exit_code and ->state under siglock to avoid the race - * with wait_task_stopped() in between; a non-zero ->exit_code will + * Change ->ptrace_code and ->state under siglock to avoid the race + * with wait_task_stopped() in between; a non-zero ->ptrace_code will * wrongly look like another report from tracee. * - * Note that we need siglock even if ->exit_code == data and/or this + * Note that we need siglock even if ->ptrace_code == data and/or this * status was not reported yet, the new status must not be cleared by * wait_task_stopped() after resume. * @@ -878,7 +878,7 @@ static int ptrace_resume(struct task_struct *child, long request, need_siglock = data && !thread_group_empty(current); if (need_siglock) spin_lock_irq(&child->sighand->siglock); - child->exit_code = data; + child->ptrace_code = data; wake_up_state(child, __TASK_TRACED); if (need_siglock) spin_unlock_irq(&child->sighand->siglock); diff --git a/kernel/signal.c b/kernel/signal.c index 9903ff12e581..fd3c404de8b6 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -2168,7 +2168,7 @@ static void do_notify_parent_cldstop(struct task_struct *tsk, info.si_status = tsk->signal->group_exit_code & 0x7f; break; case CLD_TRAPPED: - info.si_status = tsk->exit_code & 0x7f; + info.si_status = tsk->ptrace_code & 0x7f; break; default: BUG(); @@ -2198,7 +2198,7 @@ static void do_notify_parent_cldstop(struct task_struct *tsk, * with. If the code did not stop because the tracer is gone, * the stop signal remains unchanged unless clear_code. */ -static int ptrace_stop(int exit_code, int why, int clear_code, +static int ptrace_stop(int code, int why, int clear_code, unsigned long message, kernel_siginfo_t *info) __releases(¤t->sighand->siglock) __acquires(¤t->sighand->siglock) @@ -2248,7 +2248,7 @@ static int ptrace_stop(int exit_code, int why, int clear_code, current->ptrace_message = message; current->last_siginfo = info; - current->exit_code = exit_code; + current->ptrace_code = code; /* * If @why is CLD_STOPPED, we're trapping to participate in a group @@ -2315,7 +2315,7 @@ static int ptrace_stop(int exit_code, int why, int clear_code, __set_current_state(TASK_RUNNING); read_code = false; if (clear_code) - exit_code = 0; + code = 0; read_unlock(&tasklist_lock); } @@ -2325,10 +2325,10 @@ static int 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 = current->exit_code; + if (read_code) code = current->ptrace_code; current->last_siginfo = NULL; current->ptrace_message = 0; - current->exit_code = 0; + current->ptrace_code = 0; /* LISTENING can be set only during STOP traps, clear it */ current->jobctl &= ~JOBCTL_LISTENING; @@ -2339,7 +2339,7 @@ static int 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; + return code; } static int ptrace_do_notify(int signr, int exit_code, int why, unsigned long message) @@ -2501,11 +2501,11 @@ static bool do_signal_stop(int signr) * * When PT_SEIZED, it's used for both group stop and explicit * SEIZE/INTERRUPT traps. Both generate PTRACE_EVENT_STOP trap with - * accompanying siginfo. If stopped, lower eight bits of exit_code contain + * accompanying siginfo. If stopped, lower eight bits of ptrace_code contain * the stop signal; otherwise, %SIGTRAP. * * When !PT_SEIZED, it's used only for group stop trap with stop signal - * number as exit_code and no siginfo. + * number as ptrace_code and no siginfo. * * CONTEXT: * Must be called with @current->sighand->siglock held, which may be -- 2.29.2