+
+static u32
+ptrace_report_clone(struct utrace_attached_engine *engine,
+ struct task_struct *parent,
+ unsigned long clone_flags, struct task_struct *child)
+{
+ int event, option;
+ struct ptrace_state *state = get_ptrace_state(engine, parent);
+ if (unlikely(state == NULL))
+ return UTRACE_ACTION_RESUME;
+
+ pr_debug("%d (%p) engine %p"
+ " ptrace_report_clone child %d (%p) fl %lx\n",
+ parent->pid, parent, engine, child->pid, child, clone_flags);
+
+ event = PTRACE_EVENT_FORK;
+ option = PTRACE_O_TRACEFORK;
+ if (clone_flags & CLONE_VFORK) {
+ event = PTRACE_EVENT_VFORK;
+ option = PTRACE_O_TRACEVFORK;
+ }
+ else if ((clone_flags & CSIGNAL) != SIGCHLD) {
+ event = PTRACE_EVENT_CLONE;
+ option = PTRACE_O_TRACECLONE;
+ }
+
+ if (state->options & option) {
+ state->have_eventmsg = 1;
+ state->u.eventmsg = child->pid;
+ }
+ else
+ event = 0;
+
+ if (!(clone_flags & CLONE_UNTRACED)
+ && (event || (clone_flags & CLONE_PTRACE))) {
+ /*
+ * Have our tracer start following the child too.
+ */
+ ptrace_clone_setup(engine, parent, state, child);
+
+ /*
+ * That did put_ptrace_state, so we have to check
+ * again in case our tracer just started exiting.
+ */
+ state = get_ptrace_state(engine, parent);
+ if (unlikely(state == NULL))
+ return UTRACE_ACTION_RESUME;
+ }
+
+ if (event)
+ return ptrace_event(engine, parent, state, event);
+
+ put_ptrace_state(state);
+
+ return UTRACE_ACTION_RESUME;
+}
+
+
+static u32
+ptrace_report_vfork_done(struct utrace_attached_engine *engine,
+ struct task_struct *parent, pid_t child_pid)
+{
+ struct ptrace_state *state = get_ptrace_state(engine, parent);
+ if (unlikely(state == NULL))
+ return UTRACE_ACTION_RESUME;
+
+ state->have_eventmsg = 1;
+ state->u.eventmsg = child_pid;
+ return ptrace_event(engine, parent, state, PTRACE_EVENT_VFORK_DONE);
+}
+
+
+static u32
+ptrace_report_signal(struct utrace_attached_engine *engine,
+ struct task_struct *tsk, struct pt_regs *regs,
+ u32 action, siginfo_t *info,
+ const struct k_sigaction *orig_ka,
+ struct k_sigaction *return_ka)
+{
+ int signo = info == NULL ? SIGTRAP : info->si_signo;
+ struct ptrace_state *state = get_ptrace_state(engine, tsk);
+ if (unlikely(state == NULL))
+ return UTRACE_ACTION_RESUME;
+
+ state->syscall = 0;
+ state->have_eventmsg = 0;
+ state->u.siginfo = info;
+ return ptrace_report(engine, tsk, state, signo) | UTRACE_SIGNAL_IGN;
+}
+
+static u32
+ptrace_report_jctl(struct utrace_attached_engine *engine,
+ struct task_struct *tsk, int type)
+{
+ struct ptrace_state *state = get_ptrace_state(engine, tsk);
+ if (unlikely(state == NULL))
+ return UTRACE_ACTION_RESUME;
+
+ pr_debug("ptrace %d jctl notify %d type %x exit_code %x\n",
+ tsk->pid, state->parent->pid, type, tsk->exit_code);
+
+ do_notify(tsk, state->parent, type);
+ put_ptrace_state(state);
+
+ return UTRACE_JCTL_NOSIGCHLD;
+}
+
+static u32
+ptrace_report_exec(struct utrace_attached_engine *engine,
+ struct task_struct *tsk,
+ const struct linux_binprm *bprm,
+ struct pt_regs *regs)
+{
+ struct ptrace_state *state = get_ptrace_state(engine, tsk);
+ if (unlikely(state == NULL))
+ return UTRACE_ACTION_RESUME;
+
+ return ptrace_event(engine, tsk, state,
+ (state->options & PTRACE_O_TRACEEXEC)
+ ? PTRACE_EVENT_EXEC : 0);
+}
+
+static u32
+ptrace_report_syscall(struct utrace_attached_engine *engine,
+ struct task_struct *tsk, struct pt_regs *regs,
+ int entry)
+{
+ struct ptrace_state *state = get_ptrace_state(engine, tsk);
+ if (unlikely(state == NULL))
+ return UTRACE_ACTION_RESUME;
+
+#ifdef PTRACE_SYSEMU
+ if (entry && state->sysemu)
+ tracehook_abort_syscall(regs);
+#endif
+ state->syscall = 1;
+ return ptrace_report(engine, tsk, state,
+ ((state->options & PTRACE_O_TRACESYSGOOD)
+ ? 0x80 : 0) | SIGTRAP);
+}
+
+static u32
+ptrace_report_syscall_entry(struct utrace_attached_engine *engine,
+ struct task_struct *tsk, struct pt_regs *regs)
+{
+ return ptrace_report_syscall(engine, tsk, regs, 1);
+}
+
+static u32
+ptrace_report_syscall_exit(struct utrace_attached_engine *engine,
+ struct task_struct *tsk, struct pt_regs *regs)
+{
+ return ptrace_report_syscall(engine, tsk, regs, 0);
+}
+
+static u32
+ptrace_report_exit(struct utrace_attached_engine *engine,
+ struct task_struct *tsk, long orig_code, long *code)
+{
+ struct ptrace_state *state = get_ptrace_state(engine, tsk);
+ if (unlikely(state == NULL))
+ return UTRACE_ACTION_RESUME;
+
+ state->have_eventmsg = 1;
+ state->u.eventmsg = *code;
+ return ptrace_event(engine, tsk, state, PTRACE_EVENT_EXIT);
+}
+
+static int
+ptrace_unsafe_exec(struct utrace_attached_engine *engine,
+ struct task_struct *tsk)
+{
+ int unsafe = LSM_UNSAFE_PTRACE;
+ struct ptrace_state *state = get_ptrace_state(engine, tsk);
+ if (likely(state != NULL) && state->cap_sys_ptrace)
+ unsafe = LSM_UNSAFE_PTRACE_CAP;
+ put_ptrace_state(state);
+ return unsafe;
+}
+
+static struct task_struct *
+ptrace_tracer_task(struct utrace_attached_engine *engine,
+ struct task_struct *target)
+{
+ struct task_struct *parent = NULL;
+ struct ptrace_state *state = get_ptrace_state(engine, target);
+ if (likely(state != NULL)) {
+ parent = state->parent;
+ put_ptrace_state(state);
+ }
+ return parent;
+}
+
+static int
+ptrace_allow_access_process_vm(struct utrace_attached_engine *engine,
+ struct task_struct *target,
+ struct task_struct *caller)
+{
+ struct ptrace_state *state;
+ int ours = 0;
+
+ state = get_ptrace_state(engine, target);
+ if (likely(state != NULL)) {
+ ours = (((engine->flags & UTRACE_ACTION_QUIESCE)
+ || target->state == TASK_STOPPED)
+ && state->parent == caller);
+ put_ptrace_state(state);
+ }
+
+ return ours && security_ptrace(caller, target) == 0;
+}
+
+
+static const struct utrace_engine_ops ptrace_utrace_ops =
+{
+ .report_syscall_entry = ptrace_report_syscall_entry,
+ .report_syscall_exit = ptrace_report_syscall_exit,
+ .report_exec = ptrace_report_exec,
+ .report_jctl = ptrace_report_jctl,
+ .report_signal = ptrace_report_signal,
+ .report_vfork_done = ptrace_report_vfork_done,
+ .report_clone = ptrace_report_clone,
+ .report_exit = ptrace_report_exit,
+ .report_death = ptrace_report_death,
+ .report_reap = ptrace_report_reap,
+ .unsafe_exec = ptrace_unsafe_exec,
+ .tracer_task = ptrace_tracer_task,
+ .allow_access_process_vm = ptrace_allow_access_process_vm,
+};