diff options
author | Oleg Nesterov <oleg@redhat.com> | 2009-06-04 19:29:07 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-04 21:07:40 -0400 |
commit | 087eb437051b3de817720f9c80c440fc9e7dcce8 (patch) | |
tree | 25a72acabe05c3e72dddd942855d4245dfae4b82 /include | |
parent | b87297fb405ef13cac375f202d114323b076a56d (diff) |
ptrace: tracehook_report_clone: fix false positives
The "trace || CLONE_PTRACE" check in tracehook_report_clone() is not right,
- If the untraced task does clone(CLONE_PTRACE) the new child is not traced,
we must not queue SIGSTOP.
- If we forked the traced task, but the tracer exits and untraces both the
forking task and the new child (after copy_process() drops tasklist_lock),
we should not queue SIGSTOP too.
Change the code to check task_ptrace() != 0 instead. This is still racy, but
the race is harmless.
We can race with another tracer attaching to this child, or the tracer can
exit and detach in parallel. But giwen that we didn't do wake_up_new_task()
yet, the child must have the pending SIGSTOP anyway.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Roland McGrath <roland@redhat.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'include')
-rw-r--r-- | include/linux/tracehook.h | 11 |
1 files changed, 5 insertions, 6 deletions
diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h index c7aa154f4bfc..eb96603d92db 100644 --- a/include/linux/tracehook.h +++ b/include/linux/tracehook.h | |||
@@ -259,14 +259,12 @@ static inline void tracehook_finish_clone(struct task_struct *child, | |||
259 | 259 | ||
260 | /** | 260 | /** |
261 | * tracehook_report_clone - in parent, new child is about to start running | 261 | * tracehook_report_clone - in parent, new child is about to start running |
262 | * @trace: return value from tracehook_prepare_clone() | ||
263 | * @regs: parent's user register state | 262 | * @regs: parent's user register state |
264 | * @clone_flags: flags from parent's system call | 263 | * @clone_flags: flags from parent's system call |
265 | * @pid: new child's PID in the parent's namespace | 264 | * @pid: new child's PID in the parent's namespace |
266 | * @child: new child task | 265 | * @child: new child task |
267 | * | 266 | * |
268 | * Called after a child is set up, but before it has been started | 267 | * Called after a child is set up, but before it has been started running. |
269 | * running. @trace is the value returned by tracehook_prepare_clone(). | ||
270 | * This is not a good place to block, because the child has not started | 268 | * This is not a good place to block, because the child has not started |
271 | * yet. Suspend the child here if desired, and then block in | 269 | * yet. Suspend the child here if desired, and then block in |
272 | * tracehook_report_clone_complete(). This must prevent the child from | 270 | * tracehook_report_clone_complete(). This must prevent the child from |
@@ -276,13 +274,14 @@ static inline void tracehook_finish_clone(struct task_struct *child, | |||
276 | * | 274 | * |
277 | * Called with no locks held, but the child cannot run until this returns. | 275 | * Called with no locks held, but the child cannot run until this returns. |
278 | */ | 276 | */ |
279 | static inline void tracehook_report_clone(int trace, struct pt_regs *regs, | 277 | static inline void tracehook_report_clone(struct pt_regs *regs, |
280 | unsigned long clone_flags, | 278 | unsigned long clone_flags, |
281 | pid_t pid, struct task_struct *child) | 279 | pid_t pid, struct task_struct *child) |
282 | { | 280 | { |
283 | if (unlikely(trace) || unlikely(clone_flags & CLONE_PTRACE)) { | 281 | if (unlikely(task_ptrace(child))) { |
284 | /* | 282 | /* |
285 | * The child starts up with an immediate SIGSTOP. | 283 | * It doesn't matter who attached/attaching to this |
284 | * task, the pending SIGSTOP is right in any case. | ||
286 | */ | 285 | */ |
287 | sigaddset(&child->pending.signal, SIGSTOP); | 286 | sigaddset(&child->pending.signal, SIGSTOP); |
288 | set_tsk_thread_flag(child, TIF_SIGPENDING); | 287 | set_tsk_thread_flag(child, TIF_SIGPENDING); |