diff options
author | Oleg Nesterov <oleg@tv-sign.ru> | 2008-02-08 07:19:18 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-08 12:22:28 -0500 |
commit | d36174bc2bce0372693a9cfbdef8b2689c9982cb (patch) | |
tree | cdb327c68844f105a8ac8b732e862f24edc35f24 /kernel/signal.c | |
parent | f374ada53bd1ca7c16d7607369fccc6769704956 (diff) |
uglify kill_pid_info() to fix kill() vs exec() race
kill_pid_info()->pid_task() could be the old leader of the execing process.
In that case it is possible that the leader will be released before we take
siglock. This means that kill_pid_info() (and thus sys_kill()) can return a
false -ESRCH.
Change the code to retry when lock_task_sighand() fails. The endless loop is
not possible, __exit_signal() both clears ->sighand and does detach_pid().
Signed-off-by: Oleg Nesterov <oleg@tv-sign.ru>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Davide Libenzi <davidel@xmailserver.org>
Cc: Pavel Emelyanov <xemul@openvz.org>
Cc: Roland McGrath <roland@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/signal.c')
-rw-r--r-- | kernel/signal.c | 15 |
1 files changed, 12 insertions, 3 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index 3d3adb94561d..b0b43a4ad8dd 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
@@ -1050,17 +1050,26 @@ int kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp) | |||
1050 | 1050 | ||
1051 | int kill_pid_info(int sig, struct siginfo *info, struct pid *pid) | 1051 | int kill_pid_info(int sig, struct siginfo *info, struct pid *pid) |
1052 | { | 1052 | { |
1053 | int error; | 1053 | int error = -ESRCH; |
1054 | struct task_struct *p; | 1054 | struct task_struct *p; |
1055 | 1055 | ||
1056 | rcu_read_lock(); | 1056 | rcu_read_lock(); |
1057 | if (unlikely(sig_needs_tasklist(sig))) | 1057 | if (unlikely(sig_needs_tasklist(sig))) |
1058 | read_lock(&tasklist_lock); | 1058 | read_lock(&tasklist_lock); |
1059 | 1059 | ||
1060 | retry: | ||
1060 | p = pid_task(pid, PIDTYPE_PID); | 1061 | p = pid_task(pid, PIDTYPE_PID); |
1061 | error = -ESRCH; | 1062 | if (p) { |
1062 | if (p) | ||
1063 | error = group_send_sig_info(sig, info, p); | 1063 | error = group_send_sig_info(sig, info, p); |
1064 | if (unlikely(error == -ESRCH)) | ||
1065 | /* | ||
1066 | * The task was unhashed in between, try again. | ||
1067 | * If it is dead, pid_task() will return NULL, | ||
1068 | * if we race with de_thread() it will find the | ||
1069 | * new leader. | ||
1070 | */ | ||
1071 | goto retry; | ||
1072 | } | ||
1064 | 1073 | ||
1065 | if (unlikely(sig_needs_tasklist(sig))) | 1074 | if (unlikely(sig_needs_tasklist(sig))) |
1066 | read_unlock(&tasklist_lock); | 1075 | read_unlock(&tasklist_lock); |