diff options
author | Thomas Gleixner <tglx@linutronix.de> | 2009-12-09 19:53:17 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2009-12-10 17:04:11 -0500 |
commit | 14d8c9f3c09e7fd7b9af80904289fe204f5b93c6 (patch) | |
tree | 1c373436517c5853aab0e99cce83364a21134d0e | |
parent | ea5b41f9d595be354f7a50e56b28c2d72e6e88a5 (diff) |
signal: Fix racy access to __task_cred in kill_pid_info_as_uid()
kill_pid_info_as_uid() accesses __task_cred() without being in a RCU
read side critical section. tasklist_lock is not protecting that when
CONFIG_TREE_PREEMPT_RCU=y.
Convert the whole tasklist_lock section to rcu and use
lock_task_sighand to prevent the exit race.
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
LKML-Reference: <20091210004703.232302055@linutronix.de>
Acked-by: Oleg Nesterov <oleg@redhat.com>
-rw-r--r-- | kernel/signal.c | 17 |
1 files changed, 10 insertions, 7 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index 6b982f2cf524..73316568a69c 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
@@ -1175,11 +1175,12 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid, | |||
1175 | int ret = -EINVAL; | 1175 | int ret = -EINVAL; |
1176 | struct task_struct *p; | 1176 | struct task_struct *p; |
1177 | const struct cred *pcred; | 1177 | const struct cred *pcred; |
1178 | unsigned long flags; | ||
1178 | 1179 | ||
1179 | if (!valid_signal(sig)) | 1180 | if (!valid_signal(sig)) |
1180 | return ret; | 1181 | return ret; |
1181 | 1182 | ||
1182 | read_lock(&tasklist_lock); | 1183 | rcu_read_lock(); |
1183 | p = pid_task(pid, PIDTYPE_PID); | 1184 | p = pid_task(pid, PIDTYPE_PID); |
1184 | if (!p) { | 1185 | if (!p) { |
1185 | ret = -ESRCH; | 1186 | ret = -ESRCH; |
@@ -1196,14 +1197,16 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid, | |||
1196 | ret = security_task_kill(p, info, sig, secid); | 1197 | ret = security_task_kill(p, info, sig, secid); |
1197 | if (ret) | 1198 | if (ret) |
1198 | goto out_unlock; | 1199 | goto out_unlock; |
1199 | if (sig && p->sighand) { | 1200 | |
1200 | unsigned long flags; | 1201 | if (sig) { |
1201 | spin_lock_irqsave(&p->sighand->siglock, flags); | 1202 | if (lock_task_sighand(p, &flags)) { |
1202 | ret = __send_signal(sig, info, p, 1, 0); | 1203 | ret = __send_signal(sig, info, p, 1, 0); |
1203 | spin_unlock_irqrestore(&p->sighand->siglock, flags); | 1204 | unlock_task_sighand(p, &flags); |
1205 | } else | ||
1206 | ret = -ESRCH; | ||
1204 | } | 1207 | } |
1205 | out_unlock: | 1208 | out_unlock: |
1206 | read_unlock(&tasklist_lock); | 1209 | rcu_read_unlock(); |
1207 | return ret; | 1210 | return ret; |
1208 | } | 1211 | } |
1209 | EXPORT_SYMBOL_GPL(kill_pid_info_as_uid); | 1212 | EXPORT_SYMBOL_GPL(kill_pid_info_as_uid); |