aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2017-03-01 10:11:23 -0500
committerJames Morris <james.l.morris@oracle.com>2017-03-01 18:09:00 -0500
commit0837e49ab3fa8d903a499984575d71efee8097ce (patch)
tree8a15e36e5ca94f2c7b920ae5f13452aca7ff88b2
parent6053dc981449718d90a429933e99b441e1adaea6 (diff)
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>
-rw-r--r--Documentation/security/keys.txt17
-rw-r--r--drivers/md/dm-crypt.c2
-rw-r--r--fs/cifs/connect.c2
-rw-r--r--fs/crypto/keyinfo.c2
-rw-r--r--fs/ecryptfs/ecryptfs_kernel.h2
-rw-r--r--fs/fscache/object-list.c2
-rw-r--r--fs/nfs/nfs4idmap.c2
-rw-r--r--include/keys/user-type.h9
-rw-r--r--include/linux/key.h5
-rw-r--r--lib/digsig.c2
-rw-r--r--net/dns_resolver/dns_query.c4
-rw-r--r--security/keys/dh.c2
-rw-r--r--security/keys/encrypted-keys/encrypted.c4
-rw-r--r--security/keys/trusted.c4
-rw-r--r--security/keys/user_defined.c6
15 files changed, 43 insertions, 22 deletions
diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt
index 3849814bfe6d..0e03baf271bd 100644
--- a/Documentation/security/keys.txt
+++ b/Documentation/security/keys.txt
@@ -1151,8 +1151,21 @@ access the data:
1151 usage. This is called key->payload.rcu_data0. The following accessors 1151 usage. This is called key->payload.rcu_data0. The following accessors
1152 wrap the RCU calls to this element: 1152 wrap the RCU calls to this element:
1153 1153
1154 rcu_assign_keypointer(struct key *key, void *data); 1154 (a) Set or change the first payload pointer:
1155 void *rcu_dereference_key(struct key *key); 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===================
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 1cb2ca9dfae3..389a3637ffcc 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1536,7 +1536,7 @@ static int crypt_set_keyring_key(struct crypt_config *cc, const char *key_string
1536 1536
1537 down_read(&key->sem); 1537 down_read(&key->sem);
1538 1538
1539 ukp = user_key_payload(key); 1539 ukp = user_key_payload_locked(key);
1540 if (!ukp) { 1540 if (!ukp) {
1541 up_read(&key->sem); 1541 up_read(&key->sem);
1542 key_put(key); 1542 key_put(key);
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 777ad9f4fc3c..8a3ecef30d3c 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -2455,7 +2455,7 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses)
2455 } 2455 }
2456 2456
2457 down_read(&key->sem); 2457 down_read(&key->sem);
2458 upayload = user_key_payload(key); 2458 upayload = user_key_payload_locked(key);
2459 if (IS_ERR_OR_NULL(upayload)) { 2459 if (IS_ERR_OR_NULL(upayload)) {
2460 rc = upayload ? PTR_ERR(upayload) : -EINVAL; 2460 rc = upayload ? PTR_ERR(upayload) : -EINVAL;
2461 goto out_key_put; 2461 goto out_key_put;
diff --git a/fs/crypto/keyinfo.c b/fs/crypto/keyinfo.c
index 02eb6b9e4438..d5d896fa5a71 100644
--- a/fs/crypto/keyinfo.c
+++ b/fs/crypto/keyinfo.c
@@ -103,7 +103,7 @@ static int validate_user_key(struct fscrypt_info *crypt_info,
103 goto out; 103 goto out;
104 } 104 }
105 down_read(&keyring_key->sem); 105 down_read(&keyring_key->sem);
106 ukp = user_key_payload(keyring_key); 106 ukp = user_key_payload_locked(keyring_key);
107 if (ukp->datalen != sizeof(struct fscrypt_key)) { 107 if (ukp->datalen != sizeof(struct fscrypt_key)) {
108 res = -EINVAL; 108 res = -EINVAL;
109 up_read(&keyring_key->sem); 109 up_read(&keyring_key->sem);
diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h
index 599a29237cfe..95c1c8d34539 100644
--- a/fs/ecryptfs/ecryptfs_kernel.h
+++ b/fs/ecryptfs/ecryptfs_kernel.h
@@ -117,7 +117,7 @@ ecryptfs_get_key_payload_data(struct key *key)
117 117
118 auth_tok = ecryptfs_get_encrypted_key_payload_data(key); 118 auth_tok = ecryptfs_get_encrypted_key_payload_data(key);
119 if (!auth_tok) 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 else 121 else
122 return auth_tok; 122 return auth_tok;
123} 123}
diff --git a/fs/fscache/object-list.c b/fs/fscache/object-list.c
index 5d5ddaa84b21..67f940892ef8 100644
--- a/fs/fscache/object-list.c
+++ b/fs/fscache/object-list.c
@@ -329,7 +329,7 @@ static void fscache_objlist_config(struct fscache_objlist_data *data)
329 config = 0; 329 config = 0;
330 rcu_read_lock(); 330 rcu_read_lock();
331 331
332 confkey = user_key_payload(key); 332 confkey = user_key_payload_rcu(key);
333 buf = confkey->data; 333 buf = confkey->data;
334 334
335 for (len = confkey->datalen - 1; len >= 0; len--) { 335 for (len = confkey->datalen - 1; len >= 0; len--) {
diff --git a/fs/nfs/nfs4idmap.c b/fs/nfs/nfs4idmap.c
index c444285bb1b1..835c163f61af 100644
--- a/fs/nfs/nfs4idmap.c
+++ b/fs/nfs/nfs4idmap.c
@@ -316,7 +316,7 @@ static ssize_t nfs_idmap_get_key(const char *name, size_t namelen,
316 if (ret < 0) 316 if (ret < 0)
317 goto out_up; 317 goto out_up;
318 318
319 payload = user_key_payload(rkey); 319 payload = user_key_payload_rcu(rkey);
320 if (IS_ERR_OR_NULL(payload)) { 320 if (IS_ERR_OR_NULL(payload)) {
321 ret = PTR_ERR(payload); 321 ret = PTR_ERR(payload);
322 goto out_up; 322 goto out_up;
diff --git a/include/keys/user-type.h b/include/keys/user-type.h
index c56fef40f53e..e098cbe27db5 100644
--- a/include/keys/user-type.h
+++ b/include/keys/user-type.h
@@ -48,9 +48,14 @@ extern void user_describe(const struct key *user, struct seq_file *m);
48extern long user_read(const struct key *key, 48extern long user_read(const struct key *key,
49 char __user *buffer, size_t buflen); 49 char __user *buffer, size_t buflen);
50 50
51static inline const struct user_key_payload *user_key_payload(const struct key *key) 51static 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
56static 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#endif /* CONFIG_KEYS */ 61#endif /* CONFIG_KEYS */
diff --git a/include/linux/key.h b/include/linux/key.h
index 722914798f37..e45212f2777e 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -354,7 +354,10 @@ static inline bool key_is_instantiated(const struct key *key)
354 !test_bit(KEY_FLAG_NEGATIVE, &key->flags); 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 (rcu_dereference_protected((KEY)->payload.rcu_data0, \ 361 (rcu_dereference_protected((KEY)->payload.rcu_data0, \
359 rwsem_is_locked(&((struct key *)(KEY))->sem))) 362 rwsem_is_locked(&((struct key *)(KEY))->sem)))
360 363
diff --git a/lib/digsig.c b/lib/digsig.c
index 55b8b2f41a9e..03d7c63837ae 100644
--- a/lib/digsig.c
+++ b/lib/digsig.c
@@ -85,7 +85,7 @@ static int digsig_verify_rsa(struct key *key,
85 struct pubkey_hdr *pkh; 85 struct pubkey_hdr *pkh;
86 86
87 down_read(&key->sem); 87 down_read(&key->sem);
88 ukp = user_key_payload(key); 88 ukp = user_key_payload_locked(key);
89 89
90 if (ukp->datalen < sizeof(*pkh)) 90 if (ukp->datalen < sizeof(*pkh))
91 goto err1; 91 goto err1;
diff --git a/net/dns_resolver/dns_query.c b/net/dns_resolver/dns_query.c
index ecc28cff08ab..d502c94b1a82 100644
--- a/net/dns_resolver/dns_query.c
+++ b/net/dns_resolver/dns_query.c
@@ -70,7 +70,7 @@ int dns_query(const char *type, const char *name, size_t namelen,
70 const char *options, char **_result, time64_t *_expiry) 70 const char *options, char **_result, time64_t *_expiry)
71{ 71{
72 struct key *rkey; 72 struct key *rkey;
73 const struct user_key_payload *upayload; 73 struct user_key_payload *upayload;
74 const struct cred *saved_cred; 74 const struct cred *saved_cred;
75 size_t typelen, desclen; 75 size_t typelen, desclen;
76 char *desc, *cp; 76 char *desc, *cp;
@@ -141,7 +141,7 @@ int dns_query(const char *type, const char *name, size_t namelen,
141 if (ret) 141 if (ret)
142 goto put; 142 goto put;
143 143
144 upayload = user_key_payload(rkey); 144 upayload = user_key_payload_locked(rkey);
145 len = upayload->datalen; 145 len = upayload->datalen;
146 146
147 ret = -ENOMEM; 147 ret = -ENOMEM;
diff --git a/security/keys/dh.c b/security/keys/dh.c
index 531ed2ec132f..893af4c45038 100644
--- a/security/keys/dh.c
+++ b/security/keys/dh.c
@@ -55,7 +55,7 @@ static ssize_t mpi_from_key(key_serial_t keyid, size_t maxlen, MPI *mpi)
55 if (status == 0) { 55 if (status == 0) {
56 const struct user_key_payload *payload; 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 if (maxlen == 0) { 60 if (maxlen == 0) {
61 *mpi = NULL; 61 *mpi = NULL;
diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
index 4fb315cddf5b..0010955d7876 100644
--- a/security/keys/encrypted-keys/encrypted.c
+++ b/security/keys/encrypted-keys/encrypted.c
@@ -314,7 +314,7 @@ static struct key *request_user_key(const char *master_desc, const u8 **master_k
314 goto error; 314 goto error;
315 315
316 down_read(&ukey->sem); 316 down_read(&ukey->sem);
317 upayload = user_key_payload(ukey); 317 upayload = user_key_payload_locked(ukey);
318 *master_key = upayload->data; 318 *master_key = upayload->data;
319 *master_keylen = upayload->datalen; 319 *master_keylen = upayload->datalen;
320error: 320error:
@@ -926,7 +926,7 @@ static long encrypted_read(const struct key *key, char __user *buffer,
926 size_t asciiblob_len; 926 size_t asciiblob_len;
927 int ret; 927 int ret;
928 928
929 epayload = rcu_dereference_key(key); 929 epayload = dereference_key_locked(key);
930 930
931 /* returns the hex encoded iv, encrypted-data, and hmac as ascii */ 931 /* returns the hex encoded iv, encrypted-data, and hmac as ascii */
932 asciiblob_len = epayload->datablob_len + ivsize + 1 932 asciiblob_len = epayload->datablob_len + ivsize + 1
diff --git a/security/keys/trusted.c b/security/keys/trusted.c
index 90d61751ff12..2ae31c5a87de 100644
--- a/security/keys/trusted.c
+++ b/security/keys/trusted.c
@@ -1140,12 +1140,12 @@ out:
1140static long trusted_read(const struct key *key, char __user *buffer, 1140static long trusted_read(const struct key *key, char __user *buffer,
1141 size_t buflen) 1141 size_t buflen)
1142{ 1142{
1143 struct trusted_key_payload *p; 1143 const struct trusted_key_payload *p;
1144 char *ascii_buf; 1144 char *ascii_buf;
1145 char *bufp; 1145 char *bufp;
1146 int i; 1146 int i;
1147 1147
1148 p = rcu_dereference_key(key); 1148 p = dereference_key_locked(key);
1149 if (!p) 1149 if (!p)
1150 return -EINVAL; 1150 return -EINVAL;
1151 if (!buffer || buflen <= 0) 1151 if (!buffer || buflen <= 0)
diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c
index e187c8909d9d..26605134f17a 100644
--- a/security/keys/user_defined.c
+++ b/security/keys/user_defined.c
@@ -107,7 +107,7 @@ int user_update(struct key *key, struct key_preparsed_payload *prep)
107 /* attach the new data, displacing the old */ 107 /* attach the new data, displacing the old */
108 key->expiry = prep->expiry; 108 key->expiry = prep->expiry;
109 if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags)) 109 if (!test_bit(KEY_FLAG_NEGATIVE, &key->flags))
110 zap = rcu_dereference_key(key); 110 zap = dereference_key_locked(key);
111 rcu_assign_keypointer(key, prep->payload.data[0]); 111 rcu_assign_keypointer(key, prep->payload.data[0]);
112 prep->payload.data[0] = NULL; 112 prep->payload.data[0] = NULL;
113 113
@@ -123,7 +123,7 @@ EXPORT_SYMBOL_GPL(user_update);
123 */ 123 */
124void user_revoke(struct key *key) 124void 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 /* clear the quota */ 128 /* clear the quota */
129 key_payload_reserve(key, 0); 129 key_payload_reserve(key, 0);
@@ -169,7 +169,7 @@ long user_read(const struct key *key, char __user *buffer, size_t buflen)
169 const struct user_key_payload *upayload; 169 const struct user_key_payload *upayload;
170 long ret; 170 long ret;
171 171
172 upayload = user_key_payload(key); 172 upayload = user_key_payload_locked(key);
173 ret = upayload->datalen; 173 ret = upayload->datalen;
174 174
175 /* we can return the data as is */ 175 /* we can return the data as is */