security: don't use RCU accessors for cred->session_keyring
authorJann Horn <jannh@google.com>
Wed, 27 Mar 2019 15:39:38 +0000 (16:39 +0100)
committerJames Morris <james.morris@microsoft.com>
Wed, 10 Apr 2019 17:28:21 +0000 (10:28 -0700)
sparse complains that a bunch of places in kernel/cred.c access
cred->session_keyring without the RCU helpers required by the __rcu
annotation.

cred->session_keyring is written in the following places:

 - prepare_kernel_cred() [in a new cred struct]
 - keyctl_session_to_parent() [in a new cred struct]
 - prepare_creds [in a new cred struct, via memcpy]
 - install_session_keyring_to_cred()
  - from install_session_keyring() on new creds
  - from join_session_keyring() on new creds [twice]
  - from umh_keys_init()
   - from call_usermodehelper_exec_async() on new creds

All of these writes are before the creds are committed; therefore,
cred->session_keyring doesn't need RCU protection.

Remove the __rcu annotation and fix up all existing users that use __rcu.

Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: James Morris <james.morris@microsoft.com>
include/linux/cred.h
security/keys/process_keys.c
security/keys/request_key.c

index ddd45bb..efb6edf 100644 (file)
@@ -138,7 +138,7 @@ struct cred {
 #ifdef CONFIG_KEYS
        unsigned char   jit_keyring;    /* default keyring to attach requested
                                         * keys to */
-       struct key __rcu *session_keyring; /* keyring inherited over fork */
+       struct key      *session_keyring; /* keyring inherited over fork */
        struct key      *process_keyring; /* keyring private to this process */
        struct key      *thread_keyring; /* keyring private to this thread */
        struct key      *request_key_auth; /* assumed request_key authority */
index 9320424..bd7243c 100644 (file)
@@ -227,6 +227,7 @@ static int install_process_keyring(void)
  * Install the given keyring as the session keyring of the given credentials
  * struct, replacing the existing one if any.  If the given keyring is NULL,
  * then install a new anonymous session keyring.
+ * @cred can not be in use by any task yet.
  *
  * Return: 0 on success; -errno on failure.
  */
@@ -254,7 +255,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
 
        /* install the keyring */
        old = cred->session_keyring;
-       rcu_assign_pointer(cred->session_keyring, keyring);
+       cred->session_keyring = keyring;
 
        if (old)
                key_put(old);
@@ -392,11 +393,8 @@ key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)
 
        /* search the session keyring */
        if (ctx->cred->session_keyring) {
-               rcu_read_lock();
                key_ref = keyring_search_aux(
-                       make_key_ref(rcu_dereference(ctx->cred->session_keyring), 1),
-                       ctx);
-               rcu_read_unlock();
+                       make_key_ref(ctx->cred->session_keyring, 1), ctx);
 
                if (!IS_ERR(key_ref))
                        goto found;
@@ -612,10 +610,8 @@ try_again:
                        goto reget_creds;
                }
 
-               rcu_read_lock();
-               key = rcu_dereference(ctx.cred->session_keyring);
+               key = ctx.cred->session_keyring;
                __key_get(key);
-               rcu_read_unlock();
                key_ref = make_key_ref(key, 1);
                break;
 
index 2f17d84..db72dc4 100644 (file)
@@ -142,12 +142,10 @@ static int call_sbin_request_key(struct key *authkey, void *aux)
                prkey = cred->process_keyring->serial;
        sprintf(keyring_str[1], "%d", prkey);
 
-       rcu_read_lock();
-       session = rcu_dereference(cred->session_keyring);
+       session = cred->session_keyring;
        if (!session)
                session = cred->user->session_keyring;
        sskey = session->serial;
-       rcu_read_unlock();
 
        sprintf(keyring_str[2], "%d", sskey);
 
@@ -287,10 +285,7 @@ static int construct_get_dest_keyring(struct key **_dest_keyring)
 
                        /* fall through */
                case KEY_REQKEY_DEFL_SESSION_KEYRING:
-                       rcu_read_lock();
-                       dest_keyring = key_get(
-                               rcu_dereference(cred->session_keyring));
-                       rcu_read_unlock();
+                       dest_keyring = key_get(cred->session_keyring);
 
                        if (dest_keyring)
                                break;