子プロセスのstrace
Rev.1を表示中。最新版はこちら。
straceコマンド-ffオプションは、traceするプロセスの子プロセスのシステムコールも取得、係る実相はptraceシステムコールのPT_SEIZEDオプション。サンプル
straceコマンドのprint/putsの書込みサイズ表示は、サイズ値を文字列とし、\nを付加した書込み文字列を追加しての表示故 )=サイズが下位行に表示される、システムコールに係る表示でなく、straceコマンドにおける馬鹿げた実装grep -A 1 : 取得行の下位1行も取得。(AFTER)
grep -a : バイナリ行は無視しアスキー文字列行に係る検索。(ASCII)
カーネル下使用のgccのsyscall.hに定義されてないユーザ参照されないシステムコールは、バイナリ表示となる。
2>&1 : &はファイルIDで、ファイル名でない。子プロセスのstrace
[root@north trace]# cat babakaka.c
#include <stdio.h>
#include <unistd.h>
void main()
{
if (fork()) {
puts("parent");
} else {
puts("child");
}
}
[root@north trace]# ./babakaka.out
parent child
[root@north trace]# strace ./babakaka.out 2>&1 | grep -A 1 write
write(1, "parent\n", 7parent ) = 7
[root@north trace]# strace -ff ./babakaka.out 2>&1 | grep -A 1 write
write(1, "parent\n", 7parent ) = 7 write(1, "child\n", 6child ) = 6
カーネル
struct task_struct *child
child->real_parent (fork の親プロセス)
child->parent (ptarceの親プロセス)
child->ptrace = PT_PTRACED (ptarceによる設定で、システムコール呼出時にチェックされ、parentにシステムコール引数レジスタを設定)
child->parent->state = __TASK_TRACED (childがシステムコール引数レジスタをchild->parent->audit_context->argv[]に設定した
child->parent->audit_context->argv[0] = ax
child->parent->audit_context->argv[1] = bx
child->parent->audit_context->argv[2] = cx
child->parent->audit_context->argv[3] = dx
child->parentはstateが__TASK_TRACEDでptrace_do_wait()の待機から離脱し、
childが設定したaudit_context->argv[]のシステムコール引数レジスタを取得する)
ptraceされているプロセスからのforkの子プロセスは、fork親プロセスのreal_parentがptrace親プロセスのparentに設定されるが、ptrace属性は設定されない故、係る子プロセスはトレースされないが、PT_SEIZEDでptraceされたプロセスは child->ptrace=PT_PTRACED | PT_SEIZEDで、このプロセスのfork子プロセスは、ptrace_event_enabled()で、PT_SEIZEDが設定されてるなら、childのptrace属性とparentがchildのfork子プロセスに設定される故、childの子プロセスのシステムコールのレジスタもchildの子プロセス->parentに設定されたchild->parentに設定される。
SYSCALL_DEFINE0(fork)
{
struct pt_regs *regs = task_pt_regs(current);
return do_fork(SIGCHLD, regs->gprs[15], regs, 0, NULL, NULL);
}
long do_fork(unsigned long clone_flags,
unsigned long stack_start,
struct pt_regs *regs,
unsigned long stack_size,
int __user *parent_tidptr,
int __user *child_tidptr)
{
struct task_struct *p;
int trace = 0;
long nr;
if (!(clone_flags & CLONE_UNTRACED) && likely(user_mode(regs))) {
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))) <- current->ptraceのPT_SEIZEDのチェック
trace = 0;
}
p = copy_process(clone_flags, stack_start, regs, stack_size,
child_tidptr, NULL, trace); <- PT_SEIZEDのtrace!=0なら、子プロセスのptrace/parentも設定
return nr;
}
static inline bool ptrace_event_enabled(struct task_struct *task, int event)
{
return task->ptrace & PT_EVENT_FLAG(event);
}
static struct task_struct *copy_process(unsigned long clone_flags,
unsigned long stack_start,
struct pt_regs *regs,
unsigned long stack_size,
int __user *child_tidptr,
struct pid *pid,
int trace)
{
int retval;
struct task_struct *p;
int cgroup_callbacks_done = 0;
:
retval = -ENOMEM;
p = dup_task_struct(current);
:
p->pid = pid_nr(pid);
p->tgid = p->pid;
if (clone_flags & CLONE_THREAD)
p->tgid = current->tgid;
:
if (likely(p->pid)) {
ptrace_init_task(p, (clone_flags & CLONE_PTRACE) || trace);
:
}
:
return p;
}
static inline void ptrace_init_task(struct task_struct *child, bool ptrace)
{
INIT_LIST_HEAD(&child->ptrace_entry);
INIT_LIST_HEAD(&child->ptraced);
#ifdef CONFIG_HAVE_HW_BREAKPOINT
atomic_set(&child->ptrace_bp_refcnt, 1);
#endif
child->jobctl = 0;
child->ptrace = 0;
child->parent = child->real_parent; <- ptraceの親プロセスを設定
if (unlikely(ptrace) && current->ptrace) {
child->ptrace = current->ptrace; <- PT_SEIZEDならptrace属性を設定
__ptrace_link(child, current->parent);
if (child->ptrace & PT_SEIZED)
task_set_jobctl_pending(child, JOBCTL_TRAP_STOP);
else
sigaddset(&child->pending.signal, SIGSTOP);
set_tsk_thread_flag(child, TIF_SIGPENDING);
}
}
捕捉
PT_SEIZEDは、PTRACE_ATTACHのプロセスIDに係る実装で、fork子プロセスのカレントプロセスに係るPTRACE_TRACEMEの実装されず、故にfork子プロセスのPT_SEIZEDは、カレントプロセスでなく子プロセスIDでの設定
ptrace_attach(PTRACE_ATTACH,pid): pidのtask->ptrace = PT_PTRACED
ptrace_attach(PTRACE_SEIZE ,pid): pidのtask->ptrace = PT_PTRACED | PT_SEIZED
SYSCALL_DEFINE4(ptrace, long, request, long, pid, unsigned long, addr, unsigned long, data)
{
struct task_struct *child;
long ret;
if (request == PTRACE_TRACEME) { <- fork子プロセスのカレントプロセスにトレース属性設定
ret = ptrace_traceme();
if (!ret)
arch_ptrace_attach(current);
goto out;
}
child = ptrace_get_task_struct(pid);
if (IS_ERR(child)) {
ret = PTR_ERR(child);
goto out;
}
if (request == PTRACE_ATTACH || request == PTRACE_SEIZE) { <- 引数pidプロセスにトレース属性設定
ret = ptrace_attach(child, request, addr, data);
if (!ret)
arch_ptrace_attach(child);
goto out_put_task_struct;
}
ret = ptrace_check_attach(child, request == PTRACE_KILL ||
request == PTRACE_INTERRUPT);
if (ret < 0)
goto out_put_task_struct;
ret = arch_ptrace(child, request, addr, data);
out_put_task_struct:
put_task_struct(child);
out:
return ret;
}
引数requestがPTRACE_SEIZなら task->ptrace=PT_PTRACED | PT_SEIZEDのトレース属性設定、PT_SEIZEDはforkの子プロセス作成時にチェックされ、設定されてるなら、子プロセスのptrace/parentに、親プロセスのptrace/parentが設定されて、子の子プロセスもトレースされる。
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;
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);
if (!seize)
send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
spin_lock(&task->sighand->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;
}





