ptrace: kill clone/exec tracehooks

At this point, tracehooks aren't useful to mainline kernel and mostly
just add an extra layer of obfuscation.  Although they have comments,
without actual in-kernel users, it is difficult to tell what are their
assumptions and they're actually trying to achieve.  To mainline
kernel, they just aren't worth keeping around.

This patch kills the following clone and exec related tracehooks.

	tracehook_prepare_clone()
	tracehook_finish_clone()
	tracehook_report_clone()
	tracehook_report_clone_complete()
	tracehook_unsafe_exec()

The changes are mostly trivial - logic is moved to the caller and
comments are merged and adjusted appropriately.

The only exception is in check_unsafe_exec() where LSM_UNSAFE_PTRACE*
are OR'd to bprm->unsafe instead of setting it, which produces the
same result as the field is always zero on entry.  It also tests
p->ptrace instead of (p->ptrace & PT_PTRACED) for consistency, which
also gives the same result.

This doesn't introduce any behavior change.

Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
This commit is contained in:
Tejun Heo 2011-06-17 16:50:38 +02:00 committed by Oleg Nesterov
parent a288eecce5
commit 4b9d33e6d8
3 changed files with 38 additions and 131 deletions

View file

@ -1224,7 +1224,12 @@ int check_unsafe_exec(struct linux_binprm *bprm)
unsigned n_fs; unsigned n_fs;
int res = 0; int res = 0;
bprm->unsafe = tracehook_unsafe_exec(p); if (p->ptrace) {
if (p->ptrace & PT_PTRACE_CAP)
bprm->unsafe |= LSM_UNSAFE_PTRACE_CAP;
else
bprm->unsafe |= LSM_UNSAFE_PTRACE;
}
n_fs = 1; n_fs = 1;
spin_lock(&p->fs->lock); spin_lock(&p->fs->lock);

View file

@ -129,27 +129,6 @@ static inline void tracehook_report_syscall_exit(struct pt_regs *regs, int step)
ptrace_report_syscall(regs); ptrace_report_syscall(regs);
} }
/**
* tracehook_unsafe_exec - check for exec declared unsafe due to tracing
* @task: current task doing exec
*
* Return %LSM_UNSAFE_* bits applied to an exec because of tracing.
*
* @task->signal->cred_guard_mutex is held by the caller through the do_execve().
*/
static inline int tracehook_unsafe_exec(struct task_struct *task)
{
int unsafe = 0;
int ptrace = task->ptrace;
if (ptrace & PT_PTRACED) {
if (ptrace & PT_PTRACE_CAP)
unsafe |= LSM_UNSAFE_PTRACE_CAP;
else
unsafe |= LSM_UNSAFE_PTRACE;
}
return unsafe;
}
/** /**
* tracehook_tracer_task - return the task that is tracing the given task * tracehook_tracer_task - return the task that is tracing the given task
* @tsk: task to consider * @tsk: task to consider
@ -168,106 +147,6 @@ static inline struct task_struct *tracehook_tracer_task(struct task_struct *tsk)
return NULL; return NULL;
} }
/**
* tracehook_prepare_clone - prepare for new child to be cloned
* @clone_flags: %CLONE_* flags from clone/fork/vfork system call
*
* This is called before a new user task is to be cloned.
* Its return value will be passed to tracehook_finish_clone().
*
* Called with no locks held.
*/
static inline int tracehook_prepare_clone(unsigned clone_flags)
{
int event = 0;
if (clone_flags & CLONE_UNTRACED)
return 0;
if (clone_flags & CLONE_VFORK)
event = PTRACE_EVENT_VFORK;
else if ((clone_flags & CSIGNAL) != SIGCHLD)
event = PTRACE_EVENT_CLONE;
else
event = PTRACE_EVENT_FORK;
return ptrace_event_enabled(current, event) ? event : 0;
}
/**
* tracehook_finish_clone - new child created and being attached
* @child: new child task
* @clone_flags: %CLONE_* flags from clone/fork/vfork system call
* @trace: return value from tracehook_prepare_clone()
*
* This is called immediately after adding @child to its parent's children list.
* The @trace value is that returned by tracehook_prepare_clone().
*
* Called with current's siglock and write_lock_irq(&tasklist_lock) held.
*/
static inline void tracehook_finish_clone(struct task_struct *child,
unsigned long clone_flags, int trace)
{
ptrace_init_task(child, (clone_flags & CLONE_PTRACE) || trace);
}
/**
* tracehook_report_clone - in parent, new child is about to start running
* @regs: parent's user register state
* @clone_flags: flags from parent's system call
* @pid: new child's PID in the parent's namespace
* @child: new child task
*
* Called after a child is set up, but before it has been started running.
* This is not a good place to block, because the child has not started
* yet. Suspend the child here if desired, and then block in
* tracehook_report_clone_complete(). This must prevent the child from
* self-reaping if tracehook_report_clone_complete() uses the @child
* pointer; otherwise it might have died and been released by the time
* tracehook_report_clone_complete() is called.
*
* Called with no locks held, but the child cannot run until this returns.
*/
static inline void tracehook_report_clone(struct pt_regs *regs,
unsigned long clone_flags,
pid_t pid, struct task_struct *child)
{
if (unlikely(child->ptrace)) {
/*
* It doesn't matter who attached/attaching to this
* task, the pending SIGSTOP is right in any case.
*/
sigaddset(&child->pending.signal, SIGSTOP);
set_tsk_thread_flag(child, TIF_SIGPENDING);
}
}
/**
* tracehook_report_clone_complete - new child is running
* @trace: return value from tracehook_prepare_clone()
* @regs: parent's user register state
* @clone_flags: flags from parent's system call
* @pid: new child's PID in the parent's namespace
* @child: child task, already running
*
* This is called just after the child has started running. This is
* just before the clone/fork syscall returns, or blocks for vfork
* child completion if @clone_flags has the %CLONE_VFORK bit set.
* The @child pointer may be invalid if a self-reaping child died and
* tracehook_report_clone() took no action to prevent it from self-reaping.
*
* Called with no locks held.
*/
static inline void tracehook_report_clone_complete(int trace,
struct pt_regs *regs,
unsigned long clone_flags,
pid_t pid,
struct task_struct *child)
{
if (unlikely(trace))
ptrace_event(trace, pid);
}
/** /**
* tracehook_signal_handler - signal handler setup is complete * tracehook_signal_handler - signal handler setup is complete
* @sig: number of signal being delivered * @sig: number of signal being delivered

View file

@ -1340,7 +1340,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
} }
if (likely(p->pid)) { if (likely(p->pid)) {
tracehook_finish_clone(p, clone_flags, trace); ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace);
if (thread_group_leader(p)) { if (thread_group_leader(p)) {
if (is_child_reaper(pid)) if (is_child_reaper(pid))
@ -1481,10 +1481,22 @@ long do_fork(unsigned long clone_flags,
} }
/* /*
* When called from kernel_thread, don't do user tracing stuff. * Determine whether and which event to report to ptracer. When
* called from kernel_thread or CLONE_UNTRACED is explicitly
* requested, no event is reported; otherwise, report if the event
* for the type of forking is enabled.
*/ */
if (likely(user_mode(regs))) if (likely(user_mode(regs)) && !(clone_flags & CLONE_UNTRACED)) {
trace = tracehook_prepare_clone(clone_flags); if (clone_flags & CLONE_VFORK)
trace = PTRACE_EVENT_VFORK;
else if ((clone_flags & CSIGNAL) != SIGCHLD)
trace = PTRACE_EVENT_CLONE;
else
trace = PTRACE_EVENT_FORK;
if (likely(!ptrace_event_enabled(current, trace)))
trace = 0;
}
p = copy_process(clone_flags, stack_start, regs, stack_size, p = copy_process(clone_flags, stack_start, regs, stack_size,
child_tidptr, NULL, trace); child_tidptr, NULL, trace);
@ -1508,20 +1520,31 @@ long do_fork(unsigned long clone_flags,
} }
audit_finish_fork(p); audit_finish_fork(p);
tracehook_report_clone(regs, clone_flags, nr, p);
/*
* Child is ready but hasn't started running yet. Queue
* SIGSTOP if it's gonna be ptraced - it doesn't matter who
* attached/attaching to this task, the pending SIGSTOP is
* right in any case.
*/
if (unlikely(p->ptrace)) {
sigaddset(&p->pending.signal, SIGSTOP);
set_tsk_thread_flag(p, TIF_SIGPENDING);
}
/* /*
* We set PF_STARTING at creation in case tracing wants to * We set PF_STARTING at creation in case tracing wants to
* use this to distinguish a fully live task from one that * use this to distinguish a fully live task from one that
* hasn't gotten to tracehook_report_clone() yet. Now we * hasn't finished SIGSTOP raising yet. Now we clear it
* clear it and set the child going. * and set the child going.
*/ */
p->flags &= ~PF_STARTING; p->flags &= ~PF_STARTING;
wake_up_new_task(p); wake_up_new_task(p);
tracehook_report_clone_complete(trace, regs, /* forking complete and child started to run, tell ptracer */
clone_flags, nr, p); if (unlikely(trace))
ptrace_event(trace, nr);
if (clone_flags & CLONE_VFORK) { if (clone_flags & CLONE_VFORK) {
freezer_do_not_count(); freezer_do_not_count();