diff options
author | David Howells <dhowells@redhat.com> | 2008-08-07 20:02:16 +1000 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2008-08-07 20:02:16 +1000 |
commit | cf7c7a530e568ca54ba3a3c021c7e7efb71fe10f (patch) | |
tree | 6682921a0229b43ef256251ab527f317a4c4fd2b /kernel | |
parent | bdf4cba5d9ff34788307fe66fbefcf43ba50be19 (diff) |
CRED: Separate per-task-group keyrings from signal_struct
Separate per-task-group keyrings from signal_struct and dangle their anchor
from the cred struct rather than the signal_struct.
Signed-off-by: David Howells <dhowells@redhat.com>
Reviewed-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/cred.c | 61 | ||||
-rw-r--r-- | kernel/fork.c | 8 |
2 files changed, 61 insertions, 8 deletions
diff --git a/kernel/cred.c b/kernel/cred.c index 2195841ce9ef..6090cb650298 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -17,6 +17,17 @@ #include <linux/security.h> /* + * The common credentials for the initial task's thread group + */ +#ifdef CONFIG_KEYS +static struct thread_group_cred init_tgcred = { + .usage = ATOMIC_INIT(2), + .tgid = 0, + .lock = SPIN_LOCK_UNLOCKED, +}; +#endif + +/* * The initial credentials for the initial task */ struct cred init_cred = { @@ -28,9 +39,40 @@ struct cred init_cred = { .cap_bset = CAP_INIT_BSET, .user = INIT_USER, .group_info = &init_groups, +#ifdef CONFIG_KEYS + .tgcred = &init_tgcred, +#endif }; /* + * Dispose of the shared task group credentials + */ +#ifdef CONFIG_KEYS +static void put_tgcred_rcu(struct rcu_head *rcu) +{ + struct thread_group_cred *tgcred = + container_of(rcu, struct thread_group_cred, rcu); + + BUG_ON(atomic_read(&tgcred->usage) != 0); + + key_put(tgcred->session_keyring); + key_put(tgcred->process_keyring); + kfree(tgcred); +} +#endif + +/* + * Release a set of thread group credentials. + */ +static void put_tgcred(struct thread_group_cred *tgcred) +{ +#ifdef CONFIG_KEYS + if (atomic_dec_and_test(&tgcred->usage)) + call_rcu(&tgcred->rcu, put_tgcred_rcu); +#endif +} + +/* * The RCU callback to actually dispose of a set of credentials */ static void put_cred_rcu(struct rcu_head *rcu) @@ -41,6 +83,7 @@ static void put_cred_rcu(struct rcu_head *rcu) key_put(cred->thread_keyring); key_put(cred->request_key_auth); + put_tgcred(cred->tgcred); put_group_info(cred->group_info); free_uid(cred->user); security_cred_free(cred); @@ -71,12 +114,30 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags) if (!pcred) return -ENOMEM; +#ifdef CONFIG_KEYS + if (clone_flags & CLONE_THREAD) { + atomic_inc(&pcred->tgcred->usage); + } else { + pcred->tgcred = kmalloc(sizeof(struct cred), GFP_KERNEL); + if (!pcred->tgcred) { + kfree(pcred); + return -ENOMEM; + } + atomic_set(&pcred->tgcred->usage, 1); + spin_lock_init(&pcred->tgcred->lock); + pcred->tgcred->process_keyring = NULL; + pcred->tgcred->session_keyring = + key_get(p->cred->tgcred->session_keyring); + } +#endif + #ifdef CONFIG_SECURITY pcred->security = NULL; #endif ret = security_cred_alloc(pcred); if (ret < 0) { + put_tgcred(pcred->tgcred); kfree(pcred); return ret; } diff --git a/kernel/fork.c b/kernel/fork.c index 8993a4574f04..b3254d5c2f4a 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -760,7 +760,6 @@ void __cleanup_sighand(struct sighand_struct *sighand) static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) { struct signal_struct *sig; - int ret; if (clone_flags & CLONE_THREAD) { atomic_inc(¤t->signal->count); @@ -772,12 +771,6 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) if (!sig) return -ENOMEM; - ret = copy_thread_group_keys(tsk); - if (ret < 0) { - kmem_cache_free(signal_cachep, sig); - return ret; - } - atomic_set(&sig->count, 1); atomic_set(&sig->live, 1); init_waitqueue_head(&sig->wait_chldexit); @@ -835,7 +828,6 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) void __cleanup_signal(struct signal_struct *sig) { - exit_thread_group_keys(sig); kmem_cache_free(signal_cachep, sig); } |