Skip to content

Commit 6487d1d

Browse files
committed
ptrace: Return the signal to continue with from ptrace_stop
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. Link: https://lkml.kernel.org/r/875yoe7qdp.fsf_-_@email.froward.int.ebiederm.org Reviewed-by: Kees Cook <keescook@chromium.org> Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
1 parent 336d4b8 commit 6487d1d

File tree

2 files changed

+25
-19
lines changed

2 files changed

+25
-19
lines changed

include/linux/ptrace.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ extern int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned
6060
extern void ptrace_disable(struct task_struct *);
6161
extern int ptrace_request(struct task_struct *child, long request,
6262
unsigned long addr, unsigned long data);
63-
extern void ptrace_notify(int exit_code, unsigned long message);
63+
extern int ptrace_notify(int exit_code, unsigned long message);
6464
extern void __ptrace_link(struct task_struct *child,
6565
struct task_struct *new_parent,
6666
const struct cred *ptracer_cred);
@@ -419,21 +419,21 @@ extern void sigaction_compat_abi(struct k_sigaction *act, struct k_sigaction *oa
419419
static inline int ptrace_report_syscall(unsigned long message)
420420
{
421421
int ptrace = current->ptrace;
422+
int signr;
422423

423424
if (!(ptrace & PT_PTRACED))
424425
return 0;
425426

426-
ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0), message);
427+
signr = ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0),
428+
message);
427429

428430
/*
429431
* this isn't the same as continuing with a signal, but it will do
430432
* for normal use. strace only continues with a signal if the
431433
* stopping signal is not SIGTRAP. -brl
432434
*/
433-
if (current->exit_code) {
434-
send_sig(current->exit_code, current, 1);
435-
current->exit_code = 0;
436-
}
435+
if (signr)
436+
send_sig(signr, current, 1);
437437

438438
return fatal_signal_pending(current);
439439
}

kernel/signal.c

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2188,15 +2188,17 @@ static void do_notify_parent_cldstop(struct task_struct *tsk,
21882188
* That makes it a way to test a stopped process for
21892189
* being ptrace-stopped vs being job-control-stopped.
21902190
*
2191-
* If we actually decide not to stop at all because the tracer
2192-
* is gone, we keep current->exit_code unless clear_code.
2191+
* Returns the signal the ptracer requested the code resume
2192+
* with. If the code did not stop because the tracer is gone,
2193+
* the stop signal remains unchanged unless clear_code.
21932194
*/
2194-
static void ptrace_stop(int exit_code, int why, int clear_code,
2195+
static int ptrace_stop(int exit_code, int why, int clear_code,
21952196
unsigned long message, kernel_siginfo_t *info)
21962197
__releases(&current->sighand->siglock)
21972198
__acquires(&current->sighand->siglock)
21982199
{
21992200
bool gstop_done = false;
2201+
bool read_code = true;
22002202

22012203
if (arch_ptrace_stop_needed()) {
22022204
/*
@@ -2305,8 +2307,9 @@ static void ptrace_stop(int exit_code, int why, int clear_code,
23052307

23062308
/* tasklist protects us from ptrace_freeze_traced() */
23072309
__set_current_state(TASK_RUNNING);
2310+
read_code = false;
23082311
if (clear_code)
2309-
current->exit_code = 0;
2312+
exit_code = 0;
23102313
read_unlock(&tasklist_lock);
23112314
}
23122315

@@ -2316,8 +2319,11 @@ static void ptrace_stop(int exit_code, int why, int clear_code,
23162319
* any signal-sending on another CPU that wants to examine it.
23172320
*/
23182321
spin_lock_irq(&current->sighand->siglock);
2322+
if (read_code)
2323+
exit_code = current->exit_code;
23192324
current->last_siginfo = NULL;
23202325
current->ptrace_message = 0;
2326+
current->exit_code = 0;
23212327

23222328
/* LISTENING can be set only during STOP traps, clear it */
23232329
current->jobctl &= ~JOBCTL_LISTENING;
@@ -2328,9 +2334,10 @@ static void ptrace_stop(int exit_code, int why, int clear_code,
23282334
* This sets TIF_SIGPENDING, but never clears it.
23292335
*/
23302336
recalc_sigpending_tsk(current);
2337+
return exit_code;
23312338
}
23322339

2333-
static void ptrace_do_notify(int signr, int exit_code, int why, unsigned long message)
2340+
static int ptrace_do_notify(int signr, int exit_code, int why, unsigned long message)
23342341
{
23352342
kernel_siginfo_t info;
23362343

@@ -2341,18 +2348,21 @@ static void ptrace_do_notify(int signr, int exit_code, int why, unsigned long me
23412348
info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
23422349

23432350
/* Let the debugger run. */
2344-
ptrace_stop(exit_code, why, 1, message, &info);
2351+
return ptrace_stop(exit_code, why, 1, message, &info);
23452352
}
23462353

2347-
void ptrace_notify(int exit_code, unsigned long message)
2354+
int ptrace_notify(int exit_code, unsigned long message)
23482355
{
2356+
int signr;
2357+
23492358
BUG_ON((exit_code & (0x7f | ~0xffff)) != SIGTRAP);
23502359
if (unlikely(task_work_pending(current)))
23512360
task_work_run();
23522361

23532362
spin_lock_irq(&current->sighand->siglock);
2354-
ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED, message);
2363+
signr = ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED, message);
23552364
spin_unlock_irq(&current->sighand->siglock);
2365+
return signr;
23562366
}
23572367

23582368
/**
@@ -2511,7 +2521,6 @@ static void do_jobctl_trap(void)
25112521
} else {
25122522
WARN_ON_ONCE(!signr);
25132523
ptrace_stop(signr, CLD_STOPPED, 0, 0, NULL);
2514-
current->exit_code = 0;
25152524
}
25162525
}
25172526

@@ -2564,15 +2573,12 @@ static int ptrace_signal(int signr, kernel_siginfo_t *info, enum pid_type type)
25642573
* comment in dequeue_signal().
25652574
*/
25662575
current->jobctl |= JOBCTL_STOP_DEQUEUED;
2567-
ptrace_stop(signr, CLD_TRAPPED, 0, 0, info);
2576+
signr = ptrace_stop(signr, CLD_TRAPPED, 0, 0, info);
25682577

25692578
/* We're back. Did the debugger cancel the sig? */
2570-
signr = current->exit_code;
25712579
if (signr == 0)
25722580
return signr;
25732581

2574-
current->exit_code = 0;
2575-
25762582
/*
25772583
* Update the siginfo structure if the signal has
25782584
* changed. If the debugger wanted something

0 commit comments

Comments
 (0)