子プロセスの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;
}

最終更新 2018/07/25 13:23:50 - north
(2018/07/25 13:23:50 作成)


検索

アクセス数
3575173
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。