killシステムコール
Rev.3を表示中。最新版はこちら。
pid>0なら、カレントプロセスnamespaceのpidのプロセスに送信します。pid<-1なら、-pidのプロセスのプロセスグループに送信します。
pid=0なら、カレントプロセスグループのプロセスに送信します。
pid=-1なら、initプロセスとカレントプロセス及びそのスレッドグループ以外のプロセスに送信します。
SYSCALL_DEFINE2(kill, pid_t, pid, int, sig) { struct siginfo info; info.si_signo = sig; info.si_errno = 0; info.si_code = SI_USER; info.si_pid = task_tgid_vnr(current); info.si_uid = current_uid(); return kill_something_info(sig, &info, pid); } static int kill_something_info(int sig, struct siginfo *info, pid_t pid) { int ret; if (pid > 0) { rcu_read_lock(); ret = kill_pid_info(sig, info, find_vpid(pid)); rcu_read_unlock(); return ret; } read_lock(&tasklist_lock); if (pid != -1) { ret = __kill_pgrp_info(sig, info, pid ? find_vpid(-pid) : task_pgrp(current)); } else { int retval = 0, count = 0; struct task_struct * p; for_each_process(p) { if (task_pid_vnr(p) > 1 && !same_thread_group(p, current)) { int err = group_send_sig_info(sig, info, p); ++count; if (err != -EPERM) retval = err; } } ret = count ? retval : -ESRCH; } read_unlock(&tasklist_lock); return ret; }kill_pid_info()は、hlistのノードとするpid->tasks[PIDTYPE_PID]から取得したタスクに、シグナルを送信します。group_send_sig_info()は、送信するタスクのスレッドグループのタスクにも送信します。
int kill_pid_info(int sig, struct siginfo *info, struct pid *pid) { int error = -ESRCH; struct task_struct *p; rcu_read_lock(); retry: p = pid_task(pid, PIDTYPE_PID); if (p) { error = group_send_sig_info(sig, info, p); if (unlikely(error == -ESRCH)) goto retry; } rcu_read_unlock(); return error; }__kill_pid_info()は、pgrpをヘッドとするPIDTYPE_PGIDの全タスクにシグナルを送信します。PIDTYPE_PGIDを引数とするdo_each_pid_taskマクロは、取得したPIDTYPE_PGIDのプロセスの子プロセスも走査するべく、2重ループとなります。
int __kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp) { struct task_struct *p = NULL; int retval, success; success = 0; retval = -ESRCH; do_each_pid_task(pgrp, PIDTYPE_PGID, p) { int err = group_send_sig_info(sig, info, p); success |= !err; retval = err; } while_each_pid_task(pgrp, PIDTYPE_PGID, p); return success ? 0 : retval; }do_send_sig_info()でシグナル送信しますが、実際の処理はペンディングとしてリストするだけです。group_send_sig_info()はタスクのスレッドも送信するため、do_send_sig_info()でtrueとし、t->signal->shared_pendingにリストされ、falseならt->pendingにリストされます。tkillシステムコール等は、falseでコールされ、そのタスクにしかシグナル処理されません。
int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p) { int ret; rcu_read_lock(); ret = check_kill_permission(sig, info, p); rcu_read_unlock(); if (!ret && sig) ret = do_send_sig_info(sig, info, p, true); return ret; }スレッドグループでないタスクへの送信は、送信タスクのuid/euidが、送信されるタスクのsuid/uidでなければなりません。通常uid=euid=suidですので、同じuidプロセス(親子)間でしかシグナルを送信できない。という事です。
セキュリティ上、一時的にroot権限を放棄するため、euidのみを一般権限へ移行する実装が可能で、権限移行しても先のuid(通常root)へもシグナル送信することができるように。という事でしょう。
static int check_kill_permission(int sig, struct siginfo *info, struct task_struct *t) { struct pid *sid; int error; if (!valid_signal(sig)) return -EINVAL; if (!si_fromuser(info)) return 0; error = audit_signal_info(sig, t); /* Let audit system see the signal */ if (error) return error; if (!same_thread_group(current, t) && !kill_ok_by_cred(t)) { switch (sig) { case SIGCONT: sid = task_session(t); if (!sid || sid == task_session(current)) break; default: return -EPERM; } } return security_task_kill(t, info, sig, 0); } static int kill_ok_by_cred(struct task_struct *t) { const struct cred *cred = current_cred(); const struct cred *tcred = __task_cred(t); if (cred->user->user_ns == tcred->user->user_ns && (cred->euid == tcred->suid || cred->euid == tcred->uid || cred->uid == tcred->suid || cred->uid == tcred->uid)) return 1; if (ns_capable(tcred->user->user_ns, CAP_KILL)) return 1; return 0; }
補足
pid=-1でkillをコールする事は、カーネルが起動し最初のユーザプロセスで全プロセスの親でもある/sbin/initが起動した直後の状態で、カレントプロセス及びそのスレッドプロセスを起動した状態となります。ssh等のプロセスも削除されますが、そのシェルから起動したkillをコールしたプロセス(スレッド)は動作し続けます。