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.
Showing the changes to include (2 of 3 files). Show all
Unified · Split
| @@ -60,7 +60,7 @@ extern int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned · 59 unmodified lines | ||
| 60 | 60 | extern void ptrace_disable(struct task_struct *); |
| 61 | 61 | extern int ptrace_request(struct task_struct *child, long request, |
| 62 | 62 | 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); | |
| 64 | 64 | extern void __ptrace_link(struct task_struct *child, |
| 65 | 65 | struct task_struct *new_parent, |
| 66 | 66 | const struct cred *ptracer_cred); |
| @@ -155,8 +155,7 @@ static inline bool ptrace_event_enabled(struct task_struct *task, int event) · 88 unmodified lines | ||
| 155 | 155 | static inline void ptrace_event(int event, unsigned long message) |
| 156 | 156 | { |
| 157 | 157 | 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); | |
| 160 | 159 | } else if (event == PTRACE_EVENT_EXEC) { |
| 161 | 160 | /* legacy EXEC report via SIGTRAP */ |
| 162 | 161 | if ((current->ptrace & (PT_PTRACED|PT_SEIZED)) == PT_PTRACED) |
| @@ -424,8 +423,7 @@ static inline int ptrace_report_syscall(unsigned long message) · 261 unmodified lines | ||
| 424 | 423 | if (!(ptrace & PT_PTRACED)) |
| 425 | 424 | return 0; |
| 426 | 425 | |
| 427 | − current->ptrace_message = message; | |
| 428 | − ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0)); | |
| 426 | + ptrace_notify(SIGTRAP | ((ptrace & PT_TRACESYSGOOD) ? 0x80 : 0), message); | |
| 429 | 427 | |
| 430 | 428 | /* |
| 431 | 429 | * 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) · 5 unmodified lines | ||
| 437 | 435 | current->exit_code = 0; |
| 438 | 436 | } |
| 439 | 437 | |
| 440 | − current->ptrace_message = 0; | |
| 441 | 438 | return fatal_signal_pending(current); |
| 442 | 439 | } |
| 443 | 440 | |
| @@ -114,7 +114,7 @@ struct ptrace_rseq_configuration { · 113 unmodified lines | ||
| 114 | 114 | |
| 115 | 115 | /* |
| 116 | 116 | * 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. | |
| 118 | 118 | */ |
| 119 | 119 | #define PTRACE_EVENTMSG_SYSCALL_ENTRY 1 |
| 120 | 120 | #define PTRACE_EVENTMSG_SYSCALL_EXIT 2 |