Skip to content

Commit 336d4b8

Browse files
committed
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>
1 parent 355f841 commit 336d4b8

File tree

3 files changed

+16
-16
lines changed

3 files changed

+16
-16
lines changed

include/linux/ptrace.h

Lines changed: 3 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);
63+
extern void 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);
@@ -155,8 +155,7 @@ static inline bool ptrace_event_enabled(struct task_struct *task, int event)
155155
static inline void ptrace_event(int event, unsigned long message)
156156
{
157157
if (unlikely(ptrace_event_enabled(current, event))) {
158-
current->ptrace_message = message;
159-
ptrace_notify((event << 8) | SIGTRAP);
158+
ptrace_notify((event << 8) | SIGTRAP, message);
160159
} else if (event == PTRACE_EVENT_EXEC) {
161160
/* legacy EXEC report via SIGTRAP */
162161
if ((current->ptrace & (PT_PTRACED|PT_SEIZED)) == PT_PTRACED)
@@ -424,8 +423,7 @@ static inline int ptrace_report_syscall(unsigned long message)
424423
if (!(ptrace & PT_PTRACED))
425424
return 0;
426425

427-
current->ptrace_message = message;
428-
ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0));
426+
ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0), message);
429427

430428
/*
431429
* 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 message)
437435
current->exit_code = 0;
438436
}
439437

440-
current->ptrace_message = 0;
441438
return fatal_signal_pending(current);
442439
}
443440

include/uapi/linux/ptrace.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ struct ptrace_rseq_configuration {
114114

115115
/*
116116
* These values are stored in task->ptrace_message
117-
* by ptrace_report_syscall_* to describe the current syscall-stop.
117+
* by ptrace_stop to describe the current syscall-stop.
118118
*/
119119
#define PTRACE_EVENTMSG_SYSCALL_ENTRY 1
120120
#define PTRACE_EVENTMSG_SYSCALL_EXIT 2

kernel/signal.c

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2191,7 +2191,8 @@ static void do_notify_parent_cldstop(struct task_struct *tsk,
21912191
* If we actually decide not to stop at all because the tracer
21922192
* is gone, we keep current->exit_code unless clear_code.
21932193
*/
2194-
static void ptrace_stop(int exit_code, int why, int clear_code, kernel_siginfo_t *info)
2194+
static void ptrace_stop(int exit_code, int why, int clear_code,
2195+
unsigned long message, kernel_siginfo_t *info)
21952196
__releases(&current->sighand->siglock)
21962197
__acquires(&current->sighand->siglock)
21972198
{
@@ -2237,6 +2238,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, kernel_siginfo_t
22372238
*/
22382239
smp_wmb();
22392240

2241+
current->ptrace_message = message;
22402242
current->last_siginfo = info;
22412243
current->exit_code = exit_code;
22422244

@@ -2315,6 +2317,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, kernel_siginfo_t
23152317
*/
23162318
spin_lock_irq(&current->sighand->siglock);
23172319
current->last_siginfo = NULL;
2320+
current->ptrace_message = 0;
23182321

23192322
/* LISTENING can be set only during STOP traps, clear it */
23202323
current->jobctl &= ~JOBCTL_LISTENING;
@@ -2327,7 +2330,7 @@ static void ptrace_stop(int exit_code, int why, int clear_code, kernel_siginfo_t
23272330
recalc_sigpending_tsk(current);
23282331
}
23292332

2330-
static void ptrace_do_notify(int signr, int exit_code, int why)
2333+
static void ptrace_do_notify(int signr, int exit_code, int why, unsigned long message)
23312334
{
23322335
kernel_siginfo_t info;
23332336

@@ -2338,17 +2341,17 @@ static void ptrace_do_notify(int signr, int exit_code, int why)
23382341
info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
23392342

23402343
/* Let the debugger run. */
2341-
ptrace_stop(exit_code, why, 1, &info);
2344+
ptrace_stop(exit_code, why, 1, message, &info);
23422345
}
23432346

2344-
void ptrace_notify(int exit_code)
2347+
void ptrace_notify(int exit_code, unsigned long message)
23452348
{
23462349
BUG_ON((exit_code & (0x7f | ~0xffff)) != SIGTRAP);
23472350
if (unlikely(task_work_pending(current)))
23482351
task_work_run();
23492352

23502353
spin_lock_irq(&current->sighand->siglock);
2351-
ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED);
2354+
ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED, message);
23522355
spin_unlock_irq(&current->sighand->siglock);
23532356
}
23542357

@@ -2504,10 +2507,10 @@ static void do_jobctl_trap(void)
25042507
signr = SIGTRAP;
25052508
WARN_ON_ONCE(!signr);
25062509
ptrace_do_notify(signr, signr | (PTRACE_EVENT_STOP << 8),
2507-
CLD_STOPPED);
2510+
CLD_STOPPED, 0);
25082511
} else {
25092512
WARN_ON_ONCE(!signr);
2510-
ptrace_stop(signr, CLD_STOPPED, 0, NULL);
2513+
ptrace_stop(signr, CLD_STOPPED, 0, 0, NULL);
25112514
current->exit_code = 0;
25122515
}
25132516
}
@@ -2561,7 +2564,7 @@ static int ptrace_signal(int signr, kernel_siginfo_t *info, enum pid_type type)
25612564
* comment in dequeue_signal().
25622565
*/
25632566
current->jobctl |= JOBCTL_STOP_DEQUEUED;
2564-
ptrace_stop(signr, CLD_TRAPPED, 0, info);
2567+
ptrace_stop(signr, CLD_TRAPPED, 0, 0, info);
25652568

25662569
/* We're back. Did the debugger cancel the sig? */
25672570
signr = current->exit_code;
@@ -2891,7 +2894,7 @@ static void signal_delivered(struct ksignal *ksig, int stepping)
28912894
if (current->sas_ss_flags & SS_AUTODISARM)
28922895
sas_ss_reset(current);
28932896
if (stepping)
2894-
ptrace_notify(SIGTRAP);
2897+
ptrace_notify(SIGTRAP, 0);
28952898
}
28962899

28972900
void signal_setup_done(int failed, struct ksignal *ksig, int stepping)

0 commit comments

Comments
 (0)