diff options
Diffstat (limited to 'security/keys')
-rw-r--r-- | security/keys/gc.c | 2 | ||||
-rw-r--r-- | security/keys/keyring.c | 95 |
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 | */ |
142 | static void keyring_destroy(struct key *keyring) | 152 | static 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; | ||
820 | done: | 821 | done: |
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; |