Skip to content

Commit 8a004ca

Browse files
dhowellsgregkh
authored andcommitted
KEYS: Fix race between updating and finding a negative key
commit 363b02dab09b3226f3bd1420dad9c72b79a42a76 upstream. Consolidate KEY_FLAG_INSTANTIATED, KEY_FLAG_NEGATIVE and the rejection error into one field such that: (1) The instantiation state can be modified/read atomically. (2) The error can be accessed atomically with the state. (3) The error isn't stored unioned with the payload pointers. This deals with the problem that the state is spread over three different objects (two bits and a separate variable) and reading or updating them atomically isn't practical, given that not only can uninstantiated keys change into instantiated or rejected keys, but rejected keys can also turn into instantiated keys - and someone accessing the key might not be using any locking. The main side effect of this problem is that what was held in the payload may change, depending on the state. For instance, you might observe the key to be in the rejected state. You then read the cached error, but if the key semaphore wasn't locked, the key might've become instantiated between the two reads - and you might now have something in hand that isn't actually an error code. The state is now KEY_IS_UNINSTANTIATED, KEY_IS_POSITIVE or a negative error code if the key is negatively instantiated. The key_is_instantiated() function is replaced with key_is_positive() to avoid confusion as negative keys are also 'instantiated'. Additionally, barriering is included: (1) Order payload-set before state-set during instantiation. (2) Order state-read before payload-read when using the key. Further separate barriering is necessary if RCU is being used to access the payload content after reading the payload pointers. Fixes: 146aa8b ("KEYS: Merge the type-specific data with the payload data") Reported-by: Eric Biggers <ebiggers@google.com> Signed-off-by: David Howells <dhowells@redhat.com> Reviewed-by: Eric Biggers <ebiggers@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 1dda04c commit 8a004ca

14 files changed

Lines changed: 81 additions & 58 deletions

File tree

include/linux/key.h

Lines changed: 31 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@ static inline bool is_key_possessed(const key_ref_t key_ref)
126126
return (unsigned long) key_ref & 1UL;
127127
}
128128

129+
enum key_state {
130+
KEY_IS_UNINSTANTIATED,
131+
KEY_IS_POSITIVE, /* Positively instantiated */
132+
};
133+
129134
/*****************************************************************************/
130135
/*
131136
* authentication token / access credential / keyring
@@ -157,6 +162,7 @@ struct key {
157162
* - may not match RCU dereferenced payload
158163
* - payload should contain own length
159164
*/
165+
short state; /* Key state (+) or rejection error (-) */
160166

161167
#ifdef KEY_DEBUGGING
162168
unsigned magic;
@@ -165,19 +171,17 @@ struct key {
165171
#endif
166172

167173
unsigned long flags; /* status flags (change with bitops) */
168-
#define KEY_FLAG_INSTANTIATED 0 /* set if key has been instantiated */
169-
#define KEY_FLAG_DEAD 1 /* set if key type has been deleted */
170-
#define KEY_FLAG_REVOKED 2 /* set if key had been revoked */
171-
#define KEY_FLAG_IN_QUOTA 3 /* set if key consumes quota */
172-
#define KEY_FLAG_USER_CONSTRUCT 4 /* set if key is being constructed in userspace */
173-
#define KEY_FLAG_NEGATIVE 5 /* set if key is negative */
174-
#define KEY_FLAG_ROOT_CAN_CLEAR 6 /* set if key can be cleared by root without permission */
175-
#define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */
176-
#define KEY_FLAG_TRUSTED 8 /* set if key is trusted */
177-
#define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */
178-
#define KEY_FLAG_BUILTIN 10 /* set if key is builtin */
179-
#define KEY_FLAG_ROOT_CAN_INVAL 11 /* set if key can be invalidated by root without permission */
180-
#define KEY_FLAG_UID_KEYRING 12 /* set if key is a user or user session keyring */
174+
#define KEY_FLAG_DEAD 0 /* set if key type has been deleted */
175+
#define KEY_FLAG_REVOKED 1 /* set if key had been revoked */
176+
#define KEY_FLAG_IN_QUOTA 2 /* set if key consumes quota */
177+
#define KEY_FLAG_USER_CONSTRUCT 3 /* set if key is being constructed in userspace */
178+
#define KEY_FLAG_ROOT_CAN_CLEAR 4 /* set if key can be cleared by root without permission */
179+
#define KEY_FLAG_INVALIDATED 5 /* set if key has been invalidated */
180+
#define KEY_FLAG_TRUSTED 6 /* set if key is trusted */
181+
#define KEY_FLAG_TRUSTED_ONLY 7 /* set if keyring only accepts links to trusted keys */
182+
#define KEY_FLAG_BUILTIN 8 /* set if key is builtin */
183+
#define KEY_FLAG_ROOT_CAN_INVAL 9 /* set if key can be invalidated by root without permission */
184+
#define KEY_FLAG_UID_KEYRING 10 /* set if key is a user or user session keyring */
181185

182186
/* the key type and key description string
183187
* - the desc is used to match a key against search criteria
@@ -203,7 +207,6 @@ struct key {
203207
struct list_head name_link;
204208
struct assoc_array keys;
205209
};
206-
int reject_error;
207210
};
208211
};
209212

@@ -319,17 +322,27 @@ extern void key_set_timeout(struct key *, unsigned);
319322
#define KEY_NEED_SETATTR 0x20 /* Require permission to change attributes */
320323
#define KEY_NEED_ALL 0x3f /* All the above permissions */
321324

325+
static inline short key_read_state(const struct key *key)
326+
{
327+
/* Barrier versus mark_key_instantiated(). */
328+
return smp_load_acquire(&key->state);
329+
}
330+
322331
/**
323-
* key_is_instantiated - Determine if a key has been positively instantiated
332+
* key_is_positive - Determine if a key has been positively instantiated
324333
* @key: The key to check.
325334
*
326335
* Return true if the specified key has been positively instantiated, false
327336
* otherwise.
328337
*/
329-
static inline bool key_is_instantiated(const struct key *key)
338+
static inline bool key_is_positive(const struct key *key)
339+
{
340+
return key_read_state(key) == KEY_IS_POSITIVE;
341+
}
342+
343+
static inline bool key_is_negative(const struct key *key)
330344
{
331-
return test_bit(KEY_FLAG_INSTANTIATED, &key->flags) &&
332-
!test_bit(KEY_FLAG_NEGATIVE, &key->flags);
345+
return key_read_state(key) < 0;
333346
}
334347

335348
#define rcu_dereference_key(KEY) \

net/dns_resolver/dns_key.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ static int dns_resolver_match_preparse(struct key_match_data *match_data)
224224
static void dns_resolver_describe(const struct key *key, struct seq_file *m)
225225
{
226226
seq_puts(m, key->description);
227-
if (key_is_instantiated(key)) {
227+
if (key_is_positive(key)) {
228228
int err = PTR_ERR(key->payload.data[dns_key_error]);
229229

230230
if (err)

security/keys/big_key.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ void big_key_revoke(struct key *key)
138138

139139
/* clear the quota */
140140
key_payload_reserve(key, 0);
141-
if (key_is_instantiated(key) &&
141+
if (key_is_positive(key) &&
142142
(size_t)key->payload.data[big_key_len] > BIG_KEY_FILE_THRESHOLD)
143143
vfs_truncate(path, 0);
144144
}
@@ -170,7 +170,7 @@ void big_key_describe(const struct key *key, struct seq_file *m)
170170

171171
seq_puts(m, key->description);
172172

173-
if (key_is_instantiated(key))
173+
if (key_is_positive(key))
174174
seq_printf(m, ": %zu [%s]",
175175
datalen,
176176
datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff");

security/keys/encrypted-keys/encrypted.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -852,7 +852,7 @@ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep)
852852
size_t datalen = prep->datalen;
853853
int ret = 0;
854854

855-
if (test_bit(KEY_FLAG_NEGATIVE, &key->flags))
855+
if (key_is_negative(key))
856856
return -ENOKEY;
857857
if (datalen <= 0 || datalen > 32767 || !prep->data)
858858
return -EINVAL;

security/keys/gc.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,15 +129,15 @@ static noinline void key_gc_unused_keys(struct list_head *keys)
129129
while (!list_empty(keys)) {
130130
struct key *key =
131131
list_entry(keys->next, struct key, graveyard_link);
132+
short state = key->state;
133+
132134
list_del(&key->graveyard_link);
133135

134136
kdebug("- %u", key->serial);
135137
key_check(key);
136138

137139
/* Throw away the key data if the key is instantiated */
138-
if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags) &&
139-
!test_bit(KEY_FLAG_NEGATIVE, &key->flags) &&
140-
key->type->destroy)
140+
if (state == KEY_IS_POSITIVE && key->type->destroy)
141141
key->type->destroy(key);
142142

143143
security_key_free(key);
@@ -151,7 +151,7 @@ static noinline void key_gc_unused_keys(struct list_head *keys)
151151
}
152152

153153
atomic_dec(&key->user->nkeys);
154-
if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
154+
if (state != KEY_IS_UNINSTANTIATED)
155155
atomic_dec(&key->user->nikeys);
156156

157157
key_user_put(key->user);

security/keys/key.c

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,18 @@ int key_payload_reserve(struct key *key, size_t datalen)
395395
}
396396
EXPORT_SYMBOL(key_payload_reserve);
397397

398+
/*
399+
* Change the key state to being instantiated.
400+
*/
401+
static void mark_key_instantiated(struct key *key, int reject_error)
402+
{
403+
/* Commit the payload before setting the state; barrier versus
404+
* key_read_state().
405+
*/
406+
smp_store_release(&key->state,
407+
(reject_error < 0) ? reject_error : KEY_IS_POSITIVE);
408+
}
409+
398410
/*
399411
* Instantiate a key and link it into the target keyring atomically. Must be
400412
* called with the target keyring's semaphore writelocked. The target key's
@@ -418,14 +430,14 @@ static int __key_instantiate_and_link(struct key *key,
418430
mutex_lock(&key_construction_mutex);
419431

420432
/* can't instantiate twice */
421-
if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
433+
if (key->state == KEY_IS_UNINSTANTIATED) {
422434
/* instantiate the key */
423435
ret = key->type->instantiate(key, prep);
424436

425437
if (ret == 0) {
426438
/* mark the key as being instantiated */
427439
atomic_inc(&key->user->nikeys);
428-
set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
440+
mark_key_instantiated(key, 0);
429441

430442
if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
431443
awaken = 1;
@@ -553,13 +565,10 @@ int key_reject_and_link(struct key *key,
553565
mutex_lock(&key_construction_mutex);
554566

555567
/* can't instantiate twice */
556-
if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
568+
if (key->state == KEY_IS_UNINSTANTIATED) {
557569
/* mark the key as being negatively instantiated */
558570
atomic_inc(&key->user->nikeys);
559-
key->reject_error = -error;
560-
smp_wmb();
561-
set_bit(KEY_FLAG_NEGATIVE, &key->flags);
562-
set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
571+
mark_key_instantiated(key, -error);
563572
now = current_kernel_time();
564573
key->expiry = now.tv_sec + timeout;
565574
key_schedule_gc(key->expiry + key_gc_delay);
@@ -731,8 +740,8 @@ static inline key_ref_t __key_update(key_ref_t key_ref,
731740

732741
ret = key->type->update(key, prep);
733742
if (ret == 0)
734-
/* updating a negative key instantiates it */
735-
clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
743+
/* Updating a negative key positively instantiates it */
744+
mark_key_instantiated(key, 0);
736745

737746
up_write(&key->sem);
738747

@@ -967,8 +976,8 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen)
967976

968977
ret = key->type->update(key, &prep);
969978
if (ret == 0)
970-
/* updating a negative key instantiates it */
971-
clear_bit(KEY_FLAG_NEGATIVE, &key->flags);
979+
/* Updating a negative key positively instantiates it */
980+
mark_key_instantiated(key, 0);
972981

973982
up_write(&key->sem);
974983

security/keys/keyctl.c

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -738,10 +738,9 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
738738

739739
key = key_ref_to_ptr(key_ref);
740740

741-
if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) {
742-
ret = -ENOKEY;
743-
goto error2;
744-
}
741+
ret = key_read_state(key);
742+
if (ret < 0)
743+
goto error2; /* Negatively instantiated */
745744

746745
/* see if we can read it directly */
747746
ret = key_permission(key_ref, KEY_NEED_READ);
@@ -873,7 +872,7 @@ long keyctl_chown_key(key_serial_t id, uid_t user, gid_t group)
873872
atomic_dec(&key->user->nkeys);
874873
atomic_inc(&newowner->nkeys);
875874

876-
if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) {
875+
if (key->state != KEY_IS_UNINSTANTIATED) {
877876
atomic_dec(&key->user->nikeys);
878877
atomic_inc(&newowner->nikeys);
879878
}

security/keys/keyring.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ static void keyring_describe(const struct key *keyring, struct seq_file *m)
407407
else
408408
seq_puts(m, "[anon]");
409409

410-
if (key_is_instantiated(keyring)) {
410+
if (key_is_positive(keyring)) {
411411
if (keyring->keys.nr_leaves_on_tree != 0)
412412
seq_printf(m, ": %lu", keyring->keys.nr_leaves_on_tree);
413413
else
@@ -522,7 +522,8 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
522522
{
523523
struct keyring_search_context *ctx = iterator_data;
524524
const struct key *key = keyring_ptr_to_key(object);
525-
unsigned long kflags = key->flags;
525+
unsigned long kflags = READ_ONCE(key->flags);
526+
short state = READ_ONCE(key->state);
526527

527528
kenter("{%d}", key->serial);
528529

@@ -566,9 +567,8 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
566567

567568
if (ctx->flags & KEYRING_SEARCH_DO_STATE_CHECK) {
568569
/* we set a different error code if we pass a negative key */
569-
if (kflags & (1 << KEY_FLAG_NEGATIVE)) {
570-
smp_rmb();
571-
ctx->result = ERR_PTR(key->reject_error);
570+
if (state < 0) {
571+
ctx->result = ERR_PTR(state);
572572
kleave(" = %d [neg]", ctx->skipped_ret);
573573
goto skipped;
574574
}

security/keys/proc.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
182182
unsigned long timo;
183183
key_ref_t key_ref, skey_ref;
184184
char xbuf[16];
185+
short state;
185186
int rc;
186187

187188
struct keyring_search_context ctx = {
@@ -240,17 +241,19 @@ static int proc_keys_show(struct seq_file *m, void *v)
240241
sprintf(xbuf, "%luw", timo / (60*60*24*7));
241242
}
242243

244+
state = key_read_state(key);
245+
243246
#define showflag(KEY, LETTER, FLAG) \
244247
(test_bit(FLAG, &(KEY)->flags) ? LETTER : '-')
245248

246249
seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ",
247250
key->serial,
248-
showflag(key, 'I', KEY_FLAG_INSTANTIATED),
251+
state != KEY_IS_UNINSTANTIATED ? 'I' : '-',
249252
showflag(key, 'R', KEY_FLAG_REVOKED),
250253
showflag(key, 'D', KEY_FLAG_DEAD),
251254
showflag(key, 'Q', KEY_FLAG_IN_QUOTA),
252255
showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT),
253-
showflag(key, 'N', KEY_FLAG_NEGATIVE),
256+
state < 0 ? 'N' : '-',
254257
showflag(key, 'i', KEY_FLAG_INVALIDATED),
255258
atomic_read(&key->usage),
256259
xbuf,

security/keys/process_keys.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -727,7 +727,7 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
727727

728728
ret = -EIO;
729729
if (!(lflags & KEY_LOOKUP_PARTIAL) &&
730-
!test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
730+
key_read_state(key) == KEY_IS_UNINSTANTIATED)
731731
goto invalid_key;
732732

733733
/* check the permissions */

0 commit comments

Comments
 (0)