summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2008-08-07 20:02:16 +1000
committerJames Morris <jmorris@namei.org>2008-08-07 20:02:16 +1000
commitcf7c7a530e568ca54ba3a3c021c7e7efb71fe10f (patch)
tree6682921a0229b43ef256251ab527f317a4c4fd2b /kernel
parentbdf4cba5d9ff34788307fe66fbefcf43ba50be19 (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.c61
-rw-r--r--kernel/fork.c8
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(&current->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);
}