Skip to content

Commit 233e473

Browse files
committed
KEYS: Permit in-place link replacement in keyring list
Make use of the previous patch that makes the garbage collector perform RCU synchronisation before destroying defunct keys. Key pointers can now be replaced in-place without creating a new keyring payload and replacing the whole thing as the discarded keys will not be destroyed until all currently held RCU read locks are released. If the keyring payload space needs to be expanded or contracted, then a replacement will still need allocating, and the original will still have to be freed by RCU. Signed-off-by: David Howells <dhowells@redhat.com>
1 parent 65d87fe commit 233e473

File tree

3 files changed

+58
-41
lines changed

3 files changed

+58
-41
lines changed

include/keys/keyring-type.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ struct keyring_list {
2424
unsigned short maxkeys; /* max keys this list can hold */
2525
unsigned short nkeys; /* number of keys currently held */
2626
unsigned short delkey; /* key to be unlinked by RCU */
27-
struct key *keys[0];
27+
struct key __rcu *keys[0];
2828
};
2929

3030

security/keys/gc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ static void key_gc_keyring(struct key *keyring, time_t limit)
148148
loop = klist->nkeys;
149149
smp_rmb();
150150
for (loop--; loop >= 0; loop--) {
151-
key = klist->keys[loop];
151+
key = rcu_dereference(klist->keys[loop]);
152152
if (test_bit(KEY_FLAG_DEAD, &key->flags) ||
153153
(key->expiry > 0 && key->expiry <= limit))
154154
goto do_gc;

security/keys/keyring.c

Lines changed: 56 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
(keyring)->payload.subscriptions, \
2626
rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem)))
2727

28+
#define rcu_deref_link_locked(klist, index, keyring) \
29+
(rcu_dereference_protected( \
30+
(klist)->keys[index], \
31+
rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem)))
32+
2833
#define KEY_LINK_FIXQUOTA 1UL
2934

3035
/*
@@ -138,6 +143,11 @@ static int keyring_match(const struct key *keyring, const void *description)
138143
/*
139144
* Clean up a keyring when it is destroyed. Unpublish its name if it had one
140145
* and dispose of its data.
146+
*
147+
* The garbage collector detects the final key_put(), removes the keyring from
148+
* the serial number tree and then does RCU synchronisation before coming here,
149+
* so we shouldn't need to worry about code poking around here with the RCU
150+
* readlock held by this time.
141151
*/
142152
static void keyring_destroy(struct key *keyring)
143153
{
@@ -154,11 +164,10 @@ static void keyring_destroy(struct key *keyring)
154164
write_unlock(&keyring_name_lock);
155165
}
156166

157-
klist = rcu_dereference_check(keyring->payload.subscriptions,
158-
atomic_read(&keyring->usage) == 0);
167+
klist = rcu_access_pointer(keyring->payload.subscriptions);
159168
if (klist) {
160169
for (loop = klist->nkeys - 1; loop >= 0; loop--)
161-
key_put(klist->keys[loop]);
170+
key_put(rcu_access_pointer(klist->keys[loop]));
162171
kfree(klist);
163172
}
164173
}
@@ -214,7 +223,8 @@ static long keyring_read(const struct key *keyring,
214223
ret = -EFAULT;
215224

216225
for (loop = 0; loop < klist->nkeys; loop++) {
217-
key = klist->keys[loop];
226+
key = rcu_deref_link_locked(klist, loop,
227+
keyring);
218228

219229
tmp = sizeof(key_serial_t);
220230
if (tmp > buflen)
@@ -383,7 +393,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
383393
nkeys = keylist->nkeys;
384394
smp_rmb();
385395
for (kix = 0; kix < nkeys; kix++) {
386-
key = keylist->keys[kix];
396+
key = rcu_dereference(keylist->keys[kix]);
387397
kflags = key->flags;
388398

389399
/* ignore keys not of this type */
@@ -426,7 +436,7 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref,
426436
nkeys = keylist->nkeys;
427437
smp_rmb();
428438
for (; kix < nkeys; kix++) {
429-
key = keylist->keys[kix];
439+
key = rcu_dereference(keylist->keys[kix]);
430440
if (key->type != &key_type_keyring)
431441
continue;
432442

@@ -531,8 +541,7 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref,
531541
nkeys = klist->nkeys;
532542
smp_rmb();
533543
for (loop = 0; loop < nkeys ; loop++) {
534-
key = klist->keys[loop];
535-
544+
key = rcu_dereference(klist->keys[loop]);
536545
if (key->type == ktype &&
537546
(!key->type->match ||
538547
key->type->match(key, description)) &&
@@ -654,7 +663,7 @@ static int keyring_detect_cycle(struct key *A, struct key *B)
654663
nkeys = keylist->nkeys;
655664
smp_rmb();
656665
for (; kix < nkeys; kix++) {
657-
key = keylist->keys[kix];
666+
key = rcu_dereference(keylist->keys[kix]);
658667

659668
if (key == A)
660669
goto cycle_detected;
@@ -711,7 +720,7 @@ static void keyring_unlink_rcu_disposal(struct rcu_head *rcu)
711720
container_of(rcu, struct keyring_list, rcu);
712721

713722
if (klist->delkey != USHRT_MAX)
714-
key_put(klist->keys[klist->delkey]);
723+
key_put(rcu_access_pointer(klist->keys[klist->delkey]));
715724
kfree(klist);
716725
}
717726

@@ -749,24 +758,16 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
749758
/* see if there's a matching key we can displace */
750759
if (klist && klist->nkeys > 0) {
751760
for (loop = klist->nkeys - 1; loop >= 0; loop--) {
752-
if (klist->keys[loop]->type == type &&
753-
strcmp(klist->keys[loop]->description,
754-
description) == 0
755-
) {
756-
/* found a match - we'll replace this one with
757-
* the new key */
758-
size = sizeof(struct key *) * klist->maxkeys;
759-
size += sizeof(*klist);
760-
BUG_ON(size > PAGE_SIZE);
761-
762-
ret = -ENOMEM;
763-
nklist = kmemdup(klist, size, GFP_KERNEL);
764-
if (!nklist)
765-
goto error_sem;
766-
767-
/* note replacement slot */
768-
klist->delkey = nklist->delkey = loop;
769-
prealloc = (unsigned long)nklist;
761+
struct key *key = rcu_deref_link_locked(klist, loop,
762+
keyring);
763+
if (key->type == type &&
764+
strcmp(key->description, description) == 0) {
765+
/* Found a match - we'll replace the link with
766+
* one to the new key. We record the slot
767+
* position.
768+
*/
769+
klist->delkey = loop;
770+
prealloc = 0;
770771
goto done;
771772
}
772773
}
@@ -780,7 +781,7 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
780781

781782
if (klist && klist->nkeys < klist->maxkeys) {
782783
/* there's sufficient slack space to append directly */
783-
nklist = NULL;
784+
klist->delkey = klist->nkeys;
784785
prealloc = KEY_LINK_FIXQUOTA;
785786
} else {
786787
/* grow the key list */
@@ -813,10 +814,10 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
813814
}
814815

815816
/* add the key into the new space */
816-
nklist->keys[nklist->delkey] = NULL;
817+
RCU_INIT_POINTER(nklist->keys[nklist->delkey], NULL);
818+
prealloc = (unsigned long)nklist | KEY_LINK_FIXQUOTA;
817819
}
818820

819-
prealloc = (unsigned long)nklist | KEY_LINK_FIXQUOTA;
820821
done:
821822
*_prealloc = prealloc;
822823
kleave(" = 0");
@@ -862,6 +863,7 @@ void __key_link(struct key *keyring, struct key *key,
862863
unsigned long *_prealloc)
863864
{
864865
struct keyring_list *klist, *nklist;
866+
struct key *discard;
865867

866868
nklist = (struct keyring_list *)(*_prealloc & ~KEY_LINK_FIXQUOTA);
867869
*_prealloc = 0;
@@ -875,10 +877,10 @@ void __key_link(struct key *keyring, struct key *key,
875877
/* there's a matching key we can displace or an empty slot in a newly
876878
* allocated list we can fill */
877879
if (nklist) {
878-
kdebug("replace %hu/%hu/%hu",
880+
kdebug("reissue %hu/%hu/%hu",
879881
nklist->delkey, nklist->nkeys, nklist->maxkeys);
880882

881-
nklist->keys[nklist->delkey] = key;
883+
RCU_INIT_POINTER(nklist->keys[nklist->delkey], key);
882884

883885
rcu_assign_pointer(keyring->payload.subscriptions, nklist);
884886

@@ -889,9 +891,23 @@ void __key_link(struct key *keyring, struct key *key,
889891
klist->delkey, klist->nkeys, klist->maxkeys);
890892
call_rcu(&klist->rcu, keyring_unlink_rcu_disposal);
891893
}
894+
} else if (klist->delkey < klist->nkeys) {
895+
kdebug("replace %hu/%hu/%hu",
896+
klist->delkey, klist->nkeys, klist->maxkeys);
897+
898+
discard = rcu_dereference_protected(
899+
klist->keys[klist->delkey],
900+
rwsem_is_locked(&keyring->sem));
901+
rcu_assign_pointer(klist->keys[klist->delkey], key);
902+
/* The garbage collector will take care of RCU
903+
* synchronisation */
904+
key_put(discard);
892905
} else {
893906
/* there's sufficient slack space to append directly */
894-
klist->keys[klist->nkeys] = key;
907+
kdebug("append %hu/%hu/%hu",
908+
klist->delkey, klist->nkeys, klist->maxkeys);
909+
910+
RCU_INIT_POINTER(klist->keys[klist->delkey], key);
895911
smp_wmb();
896912
klist->nkeys++;
897913
}
@@ -998,7 +1014,7 @@ int key_unlink(struct key *keyring, struct key *key)
9981014
if (klist) {
9991015
/* search the keyring for the key */
10001016
for (loop = 0; loop < klist->nkeys; loop++)
1001-
if (klist->keys[loop] == key)
1017+
if (rcu_access_pointer(klist->keys[loop]) == key)
10021018
goto key_is_present;
10031019
}
10041020

@@ -1061,7 +1077,7 @@ static void keyring_clear_rcu_disposal(struct rcu_head *rcu)
10611077
klist = container_of(rcu, struct keyring_list, rcu);
10621078

10631079
for (loop = klist->nkeys - 1; loop >= 0; loop--)
1064-
key_put(klist->keys[loop]);
1080+
key_put(rcu_access_pointer(klist->keys[loop]));
10651081

10661082
kfree(klist);
10671083
}
@@ -1161,7 +1177,8 @@ void keyring_gc(struct key *keyring, time_t limit)
11611177
/* work out how many subscriptions we're keeping */
11621178
keep = 0;
11631179
for (loop = klist->nkeys - 1; loop >= 0; loop--)
1164-
if (!key_is_dead(klist->keys[loop], limit))
1180+
if (!key_is_dead(rcu_deref_link_locked(klist, loop, keyring),
1181+
limit))
11651182
keep++;
11661183

11671184
if (keep == klist->nkeys)
@@ -1182,11 +1199,11 @@ void keyring_gc(struct key *keyring, time_t limit)
11821199
*/
11831200
keep = 0;
11841201
for (loop = klist->nkeys - 1; loop >= 0; loop--) {
1185-
key = klist->keys[loop];
1202+
key = rcu_deref_link_locked(klist, loop, keyring);
11861203
if (!key_is_dead(key, limit)) {
11871204
if (keep >= max)
11881205
goto discard_new;
1189-
new->keys[keep++] = key_get(key);
1206+
RCU_INIT_POINTER(new->keys[keep++], key_get(key));
11901207
}
11911208
}
11921209
new->nkeys = keep;

0 commit comments

Comments
 (0)