Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KEYS: Differentiate uses of rcu_dereference_key() and user_key_payload()

rcu_dereference_key() and user_key_payload() are currently being used in
two different, incompatible ways:

(1) As a wrapper to rcu_dereference() - when only the RCU read lock used
to protect the key.

(2) As a wrapper to rcu_dereference_protected() - when the key semaphor is
used to protect the key and the may be being modified.

Fix this by splitting both of the key wrappers to produce:

(1) RCU accessors for keys when caller has the key semaphore locked:

dereference_key_locked()
user_key_payload_locked()

(2) RCU accessors for keys when caller holds the RCU read lock:

dereference_key_rcu()
user_key_payload_rcu()

This should fix following warning in the NFS idmapper

===============================
[ INFO: suspicious RCU usage. ]
4.10.0 #1 Tainted: G W
-------------------------------
./include/keys/user-type.h:53 suspicious rcu_dereference_protected() usage!
other info that might help us debug this:
rcu_scheduler_active = 2, debug_locks = 0
1 lock held by mount.nfs/5987:
#0: (rcu_read_lock){......}, at: [<d000000002527abc>] nfs_idmap_get_key+0x15c/0x420 [nfsv4]
stack backtrace:
CPU: 1 PID: 5987 Comm: mount.nfs Tainted: G W 4.10.0 #1
Call Trace:
dump_stack+0xe8/0x154 (unreliable)
lockdep_rcu_suspicious+0x140/0x190
nfs_idmap_get_key+0x380/0x420 [nfsv4]
nfs_map_name_to_uid+0x2a0/0x3b0 [nfsv4]
decode_getfattr_attrs+0xfac/0x16b0 [nfsv4]
decode_getfattr_generic.constprop.106+0xbc/0x150 [nfsv4]
nfs4_xdr_dec_lookup_root+0xac/0xb0 [nfsv4]
rpcauth_unwrap_resp+0xe8/0x140 [sunrpc]
call_decode+0x29c/0x910 [sunrpc]
__rpc_execute+0x140/0x8f0 [sunrpc]
rpc_run_task+0x170/0x200 [sunrpc]
nfs4_call_sync_sequence+0x68/0xa0 [nfsv4]
_nfs4_lookup_root.isra.44+0xd0/0xf0 [nfsv4]
nfs4_lookup_root+0xe0/0x350 [nfsv4]
nfs4_lookup_root_sec+0x70/0xa0 [nfsv4]
nfs4_find_root_sec+0xc4/0x100 [nfsv4]
nfs4_proc_get_rootfh+0x5c/0xf0 [nfsv4]
nfs4_get_rootfh+0x6c/0x190 [nfsv4]
nfs4_server_common_setup+0xc4/0x260 [nfsv4]
nfs4_create_server+0x278/0x3c0 [nfsv4]
nfs4_remote_mount+0x50/0xb0 [nfsv4]
mount_fs+0x74/0x210
vfs_kern_mount+0x78/0x220
nfs_do_root_mount+0xb0/0x140 [nfsv4]
nfs4_try_mount+0x60/0x100 [nfsv4]
nfs_fs_mount+0x5ec/0xda0 [nfs]
mount_fs+0x74/0x210
vfs_kern_mount+0x78/0x220
do_mount+0x254/0xf70
SyS_mount+0x94/0x100
system_call+0x38/0xe0

Reported-by: Jan Stancek <jstancek@redhat.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Tested-by: Jan Stancek <jstancek@redhat.com>
Signed-off-by: James Morris <james.l.morris@oracle.com>

authored by

David Howells and committed by
James Morris
0837e49a 6053dc98

+43 -22
+15 -2
Documentation/security/keys.txt
··· 1151 1151 usage. This is called key->payload.rcu_data0. The following accessors 1152 1152 wrap the RCU calls to this element: 1153 1153 1154 - rcu_assign_keypointer(struct key *key, void *data); 1155 - void *rcu_dereference_key(struct key *key); 1154 + (a) Set or change the first payload pointer: 1155 + 1156 + rcu_assign_keypointer(struct key *key, void *data); 1157 + 1158 + (b) Read the first payload pointer with the key semaphore held: 1159 + 1160 + [const] void *dereference_key_locked([const] struct key *key); 1161 + 1162 + Note that the return value will inherit its constness from the key 1163 + parameter. Static analysis will give an error if it things the lock 1164 + isn't held. 1165 + 1166 + (c) Read the first payload pointer with the RCU read lock held: 1167 + 1168 + const void *dereference_key_rcu(const struct key *key); 1156 1169 1157 1170 1158 1171 ===================
+1 -1
drivers/md/dm-crypt.c
··· 1536 1536 1537 1537 down_read(&key->sem); 1538 1538 1539 - ukp = user_key_payload(key); 1539 + ukp = user_key_payload_locked(key); 1540 1540 if (!ukp) { 1541 1541 up_read(&key->sem); 1542 1542 key_put(key);
+1 -1
fs/cifs/connect.c
··· 2455 2455 } 2456 2456 2457 2457 down_read(&key->sem); 2458 - upayload = user_key_payload(key); 2458 + upayload = user_key_payload_locked(key); 2459 2459 if (IS_ERR_OR_NULL(upayload)) { 2460 2460 rc = upayload ? PTR_ERR(upayload) : -EINVAL; 2461 2461 goto out_key_put;
+1 -1
fs/crypto/keyinfo.c
··· 103 103 goto out; 104 104 } 105 105 down_read(&keyring_key->sem); 106 - ukp = user_key_payload(keyring_key); 106 + ukp = user_key_payload_locked(keyring_key); 107 107 if (ukp->datalen != sizeof(struct fscrypt_key)) { 108 108 res = -EINVAL; 109 109 up_read(&keyring_key->sem);
+1 -1
fs/ecryptfs/ecryptfs_kernel.h
··· 117 117 118 118 auth_tok = ecryptfs_get_encrypted_key_payload_data(key); 119 119 if (!auth_tok) 120 - return (struct ecryptfs_auth_tok *)user_key_payload(key)->data; 120 + return (struct ecryptfs_auth_tok *)user_key_payload_locked(key)->data; 121 121 else 122 122 return auth_tok; 123 123 }
+1 -1
fs/fscache/object-list.c
··· 329 329 config = 0; 330 330 rcu_read_lock(); 331 331 332 - confkey = user_key_payload(key); 332 + confkey = user_key_payload_rcu(key); 333 333 buf = confkey->data; 334 334 335 335 for (len = confkey->datalen - 1; len >= 0; len--) {
+1 -1
fs/nfs/nfs4idmap.c
··· 316 316 if (ret < 0) 317 317 goto out_up; 318 318 319 - payload = user_key_payload(rkey); 319 + payload = user_key_payload_rcu(rkey); 320 320 if (IS_ERR_OR_NULL(payload)) { 321 321 ret = PTR_ERR(payload); 322 322 goto out_up;
+7 -2
include/keys/user-type.h
··· 48 48 extern long user_read(const struct key *key, 49 49 char __user *buffer, size_t buflen); 50 50 51 - static inline const struct user_key_payload *user_key_payload(const struct key *key) 51 + static inline const struct user_key_payload *user_key_payload_rcu(const struct key *key) 52 52 { 53 - return (struct user_key_payload *)rcu_dereference_key(key); 53 + return (struct user_key_payload *)dereference_key_rcu(key); 54 + } 55 + 56 + static inline struct user_key_payload *user_key_payload_locked(const struct key *key) 57 + { 58 + return (struct user_key_payload *)dereference_key_locked((struct key *)key); 54 59 } 55 60 56 61 #endif /* CONFIG_KEYS */
+4 -1
include/linux/key.h
··· 354 354 !test_bit(KEY_FLAG_NEGATIVE, &key->flags); 355 355 } 356 356 357 - #define rcu_dereference_key(KEY) \ 357 + #define dereference_key_rcu(KEY) \ 358 + (rcu_dereference((KEY)->payload.rcu_data0)) 359 + 360 + #define dereference_key_locked(KEY) \ 358 361 (rcu_dereference_protected((KEY)->payload.rcu_data0, \ 359 362 rwsem_is_locked(&((struct key *)(KEY))->sem))) 360 363
+1 -1
lib/digsig.c
··· 85 85 struct pubkey_hdr *pkh; 86 86 87 87 down_read(&key->sem); 88 - ukp = user_key_payload(key); 88 + ukp = user_key_payload_locked(key); 89 89 90 90 if (ukp->datalen < sizeof(*pkh)) 91 91 goto err1;
+2 -2
net/dns_resolver/dns_query.c
··· 70 70 const char *options, char **_result, time64_t *_expiry) 71 71 { 72 72 struct key *rkey; 73 - const struct user_key_payload *upayload; 73 + struct user_key_payload *upayload; 74 74 const struct cred *saved_cred; 75 75 size_t typelen, desclen; 76 76 char *desc, *cp; ··· 141 141 if (ret) 142 142 goto put; 143 143 144 - upayload = user_key_payload(rkey); 144 + upayload = user_key_payload_locked(rkey); 145 145 len = upayload->datalen; 146 146 147 147 ret = -ENOMEM;
+1 -1
security/keys/dh.c
··· 55 55 if (status == 0) { 56 56 const struct user_key_payload *payload; 57 57 58 - payload = user_key_payload(key); 58 + payload = user_key_payload_locked(key); 59 59 60 60 if (maxlen == 0) { 61 61 *mpi = NULL;
+2 -2
security/keys/encrypted-keys/encrypted.c
··· 314 314 goto error; 315 315 316 316 down_read(&ukey->sem); 317 - upayload = user_key_payload(ukey); 317 + upayload = user_key_payload_locked(ukey); 318 318 *master_key = upayload->data; 319 319 *master_keylen = upayload->datalen; 320 320 error: ··· 926 926 size_t asciiblob_len; 927 927 int ret; 928 928 929 - epayload = rcu_dereference_key(key); 929 + epayload = dereference_key_locked(key); 930 930 931 931 /* returns the hex encoded iv, encrypted-data, and hmac as ascii */ 932 932 asciiblob_len = epayload->datablob_len + ivsize + 1
+2 -2
security/keys/trusted.c
··· 1140 1140 static long trusted_read(const struct key *key, char __user *buffer, 1141 1141 size_t buflen) 1142 1142 { 1143 - struct trusted_key_payload *p; 1143 + const struct trusted_key_payload *p; 1144 1144 char *ascii_buf; 1145 1145 char *bufp; 1146 1146 int i; 1147 1147 1148 - p = rcu_dereference_key(key); 1148 + p = dereference_key_locked(key); 1149 1149 if (!p) 1150 1150 return -EINVAL; 1151 1151 if (!buffer || buflen <= 0)
+3 -3
security/keys/user_defined.c
··· 107 107 /* attach the new data, displacing the old */ 108 108 key->expiry = prep->expiry; 109 109 if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags)) 110 - zap = rcu_dereference_key(key); 110 + zap = dereference_key_locked(key); 111 111 rcu_assign_keypointer(key, prep->payload.data[0]); 112 112 prep->payload.data[0] = NULL; 113 113 ··· 123 123 */ 124 124 void user_revoke(struct key *key) 125 125 { 126 - struct user_key_payload *upayload = key->payload.data[0]; 126 + struct user_key_payload *upayload = user_key_payload_locked(key); 127 127 128 128 /* clear the quota */ 129 129 key_payload_reserve(key, 0); ··· 169 169 const struct user_key_payload *upayload; 170 170 long ret; 171 171 172 - upayload = user_key_payload(key); 172 + upayload = user_key_payload_locked(key); 173 173 ret = upayload->datalen; 174 174 175 175 /* we can return the data as is */