linux ptrace 内核源码分析,linux3.5.4 ptrace源码分析二(系列二)

相比较于PTRACE_TRACEME,PTRACE_ATTACH则是一个进程(将要成为父进程)主动的去trace一个正在运行的进程(子进程)PTRACE_ATTACH的主要处理函数如下:

static int ptrace_attach(struct task_struct *task, long request,

unsigned long addr,

unsigned long flags)

{

bool seize = (request == PTRACE_SEIZE);

int retval;

retval = -EIO;

if (seize) {

if (addr != 0)

goto out;

if (flags & ~(unsigned long)PTRACE_O_MASK)

goto out;

flags = PT_PTRACED | PT_SEIZED | (flags << PT_OPT_FLAG_SHIFT);

} else {

flags = PT_PTRACED;

}

audit_ptrace(task);

retval = -EPERM;

if (unlikely(task->flags & PF_KTHREAD))

goto out;

if (same_thread_group(task, current))

goto out;

/*

* Protect exec's credential calculations against our interference;

* SUID, SGID and LSM creds get determined differently

* under ptrace.

*/

retval = -ERESTARTNOINTR;

if (mutex_lock_interruptible(&task->signal->cred_guard_mutex))

goto out;

task_lock(task);

retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);

task_unlock(task);

if (retval)

goto unlock_creds;

write_lock_irq(&tasklist_lock);

retval = -EPERM;

if (unlikely(task->exit_state))

goto unlock_tasklist;

if (task->ptrace)

goto unlock_tasklist;

if (seize)

flags |= PT_SEIZED;

if (ns_capable(task_user_ns(task), CAP_SYS_PTRACE))

flags |= PT_PTRACE_CAP;

task->ptrace = flags;

__ptrace_link(task, current);

/* SEIZE doesn't trap tracee on attach */

if (!seize)

send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);

spin_lock(&task->sighand->siglock);

/*

* If the task is already STOPPED, set JOBCTL_TRAP_STOP and

* TRAPPING, and kick it so that it transits to TRACED. TRAPPING

* will be cleared if the child completes the transition or any

* event which clears the group stop states happens. We'll wait

* for the transition to complete before returning from this

* function.

*

* This hides STOPPED -> RUNNING -> TRACED transition from the

* attaching thread but a different thread in the same group can

* still observe the transient RUNNING state. IOW, if another

* thread's WNOHANG wait(2) on the stopped tracee races against

* ATTACH, the wait(2) may fail due to the transient RUNNING.

*

* The following task_is_stopped() test is safe as both transitions

* in and out of STOPPED are protected by siglock.

*/

if (task_is_stopped(task) &&

task_set_jobctl_pending(task, JOBCTL_TRAP_STOP | JOBCTL_TRAPPING))

signal_wake_up(task, 1);

spin_unlock(&task->sighand->siglock);

retval = 0;

unlock_tasklist:

write_unlock_irq(&tasklist_lock);

unlock_creds:

mutex_unlock(&task->signal->cred_guard_mutex);

out:

if (!retval) {

wait_on_bit(&task->jobctl, JOBCTL_TRAPPING_BIT,

ptrace_trapping_sleep_fn, TASK_UNINTERRUPTIBLE);

proc_ptrace_connector(task, PTRACE_ATTACH);

}

return retval;

}

整个函数的流程总结如下

1.  判断请求是PTRACE_SEIZE还是PTRACE_ATTACH,如果ptrace请求为PTRACE_SEIZE,则检查其参数是否正确,参数有误则退出

2.  判断task进程是否为kernel thread(PF_KTHREAD),调用same_thread_group(task,current),判断task是否和current进程在同一个线程组,查看current进程是否有权限追踪task进程,不符合要求则退出

3.  设置子进程task->ptrace = PT_TRACED,被跟踪状态,如果当前进程拥有CAP_SYS_PTRACED,设置task->ptrace |= PT_TRACE_CAP

4.  调用__ptrace_link(task, current),将task->ptrace_entry链接到current->ptraced链表中

5.  如果是PTRACE_ATTACH请求(PTRACE_SEIZE请求不会停止被追踪进程),则调用send_sig_info(SIGSTOP,SEND_SIG_FORCED, task);发送SIGSTOP信号,中止task运行,设置task->state为TASK_STOPPED

6.  等待task->jobctl的JOBCTL_TRAPPING_BIT位被清零,阻塞时进程状态被设置为TASK_UNINTERRUPTIBLE并引发进程调度

PTRACE_ATTACH处理的方式与PTRACE_TRACEME处理的方式不同,PTRACE_ATTACH会使父进程向子进程发送SIGTRAP信号,如果子进程停止,父进程的wait操作则会被唤醒,从而成功attach。

而PTRACE_TRACEME只是表明该进程(child)想被trace的意愿。如果一个进程调用了PTRACE_TRACEME,那么该进程处理信号的方式将会变得不同。比如:如果一个进程正在运行,此时输入ctrl+c(SIGINT),则该进程直接退出。但是,如果该进程中有ptrace(PTRACE_TRACEME,0,NULL,NULL)。即该进程主动要求被跟踪,那么,当输入CTRL+C时,该进程将会处于stopped的状态。

你可能感兴趣的:(linux,ptrace,内核源码分析)