| From 4e52365f279564cef0ddd41db5237f0471381093 Mon Sep 17 00:00:00 2001 |
| From: Matthew Dempsky <mdempsky@chromium.org> |
| Date: Fri, 6 Jun 2014 14:36:42 -0700 |
| Subject: ptrace: fix fork event messages across pid namespaces |
| |
| From: Matthew Dempsky <mdempsky@chromium.org> |
| |
| commit 4e52365f279564cef0ddd41db5237f0471381093 upstream. |
| |
| When tracing a process in another pid namespace, it's important for fork |
| event messages to contain the child's pid as seen from the tracer's pid |
| namespace, not the parent's. Otherwise, the tracer won't be able to |
| correlate the fork event with later SIGTRAP signals it receives from the |
| child. |
| |
| We still risk a race condition if a ptracer from a different pid |
| namespace attaches after we compute the pid_t value. However, sending a |
| bogus fork event message in this unlikely scenario is still a vast |
| improvement over the status quo where we always send bogus fork event |
| messages to debuggers in a different pid namespace than the forking |
| process. |
| |
| Signed-off-by: Matthew Dempsky <mdempsky@chromium.org> |
| Acked-by: Oleg Nesterov <oleg@redhat.com> |
| Cc: Kees Cook <keescook@chromium.org> |
| Cc: Julien Tinnes <jln@chromium.org> |
| Cc: Roland McGrath <mcgrathr@chromium.org> |
| Cc: Jan Kratochvil <jan.kratochvil@redhat.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| include/linux/ptrace.h | 32 ++++++++++++++++++++++++++++++++ |
| kernel/fork.c | 10 +++++++--- |
| 2 files changed, 39 insertions(+), 3 deletions(-) |
| |
| --- a/include/linux/ptrace.h |
| +++ b/include/linux/ptrace.h |
| @@ -5,6 +5,7 @@ |
| #include <linux/sched.h> /* For struct task_struct. */ |
| #include <linux/err.h> /* for IS_ERR_VALUE */ |
| #include <linux/bug.h> /* For BUG_ON. */ |
| +#include <linux/pid_namespace.h> /* For task_active_pid_ns. */ |
| #include <uapi/linux/ptrace.h> |
| |
| /* |
| @@ -129,6 +130,37 @@ static inline void ptrace_event(int even |
| } |
| |
| /** |
| + * ptrace_event_pid - possibly stop for a ptrace event notification |
| + * @event: %PTRACE_EVENT_* value to report |
| + * @pid: process identifier for %PTRACE_GETEVENTMSG to return |
| + * |
| + * Check whether @event is enabled and, if so, report @event and @pid |
| + * to the ptrace parent. @pid is reported as the pid_t seen from the |
| + * the ptrace parent's pid namespace. |
| + * |
| + * Called without locks. |
| + */ |
| +static inline void ptrace_event_pid(int event, struct pid *pid) |
| +{ |
| + /* |
| + * FIXME: There's a potential race if a ptracer in a different pid |
| + * namespace than parent attaches between computing message below and |
| + * when we acquire tasklist_lock in ptrace_stop(). If this happens, |
| + * the ptracer will get a bogus pid from PTRACE_GETEVENTMSG. |
| + */ |
| + unsigned long message = 0; |
| + struct pid_namespace *ns; |
| + |
| + rcu_read_lock(); |
| + ns = task_active_pid_ns(rcu_dereference(current->parent)); |
| + if (ns) |
| + message = pid_nr_ns(pid, ns); |
| + rcu_read_unlock(); |
| + |
| + ptrace_event(event, message); |
| +} |
| + |
| +/** |
| * ptrace_init_task - initialize ptrace state for a new child |
| * @child: new child task |
| * @ptrace: true if child should be ptrace'd by parent's tracer |
| --- a/kernel/fork.c |
| +++ b/kernel/fork.c |
| @@ -1604,10 +1604,12 @@ long do_fork(unsigned long clone_flags, |
| */ |
| if (!IS_ERR(p)) { |
| struct completion vfork; |
| + struct pid *pid; |
| |
| trace_sched_process_fork(current, p); |
| |
| - nr = task_pid_vnr(p); |
| + pid = get_task_pid(p, PIDTYPE_PID); |
| + nr = pid_vnr(pid); |
| |
| if (clone_flags & CLONE_PARENT_SETTID) |
| put_user(nr, parent_tidptr); |
| @@ -1622,12 +1624,14 @@ long do_fork(unsigned long clone_flags, |
| |
| /* forking complete and child started to run, tell ptracer */ |
| if (unlikely(trace)) |
| - ptrace_event(trace, nr); |
| + ptrace_event_pid(trace, pid); |
| |
| if (clone_flags & CLONE_VFORK) { |
| if (!wait_for_vfork_done(p, &vfork)) |
| - ptrace_event(PTRACE_EVENT_VFORK_DONE, nr); |
| + ptrace_event_pid(PTRACE_EVENT_VFORK_DONE, pid); |
| } |
| + |
| + put_pid(pid); |
| } else { |
| nr = PTR_ERR(p); |
| } |