diff options
author | David Howells <dhowells@redhat.com> | 2008-11-13 18:39:19 -0500 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2008-11-13 18:39:19 -0500 |
commit | c69e8d9c01db2adc503464993c358901c9af9de4 (patch) | |
tree | bed94aaa9aeb7a7834d1c880f72b62a11a752c78 /kernel/signal.c | |
parent | 86a264abe542cfececb4df129bc45a0338d8cdb9 (diff) |
CRED: Use RCU to access another task's creds and to release a task's own creds
Use RCU to access another task's creds and to release a task's own creds.
This means that it will be possible for the credentials of a task to be
replaced without another task (a) requiring a full lock to read them, and (b)
seeing deallocated memory.
Signed-off-by: David Howells <dhowells@redhat.com>
Acked-by: James Morris <jmorris@namei.org>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'kernel/signal.c')
-rw-r--r-- | kernel/signal.c | 49 |
1 files changed, 31 insertions, 18 deletions
diff --git a/kernel/signal.c b/kernel/signal.c index 80e8a6489f97..84989124bafb 100644 --- a/kernel/signal.c +++ b/kernel/signal.c | |||
@@ -177,6 +177,11 @@ int next_signal(struct sigpending *pending, sigset_t *mask) | |||
177 | return sig; | 177 | return sig; |
178 | } | 178 | } |
179 | 179 | ||
180 | /* | ||
181 | * allocate a new signal queue record | ||
182 | * - this may be called without locks if and only if t == current, otherwise an | ||
183 | * appopriate lock must be held to protect t's user_struct | ||
184 | */ | ||
180 | static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags, | 185 | static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags, |
181 | int override_rlimit) | 186 | int override_rlimit) |
182 | { | 187 | { |
@@ -184,11 +189,12 @@ static struct sigqueue *__sigqueue_alloc(struct task_struct *t, gfp_t flags, | |||
184 | struct user_struct *user; | 189 | struct user_struct *user; |
185 | 190 | ||
186 | /* | 191 | /* |
187 | * In order to avoid problems with "switch_user()", we want to make | 192 | * We won't get problems with the target's UID changing under us |
188 | * sure that the compiler doesn't re-load "t->user" | 193 | * because changing it requires RCU be used, and if t != current, the |
194 | * caller must be holding the RCU readlock (by way of a spinlock) and | ||
195 | * we use RCU protection here | ||
189 | */ | 196 | */ |
190 | user = t->cred->user; | 197 | user = __task_cred(t)->user; |
191 | barrier(); | ||
192 | atomic_inc(&user->sigpending); | 198 | atomic_inc(&user->sigpending); |
193 | if (override_rlimit || | 199 | if (override_rlimit || |
194 | atomic_read(&user->sigpending) <= | 200 | atomic_read(&user->sigpending) <= |
@@ -562,12 +568,13 @@ static int rm_from_queue(unsigned long mask, struct sigpending *s) | |||
562 | 568 | ||
563 | /* | 569 | /* |
564 | * Bad permissions for sending the signal | 570 | * Bad permissions for sending the signal |
571 | * - the caller must hold at least the RCU read lock | ||
565 | */ | 572 | */ |
566 | static int check_kill_permission(int sig, struct siginfo *info, | 573 | static int check_kill_permission(int sig, struct siginfo *info, |
567 | struct task_struct *t) | 574 | struct task_struct *t) |
568 | { | 575 | { |
576 | const struct cred *cred = current_cred(), *tcred; | ||
569 | struct pid *sid; | 577 | struct pid *sid; |
570 | uid_t uid, euid; | ||
571 | int error; | 578 | int error; |
572 | 579 | ||
573 | if (!valid_signal(sig)) | 580 | if (!valid_signal(sig)) |
@@ -580,10 +587,11 @@ static int check_kill_permission(int sig, struct siginfo *info, | |||
580 | if (error) | 587 | if (error) |
581 | return error; | 588 | return error; |
582 | 589 | ||
583 | uid = current_uid(); | 590 | tcred = __task_cred(t); |
584 | euid = current_euid(); | 591 | if ((cred->euid ^ tcred->suid) && |
585 | if ((euid ^ t->cred->suid) && (euid ^ t->cred->uid) && | 592 | (cred->euid ^ tcred->uid) && |
586 | (uid ^ t->cred->suid) && (uid ^ t->cred->uid) && | 593 | (cred->uid ^ tcred->suid) && |
594 | (cred->uid ^ tcred->uid) && | ||
587 | !capable(CAP_KILL)) { | 595 | !capable(CAP_KILL)) { |
588 | switch (sig) { | 596 | switch (sig) { |
589 | case SIGCONT: | 597 | case SIGCONT: |
@@ -1011,6 +1019,10 @@ struct sighand_struct *lock_task_sighand(struct task_struct *tsk, unsigned long | |||
1011 | return sighand; | 1019 | return sighand; |
1012 | } | 1020 | } |
1013 | 1021 | ||
1022 | /* | ||
1023 | * send signal info to all the members of a group | ||
1024 | * - the caller must hold the RCU read lock at least | ||
1025 | */ | ||
1014 | int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p) | 1026 | int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p) |
1015 | { | 1027 | { |
1016 | unsigned long flags; | 1028 | unsigned long flags; |
@@ -1032,8 +1044,8 @@ int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p) | |||
1032 | /* | 1044 | /* |
1033 | * __kill_pgrp_info() sends a signal to a process group: this is what the tty | 1045 | * __kill_pgrp_info() sends a signal to a process group: this is what the tty |
1034 | * control characters do (^C, ^Z etc) | 1046 | * control characters do (^C, ^Z etc) |
1047 | * - the caller must hold at least a readlock on tasklist_lock | ||
1035 | */ | 1048 | */ |
1036 | |||
1037 | int __kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp) | 1049 | int __kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp) |
1038 | { | 1050 | { |
1039 | struct task_struct *p = NULL; | 1051 | struct task_struct *p = NULL; |
@@ -1089,6 +1101,7 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid, | |||
1089 | { | 1101 | { |
1090 | int ret = -EINVAL; | 1102 | int ret = -EINVAL; |
1091 | struct task_struct *p; | 1103 | struct task_struct *p; |
1104 | const struct cred *pcred; | ||
1092 | 1105 | ||
1093 | if (!valid_signal(sig)) | 1106 | if (!valid_signal(sig)) |
1094 | return ret; | 1107 | return ret; |
@@ -1099,9 +1112,11 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid, | |||
1099 | ret = -ESRCH; | 1112 | ret = -ESRCH; |
1100 | goto out_unlock; | 1113 | goto out_unlock; |
1101 | } | 1114 | } |
1102 | if ((info == SEND_SIG_NOINFO || (!is_si_special(info) && SI_FROMUSER(info))) | 1115 | pcred = __task_cred(p); |
1103 | && (euid != p->cred->suid) && (euid != p->cred->uid) | 1116 | if ((info == SEND_SIG_NOINFO || |
1104 | && (uid != p->cred->suid) && (uid != p->cred->uid)) { | 1117 | (!is_si_special(info) && SI_FROMUSER(info))) && |
1118 | euid != pcred->suid && euid != pcred->uid && | ||
1119 | uid != pcred->suid && uid != pcred->uid) { | ||
1105 | ret = -EPERM; | 1120 | ret = -EPERM; |
1106 | goto out_unlock; | 1121 | goto out_unlock; |
1107 | } | 1122 | } |
@@ -1372,10 +1387,9 @@ int do_notify_parent(struct task_struct *tsk, int sig) | |||
1372 | */ | 1387 | */ |
1373 | rcu_read_lock(); | 1388 | rcu_read_lock(); |
1374 | info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns); | 1389 | info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns); |
1390 | info.si_uid = __task_cred(tsk)->uid; | ||
1375 | rcu_read_unlock(); | 1391 | rcu_read_unlock(); |
1376 | 1392 | ||
1377 | info.si_uid = tsk->cred->uid; | ||
1378 | |||
1379 | thread_group_cputime(tsk, &cputime); | 1393 | thread_group_cputime(tsk, &cputime); |
1380 | info.si_utime = cputime_to_jiffies(cputime.utime); | 1394 | info.si_utime = cputime_to_jiffies(cputime.utime); |
1381 | info.si_stime = cputime_to_jiffies(cputime.stime); | 1395 | info.si_stime = cputime_to_jiffies(cputime.stime); |
@@ -1443,10 +1457,9 @@ static void do_notify_parent_cldstop(struct task_struct *tsk, int why) | |||
1443 | */ | 1457 | */ |
1444 | rcu_read_lock(); | 1458 | rcu_read_lock(); |
1445 | info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns); | 1459 | info.si_pid = task_pid_nr_ns(tsk, tsk->parent->nsproxy->pid_ns); |
1460 | info.si_uid = __task_cred(tsk)->uid; | ||
1446 | rcu_read_unlock(); | 1461 | rcu_read_unlock(); |
1447 | 1462 | ||
1448 | info.si_uid = tsk->cred->uid; | ||
1449 | |||
1450 | info.si_utime = cputime_to_clock_t(tsk->utime); | 1463 | info.si_utime = cputime_to_clock_t(tsk->utime); |
1451 | info.si_stime = cputime_to_clock_t(tsk->stime); | 1464 | info.si_stime = cputime_to_clock_t(tsk->stime); |
1452 | 1465 | ||
@@ -1713,7 +1726,7 @@ static int ptrace_signal(int signr, siginfo_t *info, | |||
1713 | info->si_errno = 0; | 1726 | info->si_errno = 0; |
1714 | info->si_code = SI_USER; | 1727 | info->si_code = SI_USER; |
1715 | info->si_pid = task_pid_vnr(current->parent); | 1728 | info->si_pid = task_pid_vnr(current->parent); |
1716 | info->si_uid = current->parent->cred->uid; | 1729 | info->si_uid = task_uid(current->parent); |
1717 | } | 1730 | } |
1718 | 1731 | ||
1719 | /* If the (new) signal is now blocked, requeue it. */ | 1732 | /* If the (new) signal is now blocked, requeue it. */ |