pagg
[Top] [All Lists]

[PATCH] rcu-pnotify aware version of keyring support

To: Christoph Hellwig <hch@xxxxxxxxxxxxx>
Subject: [PATCH] rcu-pnotify aware version of keyring support
From: Erik Jacobson <erikj@xxxxxxx>
Date: Thu, 29 Sep 2005 11:58:29 -0500
Cc: Erik Jacobson <erikj@xxxxxxx>, lse-tech@xxxxxxxxxxxxxxxxxxxxx, akpm@xxxxxxxx, kingsley@xxxxxxxxxx, canon@xxxxxxxxx, pagg@xxxxxxxxxxx
In-reply-to: <20050922151647.GA30784@infradead.org>
References: <20050921213645.GB28239@sgi.com> <20050922151647.GA30784@infradead.org>
Sender: pagg-bounce@xxxxxxxxxxx
User-agent: Mutt/1.5.6i
This patch implements some parts of the keyring support using the rcu
version of pnotify.  It may also be illegal because it could sleep with
the rcu_read_lock open.

See my performance post to follow shortly for a discussion on these things.

 include/linux/key.h          |   21 ++++-
 include/linux/sched.h        |    4
 kernel/exit.c                |    1
 kernel/fork.c                |    6 -
 security/keys/key.c          |   24 +++++
 security/keys/keyctl.c       |   34 +++++++-
 security/keys/process_keys.c |  173 
+++++++++++++++++++++++++++++++++++++------ security/keys/request_key.c  |   37 
++++++++-
 8 files changed, 257 insertions(+), 43 deletions(-)


Index: linux/include/linux/key.h
===================================================================
--- linux.orig/include/linux/key.h      2005-09-27 16:01:17.561027702 -0500
+++ linux/include/linux/key.h   2005-09-27 16:05:34.078204518 -0500
@@ -19,6 +19,7 @@
 #include <linux/list.h>
 #include <linux/rbtree.h>
 #include <linux/rcupdate.h>
+#include <linux/pnotify.h>
 #include <asm/atomic.h>
 
 #ifdef __KERNEL__
@@ -262,9 +263,9 @@
 extern struct key root_user_keyring, root_session_keyring;
 extern int alloc_uid_keyring(struct user_struct *user);
 extern void switch_uid_keyring(struct user_struct *new_user);
-extern int copy_keys(unsigned long clone_flags, struct task_struct *tsk);
+extern int copy_keys(struct task_struct *tsk, struct pnotify_subscriber *sub, 
void *olddata);
 extern int copy_thread_group_keys(struct task_struct *tsk);
-extern void exit_keys(struct task_struct *tsk);
+extern void exit_keys(struct task_struct *task, struct pnotify_subscriber 
*sub);
 extern void exit_thread_group_keys(struct signal_struct *tg);
 extern int suid_keys(struct task_struct *tsk);
 extern int exec_keys(struct task_struct *tsk);
@@ -279,6 +280,22 @@
        old_session;                                            \
 })
 
+/* pnotify subscriber service request */
+static struct pnotify_events key_events = {
+       .module = NULL,
+       .name = "key",
+       .data = NULL,
+       .entry = LIST_HEAD_INIT(key_events.entry),
+       .fork = copy_keys,
+       .exit = exit_keys,
+};
+
+/* key info associated with the task struct and managed by pnotify */
+struct key_task {
+       struct key *thread_keyring; /* keyring private to this thread */
+       unsigned char jit_keyring; /* default keyring to attach requested keys 
to */
+};
+
 #else /* CONFIG_KEYS */
 
 #define key_validate(k)                        0
Index: linux/kernel/exit.c
===================================================================
--- linux.orig/kernel/exit.c    2005-09-27 16:01:17.562004166 -0500
+++ linux/kernel/exit.c 2005-09-27 16:05:34.082110375 -0500
@@ -857,7 +857,6 @@
        exit_namespace(tsk);
        exit_thread();
        cpuset_exit(tsk);
-       exit_keys(tsk);
 
        if (group_dead && tsk->signal->leader)
                disassociate_ctty(1);
Index: linux/kernel/fork.c
===================================================================
--- linux.orig/kernel/fork.c    2005-09-27 16:01:17.562004166 -0500
+++ linux/kernel/fork.c 2005-09-27 16:05:34.083086839 -0500
@@ -1009,10 +1009,8 @@
                goto bad_fork_cleanup_sighand;
        if ((retval = copy_mm(clone_flags, p)))
                goto bad_fork_cleanup_signal;
-       if ((retval = copy_keys(clone_flags, p)))
-               goto bad_fork_cleanup_mm;
        if ((retval = copy_namespace(clone_flags, p)))
-               goto bad_fork_cleanup_keys;
+               goto bad_fork_cleanup_mm;
        retval = copy_thread(0, clone_flags, stack_start, stack_size, p, regs);
        if (retval)
                goto bad_fork_cleanup_namespace;
@@ -1175,8 +1173,6 @@
 bad_fork_cleanup_namespace:
        pnotify_exit(p);
        exit_namespace(p);
-bad_fork_cleanup_keys:
-       exit_keys(p);
 bad_fork_cleanup_mm:
        if (p->mm)
                mmput(p->mm);
Index: linux/security/keys/key.c
===================================================================
--- linux.orig/security/keys/key.c      2005-09-27 16:01:17.562980631 -0500
+++ linux/security/keys/key.c   2005-09-27 16:05:34.088945625 -0500
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/workqueue.h>
 #include <linux/err.h>
+#include <linux/pnotify.h>
 #include "internal.h"
 
 static kmem_cache_t    *key_jar;
@@ -1009,6 +1010,9 @@
  */
 void __init key_init(void)
 {
+       struct key_task *kt;
+       struct pnotify_subscriber *sub;
+
        /* allocate a slab in which we can store keys */
        key_jar = kmem_cache_create("key_jar", sizeof(struct key),
                        0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL, NULL);
@@ -1039,4 +1043,24 @@
        /* link the two root keyrings together */
        key_link(&root_session_keyring, &root_user_keyring);
 
+       /* Allocate memory for task assocated key_task structure */
+       kt = (struct key_task *)kmalloc(sizeof(struct key_task),GFP_KERNEL);
+       if (!kt) {
+               printk(KERN_ERR "Insufficient memory to allocate key_task 
structure"
+                 " in key_init function.\n");
+               return;
+   }
+       kt->thread_keyring = NULL;
+
+       /* subscribe this kernel entity to the subscriber list for current task 
*/
+       /* Here, there is only one process in existence so we don't do any 
locking.
+        */
+       sub = pnotify_subscribe(current, &key_events);
+       if (!sub) {
+               printk(KERN_ERR "Insufficient memory to add to subscriber list 
structure"
+                 " in key_init function.\n");
+       }
+       /* Associate the kt structure with this task via pnotify subscriber */
+       sub->data = (void *)kt;
+
 } /* end key_init() */
Index: linux/security/keys/process_keys.c
===================================================================
--- linux.orig/security/keys/process_keys.c     2005-09-27 16:01:17.562980631 
-0500
+++ linux/security/keys/process_keys.c  2005-09-27 16:17:39.829430380 -0500
@@ -16,6 +16,7 @@
 #include <linux/keyctl.h>
 #include <linux/fs.h>
 #include <linux/err.h>
+#include <linux/pnotify.h>
 #include <asm/uaccess.h>
 #include "internal.h"
 
@@ -137,6 +138,8 @@
 int install_thread_keyring(struct task_struct *tsk)
 {
        struct key *keyring, *old;
+       struct key_task *kt;
+       struct pnotify_subscriber *sub;
        char buf[20];
        int ret;
 
@@ -149,9 +152,24 @@
        }
 
        task_lock(tsk);
-       old = tsk->thread_keyring;
-       tsk->thread_keyring = keyring;
+       rcu_read_lock();
+       down_write(&tsk->pnotify_subscriber_list_sem);
+       sub = pnotify_get_subscriber(tsk, key_events.name);
+       if (sub == NULL || sub->data == NULL) { /* shouldn't happen */
+               printk(KERN_ERR "install_thread_keyring pnotify subscriber or 
data ptr null, task: %d\n", tsk->pid);
+               task_unlock(tsk);
+               up_write(&tsk->pnotify_subscriber_list_sem);
+               rcu_read_unlock();
+               ret = PTR_ERR(sub);
+               goto error;
+       }
+       kt = (struct key_task *)sub->data;
+
+       old = kt->thread_keyring;
+       kt->thread_keyring = keyring;
        task_unlock(tsk);
+       up_write(&tsk->pnotify_subscriber_list_sem);
+       rcu_read_unlock();
 
        ret = 0;
 
@@ -267,13 +285,27 @@
 /*
  * copy the keys for fork
  */
-int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
+int copy_keys(struct task_struct *tsk, struct pnotify_subscriber *sub, void 
*olddata)
 {
-       key_check(tsk->thread_keyring);
+       struct key_task *kt = ((struct key_task *)(sub->data));
+
+       /* Allocate memory for task-associated key_task structure */
+       kt = (struct key_task *)kmalloc(sizeof(struct key_task),GFP_KERNEL);
+       if (!kt) {
+               printk(KERN_ERR "Insufficient memory to allocate key_task 
structure"
+                 " in copy_keys function.  Task was: %d", tsk->pid);
+               return PNOTIFY_ERROR;
+       }
+       /* Associate key_task structure with the new child via pnotify 
subscriber */
+       /* At this moment, this is an in-construction-task so locking isn't an
+        * issue */
+       sub->data = (void *)kt;
+
+       key_check(kt->thread_keyring);
 
        /* no thread keyring yet */
-       tsk->thread_keyring = NULL;
-       return 0;
+       kt->thread_keyring = NULL;
+       return PNOTIFY_OK;
 
 } /* end copy_keys() */
 
@@ -292,9 +324,16 @@
 /*
  * dispose of keys upon thread exit
  */
-void exit_keys(struct task_struct *tsk)
+void exit_keys(struct task_struct *task, struct pnotify_subscriber *sub)
 {
-       key_put(tsk->thread_keyring);
+       struct key_task *kt = ((struct key_task *)(sub->data));
+       if (kt == NULL) { /* shouldn't ever happen */
+               printk(KERN_ERR "exit_keys pnotify subscriber data ptr null, 
task: %d\n", task->pid);
+               return;
+       }
+       key_put(kt->thread_keyring);
+       kfree(kt); /* Free pnotify subscriber data for this task */
+       sub->data = NULL;
 
 } /* end exit_keys() */
 
@@ -306,12 +345,31 @@
 {
        unsigned long flags;
        struct key *old;
+       struct key_task *kt;
+       struct pnotify_subscriber *sub;
 
-       /* newly exec'd tasks don't get a thread keyring */
        task_lock(tsk);
-       old = tsk->thread_keyring;
-       tsk->thread_keyring = NULL;
+       /* pnotify doesn't have a compute_creds event at this time, so we
+        * need to retrieve the data */
+
+       rcu_read_lock();
+       down_write(&tsk->pnotify_subscriber_list_sem);
+       sub = pnotify_get_subscriber(tsk, key_events.name);
+       if (sub == NULL || sub->data == NULL) { /* shouldn't happen */
+               printk(KERN_ERR "exec_keys pnotify subscriber or data ptr null, 
task: %d\n", tsk->pid);
+               task_unlock(tsk);
+               up_write(&tsk->pnotify_subscriber_list_sem);
+               rcu_read_unlock();
+               return PNOTIFY_OK; /* key structures not populated yet */
+       }
+       kt = (struct key_task *)sub->data;
+
+       /* newly exec'd tasks don't get a thread keyring */
+       old = kt->thread_keyring;
+       kt->thread_keyring = NULL;
        task_unlock(tsk);
+       up_write(&tsk->pnotify_subscriber_list_sem);
+       rcu_read_unlock();
 
        key_put(old);
 
@@ -344,12 +402,29 @@
  */
 void key_fsuid_changed(struct task_struct *tsk)
 {
+       struct key_task *kt;
+       struct pnotify_subscriber *sub;
+
+       /* no pnotify event for this, so we need to grab the data */
+       rcu_read_lock();
+       down_write(&tsk->pnotify_subscriber_list_sem);
+       sub = pnotify_get_subscriber(tsk, key_events.name);
+       if (sub == NULL || sub->data == NULL) { /* shouldn't happen */
+               printk(KERN_ERR "key_fsuid_changed pnotify subscriber or data 
ptr null, task: %d\n", tsk->pid);
+               up_write(&tsk->pnotify_subscriber_list_sem);
+               rcu_read_unlock();
+               return;
+       }
+       kt = (struct key_task *)sub->data;
+
        /* update the ownership of the thread keyring */
-       if (tsk->thread_keyring) {
-               down_write(&tsk->thread_keyring->sem);
-               tsk->thread_keyring->uid = tsk->fsuid;
-               up_write(&tsk->thread_keyring->sem);
+       if (kt->thread_keyring) {
+               down_write(&kt->thread_keyring->sem);
+               kt->thread_keyring->uid = tsk->fsuid;
+               up_write(&kt->thread_keyring->sem);
        }
+       up_write(&tsk->pnotify_subscriber_list_sem);
+       rcu_read_unlock();
 
 } /* end key_fsuid_changed() */
 
@@ -359,12 +434,29 @@
  */
 void key_fsgid_changed(struct task_struct *tsk)
 {
+       struct key_task *kt;
+       struct pnotify_subscriber *sub;
+
+       /* pnotify doesn't have an event for this, so we need to grab the data 
*/
+       rcu_read_lock();
+       down_write(&tsk->pnotify_subscriber_list_sem);
+       sub = pnotify_get_subscriber(tsk, key_events.name);
+       if (sub == NULL || sub->data == NULL) { /* shouldn't happen */
+               printk(KERN_ERR "key_fsgid_changed pnotify subscriber or data 
ptr was null, task: %d\n", tsk->pid);
+               up_write(&tsk->pnotify_subscriber_list_sem);
+               rcu_read_unlock();
+               return;
+       }
+       kt = (struct key_task *)sub->data;
+
        /* update the ownership of the thread keyring */
-       if (tsk->thread_keyring) {
-               down_write(&tsk->thread_keyring->sem);
-               tsk->thread_keyring->gid = tsk->fsgid;
-               up_write(&tsk->thread_keyring->sem);
+       if (kt->thread_keyring) {
+               down_write(&kt->thread_keyring->sem);
+               kt->thread_keyring->gid = tsk->fsgid;
+               up_write(&kt->thread_keyring->sem);
        }
+       up_write(&tsk->pnotify_subscriber_list_sem);
+       rcu_read_unlock();
 
 } /* end key_fsgid_changed() */
 
@@ -383,6 +475,8 @@
 {
        struct request_key_auth *rka;
        struct key *key, *ret, *err, *instkey;
+       struct pnotify_subscriber *sub;
+       struct key_task *kt;
 
        /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
         * searchable, but we failed to find a key or we found a negative key;
@@ -395,12 +489,26 @@
        ret = NULL;
        err = ERR_PTR(-EAGAIN);
 
+       rcu_read_lock();
+       down_write(&context->pnotify_subscriber_list_sem);
+       sub = pnotify_get_subscriber(context, key_events.name);
+       if (sub == NULL || sub->data == NULL) {
+               printk(KERN_ERR "search_process_keyrings pnotify subscriber or 
data ptr null, task: %d\n", context->pid);
+               up_write(&context->pnotify_subscriber_list_sem);
+               rcu_read_unlock();
+               return (struct key *)-EFAULT;
+       }
+       kt = (struct key_task *)sub->data;
+
        /* search the thread keyring first */
-       if (context->thread_keyring) {
-               key = keyring_search_aux(context->thread_keyring,
+       if (kt->thread_keyring) {
+               key = keyring_search_aux(kt->thread_keyring,
                                         context, type, description, match);
-               if (!IS_ERR(key))
+               if (!IS_ERR(key)) {
+                       up_write(&context->pnotify_subscriber_list_sem);
+                       rcu_read_unlock();
                        goto found;
+               }
 
                switch (PTR_ERR(key)) {
                case -EAGAIN: /* no key */
@@ -414,6 +522,8 @@
                        break;
                }
        }
+       up_write(&context->pnotify_subscriber_list_sem);
+       rcu_read_unlock();
 
        /* search the process keyring second */
        if (context->signal->process_keyring) {
@@ -535,15 +645,28 @@
 {
        struct key *key;
        int ret;
+       struct pnotify_subscriber *sub;
+       struct key_task *kt;
 
        if (!context)
                context = current;
 
        key = ERR_PTR(-ENOKEY);
 
+       rcu_read_lock();
+       down_write(&context->pnotify_subscriber_list_sem);
+       sub = pnotify_get_subscriber(context, key_events.name);
+       if (sub == NULL || sub->data == NULL) { /* shouldn't happen */
+               printk(KERN_ERR "search_process_keyrings pnotify subscriber or 
data ptr null, task: %d\n", context->pid);
+               up_write(&context->pnotify_subscriber_list_sem);
+               rcu_read_unlock();
+               return (struct key *)-EFAULT;
+       }
+       kt = (struct key_task *)sub->data;
+
        switch (id) {
        case KEY_SPEC_THREAD_KEYRING:
-               if (!context->thread_keyring) {
+               if (!kt->thread_keyring) {
                        if (!create)
                                goto error;
 
@@ -554,7 +677,7 @@
                        }
                }
 
-               key = context->thread_keyring;
+               key = kt->thread_keyring;
                atomic_inc(&key->usage);
                break;
 
@@ -634,6 +757,8 @@
                goto invalid_key;
 
  error:
+       up_write(&context->pnotify_subscriber_list_sem);
+       rcu_read_unlock();
        return key;
 
  invalid_key:
Index: linux/include/linux/sched.h
===================================================================
--- linux.orig/include/linux/sched.h    2005-09-27 16:01:17.561027702 -0500
+++ linux/include/linux/sched.h 2005-09-27 16:05:34.093827947 -0500
@@ -718,10 +718,6 @@
        kernel_cap_t   cap_effective, cap_inheritable, cap_permitted;
        unsigned keep_capabilities:1;
        struct user_struct *user;
-#ifdef CONFIG_KEYS
-       struct key *thread_keyring;     /* keyring private to this thread */
-       unsigned char jit_keyring;      /* default keyring to attach requested 
keys to */
-#endif
        int oomkilladj; /* OOM kill score adjustment (bit shift). */
        char comm[TASK_COMM_LEN]; /* executable name excluding path
                                     - access with [gs]et_task_comm (which lock
Index: linux/security/keys/keyctl.c
===================================================================
--- linux.orig/security/keys/keyctl.c   2005-09-27 16:01:17.563957095 -0500
+++ linux/security/keys/keyctl.c        2005-09-27 16:18:13.257708245 -0500
@@ -931,31 +931,57 @@
 long keyctl_set_reqkey_keyring(int reqkey_defl)
 {
        int ret;
+       unsigned char jit_return;
+       struct pnotify_subscriber *sub;
+       struct key_task *kt;
+
+       rcu_read_lock();
+       down_write(&current->pnotify_subscriber_list_sem);
+       sub = pnotify_get_subscriber(current, key_events.name);
+       if (sub == NULL || sub->data == NULL) { /* shouldn't happen */
+               printk(KERN_ERR "keyctl_set_reqkey_keyring pnotify subscriber 
or data ptr null, task: %d\n", current->pid);
+               up_write(&current->pnotify_subscriber_list_sem);
+               rcu_read_unlock();
+               return -EFAULT;
+       }
+       kt = (struct key_task *)sub->data;
 
        switch (reqkey_defl) {
        case KEY_REQKEY_DEFL_THREAD_KEYRING:
                ret = install_thread_keyring(current);
-               if (ret < 0)
+               if (ret < 0) {
+                       up_write(&current->pnotify_subscriber_list_sem);
+                       rcu_read_unlock();
                        return ret;
+               }
                goto set;
 
        case KEY_REQKEY_DEFL_PROCESS_KEYRING:
                ret = install_process_keyring(current);
-               if (ret < 0)
+               if (ret < 0) {
+                       up_write(&current->pnotify_subscriber_list_sem);
+                       rcu_read_unlock();
                        return ret;
+               }
 
        case KEY_REQKEY_DEFL_DEFAULT:
        case KEY_REQKEY_DEFL_SESSION_KEYRING:
        case KEY_REQKEY_DEFL_USER_KEYRING:
        case KEY_REQKEY_DEFL_USER_SESSION_KEYRING:
        set:
-               current->jit_keyring = reqkey_defl;
+
+               kt->jit_keyring = reqkey_defl;
 
        case KEY_REQKEY_DEFL_NO_CHANGE:
-               return current->jit_keyring;
+               jit_return = kt->jit_keyring;
+               up_write(&current->pnotify_subscriber_list_sem);
+               rcu_read_unlock();
+               return jit_return;
 
        case KEY_REQKEY_DEFL_GROUP_KEYRING:
        default:
+               up_write(&current->pnotify_subscriber_list_sem);
+               rcu_read_unlock();
                return -EINVAL;
        }
 
Index: linux/security/keys/request_key.c
===================================================================
--- linux.orig/security/keys/request_key.c      2005-09-27 16:01:17.563957095 
-0500
+++ linux/security/keys/request_key.c   2005-09-27 16:18:37.840196240 -0500
@@ -14,6 +14,7 @@
 #include <linux/kmod.h>
 #include <linux/err.h>
 #include <linux/keyctl.h>
+#include <linux/pnotify.h>
 #include "internal.h"
 
 struct key_construction {
@@ -39,6 +40,19 @@
        char *argv[10], *envp[3], uid_str[12], gid_str[12];
        char key_str[12], keyring_str[3][12];
        int ret, i;
+       struct pnotify_subscriber *sub;
+       struct key_task *kt;
+
+       rcu_read_lock();
+       down_write(&tsk->pnotify_subscriber_list_sem);
+       sub = pnotify_get_subscriber(current, key_events.name);
+       if (sub == NULL || sub->data == NULL) { /* shouldn't happen */
+               printk(KERN_ERR "call_request_key pnotify subscriber or data 
ptr null, task: %d\n", tsk->pid);
+               up_write(&tsk->pnotify_subscriber_list_sem);
+               rcu_read_unlock();
+               return -EFAULT;
+       }
+       kt = (struct key_task *)sub->data;
 
        kenter("{%d},%s,%s", key->serial, op, callout_info);
 
@@ -58,7 +72,7 @@
 
        /* we specify the process's default keyrings */
        sprintf(keyring_str[0], "%d",
-               tsk->thread_keyring ? tsk->thread_keyring->serial : 0);
+               kt->thread_keyring ? kt->thread_keyring->serial : 0);
 
        prkey = 0;
        if (tsk->signal->process_keyring)
@@ -105,6 +119,8 @@
        key_put(session_keyring);
 
  error:
+       up_write(&tsk->pnotify_subscriber_list_sem);
+       rcu_read_unlock();
        kleave(" = %d", ret);
        return ret;
 
@@ -300,15 +316,28 @@
 {
        struct task_struct *tsk = current;
        struct key *drop = NULL;
+       struct pnotify_subscriber *sub;
+       struct key_task *kt;
+
+       rcu_read_lock();
+       down_write(&tsk->pnotify_subscriber_list_sem);
+       sub = pnotify_get_subscriber(current, key_events.name);
+       if (sub == NULL || sub->data == NULL) { /* shouldn't happen */
+               printk(KERN_ERR "request_key_link pnotify subscriber or data 
ptr null, task: %d\n", tsk->pid);
+               up_write(&tsk->pnotify_subscriber_list_sem);
+               rcu_read_unlock();
+               return;
+       }
+       kt = (struct key_task *)sub->data;
 
        kenter("{%d},%p", key->serial, dest_keyring);
 
        /* find the appropriate keyring */
        if (!dest_keyring) {
-               switch (tsk->jit_keyring) {
+               switch (kt->jit_keyring) {
                case KEY_REQKEY_DEFL_DEFAULT:
                case KEY_REQKEY_DEFL_THREAD_KEYRING:
-                       dest_keyring = tsk->thread_keyring;
+                       dest_keyring = kt->thread_keyring;
                        if (dest_keyring)
                                break;
 
@@ -347,6 +376,8 @@
        key_put(drop);
 
        kleave("");
+       down_write(&tsk->pnotify_subscriber_list_sem);
+       rcu_read_unlock();
 
 } /* end request_key_link() */
 

<Prev in Thread] Current Thread [Next in Thread>
  • [PATCH] rcu-pnotify aware version of keyring support, Erik Jacobson <=