aboutsummaryrefslogtreecommitdiffstats
path: root/security/keys
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2012-05-11 05:56:56 -0400
committerDavid Howells <dhowells@redhat.com>2012-05-11 05:56:56 -0400
commit233e4735f2a45d9e641c2488b8d7afeb1f377dac (patch)
treed273536aaea91cf4817dd305450f327ebb37059f /security/keys
parent65d87fe68abf2fc226a9e96be61160f65d6b4680 (diff)
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>
Diffstat (limited to 'security/keys')
-rw-r--r--security/keys/gc.c2
-rw-r--r--security/keys/keyring.c95
2 files changed, 57 insertions, 40 deletions
diff --git a/security/keys/gc.c b/security/keys/gc.c
index 27610bf72195..adddaa258d50 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -148,7 +148,7 @@ static void key_gc_keyring(struct key *keyring, time_t limit)
148 loop = klist->nkeys; 148 loop = klist->nkeys;
149 smp_rmb(); 149 smp_rmb();
150 for (loop--; loop >= 0; loop--) { 150 for (loop--; loop >= 0; loop--) {
151 key = klist->keys[loop]; 151 key = rcu_dereference(klist->keys[loop]);
152 if (test_bit(KEY_FLAG_DEAD, &key->flags) || 152 if (test_bit(KEY_FLAG_DEAD, &key->flags) ||
153 (key->expiry > 0 && key->expiry <= limit)) 153 (key->expiry > 0 && key->expiry <= limit))
154 goto do_gc; 154 goto do_gc;
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index d605f75292e4..459b3cc347f2 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -25,6 +25,11 @@
25 (keyring)->payload.subscriptions, \ 25 (keyring)->payload.subscriptions, \
26 rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem))) 26 rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem)))
27 27
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
28#define KEY_LINK_FIXQUOTA 1UL 33#define KEY_LINK_FIXQUOTA 1UL
29 34
30/* 35/*
@@ -138,6 +143,11 @@ static int keyring_match(const struct key *keyring, const void *description)
138/* 143/*
139 * Clean up a keyring when it is destroyed. Unpublish its name if it had one 144 * Clean up a keyring when it is destroyed. Unpublish its name if it had one
140 * and dispose of its data. 145 * 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.
141 */ 151 */
142static void keyring_destroy(struct key *keyring) 152static void keyring_destroy(struct key *keyring)
143{ 153{
@@ -154,11 +164,10 @@ static void keyring_destroy(struct key *keyring)
154 write_unlock(&keyring_name_lock); 164 write_unlock(&keyring_name_lock);
155 } 165 }
156 166
157 klist = rcu_dereference_check(keyring->payload.subscriptions, 167 klist = rcu_access_pointer(keyring->payload.subscriptions);
158 atomic_read(&keyring->usage) == 0);
159 if (klist) { 168 if (klist) {
160 for (loop = klist->nkeys - 1; loop >= 0; loop--) 169 for (loop = klist->nkeys - 1; loop >= 0; loop--)
161 key_put(klist->keys[loop]); 170 key_put(rcu_access_pointer(klist->keys[loop]));
162 kfree(klist); 171 kfree(klist);
163 } 172 }
164} 173}
@@ -214,7 +223,8 @@ static long keyring_read(const struct key *keyring,
214 ret = -EFAULT; 223 ret = -EFAULT;
215 224
216 for (loop = 0; loop < klist->nkeys; loop++) { 225 for (loop = 0; loop < klist->nkeys; loop++) {
217 key = klist->keys[loop]; 226 key = rcu_deref_link_locked(klist, loop,
227 keyring);
218 228
219 tmp = sizeof(key_serial_t); 229 tmp = sizeof(key_serial_t);
220 if (tmp > buflen) 230 if (tmp > buflen)
@@ -383,7 +393,7 @@ descend:
383 nkeys = keylist->nkeys; 393 nkeys = keylist->nkeys;
384 smp_rmb(); 394 smp_rmb();
385 for (kix = 0; kix < nkeys; kix++) { 395 for (kix = 0; kix < nkeys; kix++) {
386 key = keylist->keys[kix]; 396 key = rcu_dereference(keylist->keys[kix]);
387 kflags = key->flags; 397 kflags = key->flags;
388 398
389 /* ignore keys not of this type */ 399 /* ignore keys not of this type */
@@ -426,7 +436,7 @@ ascend:
426 nkeys = keylist->nkeys; 436 nkeys = keylist->nkeys;
427 smp_rmb(); 437 smp_rmb();
428 for (; kix < nkeys; kix++) { 438 for (; kix < nkeys; kix++) {
429 key = keylist->keys[kix]; 439 key = rcu_dereference(keylist->keys[kix]);
430 if (key->type != &key_type_keyring) 440 if (key->type != &key_type_keyring)
431 continue; 441 continue;
432 442
@@ -531,8 +541,7 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref,
531 nkeys = klist->nkeys; 541 nkeys = klist->nkeys;
532 smp_rmb(); 542 smp_rmb();
533 for (loop = 0; loop < nkeys ; loop++) { 543 for (loop = 0; loop < nkeys ; loop++) {
534 key = klist->keys[loop]; 544 key = rcu_dereference(klist->keys[loop]);
535
536 if (key->type == ktype && 545 if (key->type == ktype &&
537 (!key->type->match || 546 (!key->type->match ||
538 key->type->match(key, description)) && 547 key->type->match(key, description)) &&
@@ -654,7 +663,7 @@ ascend:
654 nkeys = keylist->nkeys; 663 nkeys = keylist->nkeys;
655 smp_rmb(); 664 smp_rmb();
656 for (; kix < nkeys; kix++) { 665 for (; kix < nkeys; kix++) {
657 key = keylist->keys[kix]; 666 key = rcu_dereference(keylist->keys[kix]);
658 667
659 if (key == A) 668 if (key == A)
660 goto cycle_detected; 669 goto cycle_detected;
@@ -711,7 +720,7 @@ static void keyring_unlink_rcu_disposal(struct rcu_head *rcu)
711 container_of(rcu, struct keyring_list, rcu); 720 container_of(rcu, struct keyring_list, rcu);
712 721
713 if (klist->delkey != USHRT_MAX) 722 if (klist->delkey != USHRT_MAX)
714 key_put(klist->keys[klist->delkey]); 723 key_put(rcu_access_pointer(klist->keys[klist->delkey]));
715 kfree(klist); 724 kfree(klist);
716} 725}
717 726
@@ -749,24 +758,16 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
749 /* see if there's a matching key we can displace */ 758 /* see if there's a matching key we can displace */
750 if (klist && klist->nkeys > 0) { 759 if (klist && klist->nkeys > 0) {
751 for (loop = klist->nkeys - 1; loop >= 0; loop--) { 760 for (loop = klist->nkeys - 1; loop >= 0; loop--) {
752 if (klist->keys[loop]->type == type && 761 struct key *key = rcu_deref_link_locked(klist, loop,
753 strcmp(klist->keys[loop]->description, 762 keyring);
754 description) == 0 763 if (key->type == type &&
755 ) { 764 strcmp(key->description, description) == 0) {
756 /* found a match - we'll replace this one with 765 /* Found a match - we'll replace the link with
757 * the new key */ 766 * one to the new key. We record the slot
758 size = sizeof(struct key *) * klist->maxkeys; 767 * position.
759 size += sizeof(*klist); 768 */
760 BUG_ON(size > PAGE_SIZE); 769 klist->delkey = loop;
761 770 prealloc = 0;
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;
770 goto done; 771 goto done;
771 } 772 }
772 } 773 }
@@ -780,7 +781,7 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
780 781
781 if (klist && klist->nkeys < klist->maxkeys) { 782 if (klist && klist->nkeys < klist->maxkeys) {
782 /* there's sufficient slack space to append directly */ 783 /* there's sufficient slack space to append directly */
783 nklist = NULL; 784 klist->delkey = klist->nkeys;
784 prealloc = KEY_LINK_FIXQUOTA; 785 prealloc = KEY_LINK_FIXQUOTA;
785 } else { 786 } else {
786 /* grow the key list */ 787 /* grow the key list */
@@ -813,10 +814,10 @@ int __key_link_begin(struct key *keyring, const struct key_type *type,
813 } 814 }
814 815
815 /* add the key into the new space */ 816 /* 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;
817 } 819 }
818 820
819 prealloc = (unsigned long)nklist | KEY_LINK_FIXQUOTA;
820done: 821done:
821 *_prealloc = prealloc; 822 *_prealloc = prealloc;
822 kleave(" = 0"); 823 kleave(" = 0");
@@ -862,6 +863,7 @@ void __key_link(struct key *keyring, struct key *key,
862 unsigned long *_prealloc) 863 unsigned long *_prealloc)
863{ 864{
864 struct keyring_list *klist, *nklist; 865 struct keyring_list *klist, *nklist;
866 struct key *discard;
865 867
866 nklist = (struct keyring_list *)(*_prealloc & ~KEY_LINK_FIXQUOTA); 868 nklist = (struct keyring_list *)(*_prealloc & ~KEY_LINK_FIXQUOTA);
867 *_prealloc = 0; 869 *_prealloc = 0;
@@ -875,10 +877,10 @@ void __key_link(struct key *keyring, struct key *key,
875 /* there's a matching key we can displace or an empty slot in a newly 877 /* there's a matching key we can displace or an empty slot in a newly
876 * allocated list we can fill */ 878 * allocated list we can fill */
877 if (nklist) { 879 if (nklist) {
878 kdebug("replace %hu/%hu/%hu", 880 kdebug("reissue %hu/%hu/%hu",
879 nklist->delkey, nklist->nkeys, nklist->maxkeys); 881 nklist->delkey, nklist->nkeys, nklist->maxkeys);
880 882
881 nklist->keys[nklist->delkey] = key; 883 RCU_INIT_POINTER(nklist->keys[nklist->delkey], key);
882 884
883 rcu_assign_pointer(keyring->payload.subscriptions, nklist); 885 rcu_assign_pointer(keyring->payload.subscriptions, nklist);
884 886
@@ -889,9 +891,23 @@ void __key_link(struct key *keyring, struct key *key,
889 klist->delkey, klist->nkeys, klist->maxkeys); 891 klist->delkey, klist->nkeys, klist->maxkeys);
890 call_rcu(&klist->rcu, keyring_unlink_rcu_disposal); 892 call_rcu(&klist->rcu, keyring_unlink_rcu_disposal);
891 } 893 }
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);
892 } else { 905 } else {
893 /* there's sufficient slack space to append directly */ 906 /* 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);
895 smp_wmb(); 911 smp_wmb();
896 klist->nkeys++; 912 klist->nkeys++;
897 } 913 }
@@ -998,7 +1014,7 @@ int key_unlink(struct key *keyring, struct key *key)
998 if (klist) { 1014 if (klist) {
999 /* search the keyring for the key */ 1015 /* search the keyring for the key */
1000 for (loop = 0; loop < klist->nkeys; loop++) 1016 for (loop = 0; loop < klist->nkeys; loop++)
1001 if (klist->keys[loop] == key) 1017 if (rcu_access_pointer(klist->keys[loop]) == key)
1002 goto key_is_present; 1018 goto key_is_present;
1003 } 1019 }
1004 1020
@@ -1061,7 +1077,7 @@ static void keyring_clear_rcu_disposal(struct rcu_head *rcu)
1061 klist = container_of(rcu, struct keyring_list, rcu); 1077 klist = container_of(rcu, struct keyring_list, rcu);
1062 1078
1063 for (loop = klist->nkeys - 1; loop >= 0; loop--) 1079 for (loop = klist->nkeys - 1; loop >= 0; loop--)
1064 key_put(klist->keys[loop]); 1080 key_put(rcu_access_pointer(klist->keys[loop]));
1065 1081
1066 kfree(klist); 1082 kfree(klist);
1067} 1083}
@@ -1161,7 +1177,8 @@ void keyring_gc(struct key *keyring, time_t limit)
1161 /* work out how many subscriptions we're keeping */ 1177 /* work out how many subscriptions we're keeping */
1162 keep = 0; 1178 keep = 0;
1163 for (loop = klist->nkeys - 1; loop >= 0; loop--) 1179 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))
1165 keep++; 1182 keep++;
1166 1183
1167 if (keep == klist->nkeys) 1184 if (keep == klist->nkeys)
@@ -1182,11 +1199,11 @@ void keyring_gc(struct key *keyring, time_t limit)
1182 */ 1199 */
1183 keep = 0; 1200 keep = 0;
1184 for (loop = klist->nkeys - 1; loop >= 0; loop--) { 1201 for (loop = klist->nkeys - 1; loop >= 0; loop--) {
1185 key = klist->keys[loop]; 1202 key = rcu_deref_link_locked(klist, loop, keyring);
1186 if (!key_is_dead(key, limit)) { 1203 if (!key_is_dead(key, limit)) {
1187 if (keep >= max) 1204 if (keep >= max)
1188 goto discard_new; 1205 goto discard_new;
1189 new->keys[keep++] = key_get(key); 1206 RCU_INIT_POINTER(new->keys[keep++], key_get(key));
1190 } 1207 }
1191 } 1208 }
1192 new->nkeys = keep; 1209 new->nkeys = keep;