Merge branch 'master' into next
James Morris [Thu, 6 May 2010 12:21:04 +0000 (22:21 +1000)]
Conflicts:
security/keys/keyring.c

Resolved conflict with whitespace fix in find_keyring_by_name()

Signed-off-by: James Morris <jmorris@namei.org>

1  2 
security/keys/keyring.c
security/keys/request_key.c

diff --combined security/keys/keyring.c
  #include <linux/seq_file.h>
  #include <linux/err.h>
  #include <keys/keyring-type.h>
 -#include <asm/uaccess.h>
 +#include <linux/uaccess.h>
  #include "internal.h"
  
+ #define rcu_dereference_locked_keyring(keyring)                               \
+       (rcu_dereference_protected(                                     \
+               (keyring)->payload.subscriptions,                       \
+               rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem)))
  /*
   * when plumbing the depths of the key tree, this sets a hard limit set on how
   * deep we're willing to go
@@@ -39,7 -44,7 +44,7 @@@ static inline unsigned keyring_hash(con
        unsigned bucket = 0;
  
        for (; *desc; desc++)
 -              bucket += (unsigned char) *desc;
 +              bucket += (unsigned char)*desc;
  
        return bucket & (KEYRING_NAME_HASH_SIZE - 1);
  }
@@@ -170,10 -175,12 +175,10 @@@ static void keyring_describe(const stru
  {
        struct keyring_list *klist;
  
 -      if (keyring->description) {
 +      if (keyring->description)
                seq_puts(m, keyring->description);
 -      }
 -      else {
 +      else
                seq_puts(m, "[anon]");
 -      }
  
        rcu_read_lock();
        klist = rcu_dereference(keyring->payload.subscriptions);
@@@ -199,8 -206,7 +204,7 @@@ static long keyring_read(const struct k
        int loop, ret;
  
        ret = 0;
-       klist = keyring->payload.subscriptions;
+       klist = rcu_dereference_locked_keyring(keyring);
        if (klist) {
                /* calculate how much data we could return */
                qty = klist->nkeys * sizeof(key_serial_t);
                ret = qty;
        }
  
 - error:
 +error:
        return ret;
  
  } /* end keyring_read() */
@@@ -304,7 -310,7 +308,7 @@@ key_ref_t keyring_search_aux(key_ref_t 
        key_check(keyring);
  
        /* top keyring must have search permission to begin the search */
 -        err = key_task_permission(keyring_ref, cred, KEY_SEARCH);
 +      err = key_task_permission(keyring_ref, cred, KEY_SEARCH);
        if (err < 0) {
                key_ref = ERR_PTR(err);
                goto error;
@@@ -506,7 -512,7 +510,7 @@@ key_ref_t __keyring_search_one(key_ref_
        rcu_read_unlock();
        return ERR_PTR(-ENOKEY);
  
 - found:
 +found:
        atomic_inc(&key->usage);
        rcu_read_unlock();
        return make_key_ref(key, possessed);
@@@ -524,9 -530,8 +528,8 @@@ struct key *find_keyring_by_name(const 
        struct key *keyring;
        int bucket;
  
-       keyring = ERR_PTR(-EINVAL);
        if (!name)
-               goto error;
+               return ERR_PTR(-EINVAL);
  
        bucket = keyring_hash(name);
  
                                           KEY_SEARCH) < 0)
                                continue;
  
-                       /* we've got a match */
-                       atomic_inc(&keyring->usage);
-                       read_unlock(&keyring_name_lock);
-                       goto error;
+                       /* we've got a match but we might end up racing with
+                        * key_cleanup() if the keyring is currently 'dead'
+                        * (ie. it has a zero usage count) */
+                       if (!atomic_inc_not_zero(&keyring->usage))
+                               continue;
+                       goto out;
                }
        }
  
-       read_unlock(&keyring_name_lock);
        keyring = ERR_PTR(-ENOKEY);
- error:
+ out:
+       read_unlock(&keyring_name_lock);
        return keyring;
  
  } /* end find_keyring_by_name() */
@@@ -596,7 -602,7 +600,7 @@@ static int keyring_detect_cycle(struct 
        sp = 0;
  
        /* start processing a new keyring */
 - descend:
 +descend:
        if (test_bit(KEY_FLAG_REVOKED, &subtree->flags))
                goto not_this_keyring;
  
                goto not_this_keyring;
        kix = 0;
  
 - ascend:
 +ascend:
        /* iterate through the remaining keys in this keyring */
        for (; kix < keylist->nkeys; kix++) {
                key = keylist->keys[kix];
  
        /* the keyring we're looking at was disqualified or didn't contain a
         * matching key */
 - not_this_keyring:
 +not_this_keyring:
        if (sp > 0) {
                /* resume the checking of a keyring higher up in the tree */
                sp--;
  
        ret = 0; /* no cycles detected */
  
 - error:
 +error:
        rcu_read_unlock();
        return ret;
  
 - too_deep:
 +too_deep:
        ret = -ELOOP;
        goto error;
  
 - cycle_detected:
 +cycle_detected:
        ret = -EDEADLK;
        goto error;
  
@@@ -705,22 -711,20 +709,21 @@@ int __key_link(struct key *keyring, str
        if (keyring->type != &key_type_keyring)
                goto error;
  
 -      /* serialise link/link calls to prevent parallel calls causing a
 -       * cycle when applied to two keyring in opposite orders */
 -      down_write(&keyring_serialise_link_sem);
 -
 -      /* check that we aren't going to create a cycle adding one keyring to
 -       * another */
 +      /* do some special keyring->keyring link checks */
        if (key->type == &key_type_keyring) {
 +              /* serialise link/link calls to prevent parallel calls causing
 +               * a cycle when applied to two keyring in opposite orders */
 +              down_write(&keyring_serialise_link_sem);
 +
 +              /* check that we aren't going to create a cycle adding one
 +               * keyring to another */
                ret = keyring_detect_cycle(keyring, key);
                if (ret < 0)
                        goto error2;
        }
  
        /* see if there's a matching key we can displace */
-       klist = keyring->payload.subscriptions;
+       klist = rcu_dereference_locked_keyring(keyring);
        if (klist && klist->nkeys > 0) {
                struct key_type *type = key->type;
  
        if (ret < 0)
                goto error2;
  
-       klist = keyring->payload.subscriptions;
        if (klist && klist->nkeys < klist->maxkeys) {
                /* there's sufficient slack space to add directly */
                atomic_inc(&key->usage);
                smp_wmb();
                klist->nkeys++;
                smp_wmb();
 -      }
 -      else {
 +      } else {
                /* grow the key list */
                max = 4;
                if (klist)
  done:
        ret = 0;
  error2:
 -      up_write(&keyring_serialise_link_sem);
 +      if (key->type == &key_type_keyring)
 +              up_write(&keyring_serialise_link_sem);
  error:
        return ret;
  
@@@ -867,7 -869,7 +868,7 @@@ int key_unlink(struct key *keyring, str
  
        down_write(&keyring->sem);
  
-       klist = keyring->payload.subscriptions;
+       klist = rcu_dereference_locked_keyring(keyring);
        if (klist) {
                /* search the keyring for the key */
                for (loop = 0; loop < klist->nkeys; loop++)
@@@ -958,7 -960,7 +959,7 @@@ int keyring_clear(struct key *keyring
                /* detach the pointer block with the locks held */
                down_write(&keyring->sem);
  
-               klist = keyring->payload.subscriptions;
+               klist = rcu_dereference_locked_keyring(keyring);
                if (klist) {
                        /* adjust the quota */
                        key_payload_reserve(keyring,
@@@ -990,7 -992,9 +991,9 @@@ EXPORT_SYMBOL(keyring_clear)
   */
  static void keyring_revoke(struct key *keyring)
  {
-       struct keyring_list *klist = keyring->payload.subscriptions;
+       struct keyring_list *klist;
+       klist = rcu_dereference_locked_keyring(keyring);
  
        /* adjust the quota */
        key_payload_reserve(keyring, 0);
@@@ -1024,7 -1028,7 +1027,7 @@@ void keyring_gc(struct key *keyring, ti
  
        down_write(&keyring->sem);
  
-       klist = keyring->payload.subscriptions;
+       klist = rcu_dereference_locked_keyring(keyring);
        if (!klist)
                goto no_klist;
  
@@@ -94,7 -94,7 +94,7 @@@ static int call_sbin_request_key(struc
        }
  
        /* attach the auth key to the session keyring */
-       ret = __key_link(keyring, authkey);
+       ret = key_link(keyring, authkey);
        if (ret < 0)
                goto error_link;
  
@@@ -302,7 -302,6 +302,7 @@@ static int construct_alloc_key(struct k
        const struct cred *cred = current_cred();
        struct key *key;
        key_ref_t key_ref;
 +      int ret;
  
        kenter("%s,%s,,,", type->name, description);
  
        kleave(" = 0 [%d]", key_serial(key));
        return 0;
  
 +      /* the key is now present - we tell the caller that we found it by
 +       * returning -EINPROGRESS  */
  key_already_present:
        mutex_unlock(&key_construction_mutex);
 +      ret = 0;
        if (dest_keyring) {
 -              __key_link(dest_keyring, key_ref_to_ptr(key_ref));
 +              ret = __key_link(dest_keyring, key_ref_to_ptr(key_ref));
                up_write(&dest_keyring->sem);
        }
        mutex_unlock(&user->cons_lock);
        key_put(key);
 +      if (ret < 0) {
 +              key_ref_put(key_ref);
 +              *_key = NULL;
 +              kleave(" = %d [link]", ret);
 +              return ret;
 +      }
        *_key = key = key_ref_to_ptr(key_ref);
        kleave(" = -EINPROGRESS [%d]", key_serial(key));
        return -EINPROGRESS;
@@@ -400,10 -390,6 +400,10 @@@ static struct key *construct_key_and_li
                        kdebug("cons failed");
                        goto construction_failed;
                }
 +      } else if (ret == -EINPROGRESS) {
 +              ret = 0;
 +      } else {
 +              key = ERR_PTR(ret);
        }
  
        key_put(dest_keyring);
@@@ -436,7 -422,6 +436,7 @@@ struct key *request_key_and_link(struc
        const struct cred *cred = current_cred();
        struct key *key;
        key_ref_t key_ref;
 +      int ret;
  
        kenter("%s,%s,%p,%zu,%p,%p,%lx",
               type->name, description, callout_info, callout_len, aux,
                key = key_ref_to_ptr(key_ref);
                if (dest_keyring) {
                        construct_get_dest_keyring(&dest_keyring);
 -                      key_link(dest_keyring, key);
 +                      ret = key_link(dest_keyring, key);
                        key_put(dest_keyring);
 +                      if (ret < 0) {
 +                              key_put(key);
 +                              key = ERR_PTR(ret);
 +                              goto error;
 +                      }
                }
        } else if (PTR_ERR(key_ref) != -EAGAIN) {
                key = ERR_CAST(key_ref);