Commit 09a05394fe2448a4139b014936330af23fa7ec83

Authored by Roland McGrath
Committed by Linus Torvalds
1 parent 30199f5a46

tracehook: clone

This moves all the ptrace initialization and tracing logic for task
creation into tracehook.h and ptrace.h inlines.  It reorganizes the code
slightly, but should not change any behavior.

There are four tracehook entry points, at each important stage of task
creation.  This keeps the interface from the core fork.c code fairly
clean, while supporting the complex setup required for ptrace or something
like it.

Signed-off-by: Roland McGrath <roland@redhat.com>
Cc: Oleg Nesterov <oleg@tv-sign.ru>
Reviewed-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

Showing 3 changed files with 150 additions and 41 deletions Side-by-side Diff

include/linux/ptrace.h
... ... @@ -154,6 +154,28 @@
154 154 return 1;
155 155 }
156 156  
  157 +/**
  158 + * ptrace_init_task - initialize ptrace state for a new child
  159 + * @child: new child task
  160 + * @ptrace: true if child should be ptrace'd by parent's tracer
  161 + *
  162 + * This is called immediately after adding @child to its parent's children
  163 + * list. @ptrace is false in the normal case, and true to ptrace @child.
  164 + *
  165 + * Called with current's siglock and write_lock_irq(&tasklist_lock) held.
  166 + */
  167 +static inline void ptrace_init_task(struct task_struct *child, bool ptrace)
  168 +{
  169 + INIT_LIST_HEAD(&child->ptrace_entry);
  170 + INIT_LIST_HEAD(&child->ptraced);
  171 + child->parent = child->real_parent;
  172 + child->ptrace = 0;
  173 + if (unlikely(ptrace)) {
  174 + child->ptrace = current->ptrace;
  175 + __ptrace_link(child, current->parent);
  176 + }
  177 +}
  178 +
157 179 #ifndef force_successful_syscall_return
158 180 /*
159 181 * System call handlers that, upon successful completion, need to return a
include/linux/tracehook.h
... ... @@ -110,5 +110,105 @@
110 110 ptrace_event(PT_TRACE_EXIT, PTRACE_EVENT_EXIT, *exit_code);
111 111 }
112 112  
  113 +/**
  114 + * tracehook_prepare_clone - prepare for new child to be cloned
  115 + * @clone_flags: %CLONE_* flags from clone/fork/vfork system call
  116 + *
  117 + * This is called before a new user task is to be cloned.
  118 + * Its return value will be passed to tracehook_finish_clone().
  119 + *
  120 + * Called with no locks held.
  121 + */
  122 +static inline int tracehook_prepare_clone(unsigned clone_flags)
  123 +{
  124 + if (clone_flags & CLONE_UNTRACED)
  125 + return 0;
  126 +
  127 + if (clone_flags & CLONE_VFORK) {
  128 + if (current->ptrace & PT_TRACE_VFORK)
  129 + return PTRACE_EVENT_VFORK;
  130 + } else if ((clone_flags & CSIGNAL) != SIGCHLD) {
  131 + if (current->ptrace & PT_TRACE_CLONE)
  132 + return PTRACE_EVENT_CLONE;
  133 + } else if (current->ptrace & PT_TRACE_FORK)
  134 + return PTRACE_EVENT_FORK;
  135 +
  136 + return 0;
  137 +}
  138 +
  139 +/**
  140 + * tracehook_finish_clone - new child created and being attached
  141 + * @child: new child task
  142 + * @clone_flags: %CLONE_* flags from clone/fork/vfork system call
  143 + * @trace: return value from tracehook_clone_prepare()
  144 + *
  145 + * This is called immediately after adding @child to its parent's children list.
  146 + * The @trace value is that returned by tracehook_prepare_clone().
  147 + *
  148 + * Called with current's siglock and write_lock_irq(&tasklist_lock) held.
  149 + */
  150 +static inline void tracehook_finish_clone(struct task_struct *child,
  151 + unsigned long clone_flags, int trace)
  152 +{
  153 + ptrace_init_task(child, (clone_flags & CLONE_PTRACE) || trace);
  154 +}
  155 +
  156 +/**
  157 + * tracehook_report_clone - in parent, new child is about to start running
  158 + * @trace: return value from tracehook_clone_prepare()
  159 + * @regs: parent's user register state
  160 + * @clone_flags: flags from parent's system call
  161 + * @pid: new child's PID in the parent's namespace
  162 + * @child: new child task
  163 + *
  164 + * Called after a child is set up, but before it has been started running.
  165 + * The @trace value is that returned by tracehook_clone_prepare().
  166 + * This is not a good place to block, because the child has not started yet.
  167 + * Suspend the child here if desired, and block in tracehook_clone_complete().
  168 + * This must prevent the child from self-reaping if tracehook_clone_complete()
  169 + * uses the @child pointer; otherwise it might have died and been released by
  170 + * the time tracehook_report_clone_complete() is called.
  171 + *
  172 + * Called with no locks held, but the child cannot run until this returns.
  173 + */
  174 +static inline void tracehook_report_clone(int trace, struct pt_regs *regs,
  175 + unsigned long clone_flags,
  176 + pid_t pid, struct task_struct *child)
  177 +{
  178 + if (unlikely(trace)) {
  179 + /*
  180 + * The child starts up with an immediate SIGSTOP.
  181 + */
  182 + sigaddset(&child->pending.signal, SIGSTOP);
  183 + set_tsk_thread_flag(child, TIF_SIGPENDING);
  184 + }
  185 +}
  186 +
  187 +/**
  188 + * tracehook_report_clone_complete - new child is running
  189 + * @trace: return value from tracehook_clone_prepare()
  190 + * @regs: parent's user register state
  191 + * @clone_flags: flags from parent's system call
  192 + * @pid: new child's PID in the parent's namespace
  193 + * @child: child task, already running
  194 + *
  195 + * This is called just after the child has started running. This is
  196 + * just before the clone/fork syscall returns, or blocks for vfork
  197 + * child completion if @clone_flags has the %CLONE_VFORK bit set.
  198 + * The @child pointer may be invalid if a self-reaping child died and
  199 + * tracehook_report_clone() took no action to prevent it from self-reaping.
  200 + *
  201 + * Called with no locks held.
  202 + */
  203 +static inline void tracehook_report_clone_complete(int trace,
  204 + struct pt_regs *regs,
  205 + unsigned long clone_flags,
  206 + pid_t pid,
  207 + struct task_struct *child)
  208 +{
  209 + if (unlikely(trace))
  210 + ptrace_event(0, trace, pid);
  211 +}
  212 +
113 213 #endif /* <linux/tracehook.h> */
... ... @@ -37,6 +37,7 @@
37 37 #include <linux/swap.h>
38 38 #include <linux/syscalls.h>
39 39 #include <linux/jiffies.h>
  40 +#include <linux/tracehook.h>
40 41 #include <linux/futex.h>
41 42 #include <linux/task_io_accounting_ops.h>
42 43 #include <linux/rcupdate.h>
... ... @@ -865,8 +866,7 @@
865 866  
866 867 new_flags &= ~PF_SUPERPRIV;
867 868 new_flags |= PF_FORKNOEXEC;
868   - if (!(clone_flags & CLONE_PTRACE))
869   - p->ptrace = 0;
  869 + new_flags |= PF_STARTING;
870 870 p->flags = new_flags;
871 871 clear_freeze_flag(p);
872 872 }
... ... @@ -907,7 +907,8 @@
907 907 struct pt_regs *regs,
908 908 unsigned long stack_size,
909 909 int __user *child_tidptr,
910   - struct pid *pid)
  910 + struct pid *pid,
  911 + int trace)
911 912 {
912 913 int retval;
913 914 struct task_struct *p;
... ... @@ -1163,8 +1164,6 @@
1163 1164 */
1164 1165 p->group_leader = p;
1165 1166 INIT_LIST_HEAD(&p->thread_group);
1166   - INIT_LIST_HEAD(&p->ptrace_entry);
1167   - INIT_LIST_HEAD(&p->ptraced);
1168 1167  
1169 1168 /* Now that the task is set up, run cgroup callbacks if
1170 1169 * necessary. We need to run them before the task is visible
... ... @@ -1195,7 +1194,6 @@
1195 1194 p->real_parent = current->real_parent;
1196 1195 else
1197 1196 p->real_parent = current;
1198   - p->parent = p->real_parent;
1199 1197  
1200 1198 spin_lock(&current->sighand->siglock);
1201 1199  
... ... @@ -1237,8 +1235,7 @@
1237 1235  
1238 1236 if (likely(p->pid)) {
1239 1237 list_add_tail(&p->sibling, &p->real_parent->children);
1240   - if (unlikely(p->ptrace & PT_PTRACED))
1241   - __ptrace_link(p, current->parent);
  1238 + tracehook_finish_clone(p, clone_flags, trace);
1242 1239  
1243 1240 if (thread_group_leader(p)) {
1244 1241 if (clone_flags & CLONE_NEWPID)
1245 1242  
... ... @@ -1323,29 +1320,13 @@
1323 1320 struct pt_regs regs;
1324 1321  
1325 1322 task = copy_process(CLONE_VM, 0, idle_regs(&regs), 0, NULL,
1326   - &init_struct_pid);
  1323 + &init_struct_pid, 0);
1327 1324 if (!IS_ERR(task))
1328 1325 init_idle(task, cpu);
1329 1326  
1330 1327 return task;
1331 1328 }
1332 1329  
1333   -static int fork_traceflag(unsigned clone_flags)
1334   -{
1335   - if (clone_flags & CLONE_UNTRACED)
1336   - return 0;
1337   - else if (clone_flags & CLONE_VFORK) {
1338   - if (current->ptrace & PT_TRACE_VFORK)
1339   - return PTRACE_EVENT_VFORK;
1340   - } else if ((clone_flags & CSIGNAL) != SIGCHLD) {
1341   - if (current->ptrace & PT_TRACE_CLONE)
1342   - return PTRACE_EVENT_CLONE;
1343   - } else if (current->ptrace & PT_TRACE_FORK)
1344   - return PTRACE_EVENT_FORK;
1345   -
1346   - return 0;
1347   -}
1348   -
1349 1330 /*
1350 1331 * Ok, this is the main fork-routine.
1351 1332 *
1352 1333  
... ... @@ -1380,14 +1361,14 @@
1380 1361 }
1381 1362 }
1382 1363  
1383   - if (unlikely(current->ptrace)) {
1384   - trace = fork_traceflag (clone_flags);
1385   - if (trace)
1386   - clone_flags |= CLONE_PTRACE;
1387   - }
  1364 + /*
  1365 + * When called from kernel_thread, don't do user tracing stuff.
  1366 + */
  1367 + if (likely(user_mode(regs)))
  1368 + trace = tracehook_prepare_clone(clone_flags);
1388 1369  
1389 1370 p = copy_process(clone_flags, stack_start, regs, stack_size,
1390   - child_tidptr, NULL);
  1371 + child_tidptr, NULL, trace);
1391 1372 /*
1392 1373 * Do this prior waking up the new thread - the thread pointer
1393 1374 * might get invalid after that point, if the thread exits quickly.
1394 1375  
1395 1376  
1396 1377  
... ... @@ -1405,23 +1386,29 @@
1405 1386 init_completion(&vfork);
1406 1387 }
1407 1388  
1408   - if ((p->ptrace & PT_PTRACED) || (clone_flags & CLONE_STOPPED)) {
  1389 + tracehook_report_clone(trace, regs, clone_flags, nr, p);
  1390 +
  1391 + /*
  1392 + * We set PF_STARTING at creation in case tracing wants to
  1393 + * use this to distinguish a fully live task from one that
  1394 + * hasn't gotten to tracehook_report_clone() yet. Now we
  1395 + * clear it and set the child going.
  1396 + */
  1397 + p->flags &= ~PF_STARTING;
  1398 +
  1399 + if (unlikely(clone_flags & CLONE_STOPPED)) {
1409 1400 /*
1410 1401 * We'll start up with an immediate SIGSTOP.
1411 1402 */
1412 1403 sigaddset(&p->pending.signal, SIGSTOP);
1413 1404 set_tsk_thread_flag(p, TIF_SIGPENDING);
1414   - }
1415   -
1416   - if (!(clone_flags & CLONE_STOPPED))
1417   - wake_up_new_task(p, clone_flags);
1418   - else
1419 1405 __set_task_state(p, TASK_STOPPED);
1420   -
1421   - if (unlikely (trace)) {
1422   - current->ptrace_message = nr;
1423   - ptrace_notify ((trace << 8) | SIGTRAP);
  1406 + } else {
  1407 + wake_up_new_task(p, clone_flags);
1424 1408 }
  1409 +
  1410 + tracehook_report_clone_complete(trace, regs,
  1411 + clone_flags, nr, p);
1425 1412  
1426 1413 if (clone_flags & CLONE_VFORK) {
1427 1414 freezer_do_not_count();