aboutsummaryrefslogtreecommitdiffstats
path: root/security
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
commit65d87fe68abf2fc226a9e96be61160f65d6b4680 (patch)
tree23881b6daf54c7522178363f0ae32ddb6c836784 /security
parent1eb1bcf5bfad001128293b86d891c9d6f2f27333 (diff)
KEYS: Perform RCU synchronisation on keys prior to key destruction
Make the keys garbage collector invoke synchronize_rcu() prior to destroying keys with a zero usage count. This means that a key can be examined under the RCU read lock in the safe knowledge that it won't get deallocated until after the lock is released - even if its usage count becomes zero whilst we're looking at it. This is useful in keyring search vs key link. Consider a keyring containing a link to a key. That link can be replaced in-place in the keyring without requiring an RCU copy-and-replace on the keyring contents without breaking a search underway on that keyring when the displaced key is released, provided the key is actually destroyed only after the RCU read lock held by the search algorithm is released. This permits __key_link() to replace a key without having to reallocate the key payload. A key gets replaced if a new key being linked into a keyring has the same type and description. Signed-off-by: David Howells <dhowells@redhat.com> Acked-by: Jeff Layton <jlayton@redhat.com>
Diffstat (limited to 'security')
-rw-r--r--security/keys/gc.c73
1 files changed, 44 insertions, 29 deletions
diff --git a/security/keys/gc.c b/security/keys/gc.c
index a42b45531aac..27610bf72195 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -168,38 +168,45 @@ do_gc:
168} 168}
169 169
170/* 170/*
171 * Garbage collect an unreferenced, detached key 171 * Garbage collect a list of unreferenced, detached keys
172 */ 172 */
173static noinline void key_gc_unused_key(struct key *key) 173static noinline void key_gc_unused_keys(struct list_head *keys)
174{ 174{
175 key_check(key); 175 while (!list_empty(keys)) {
176 176 struct key *key =
177 security_key_free(key); 177 list_entry(keys->next, struct key, graveyard_link);
178 178 list_del(&key->graveyard_link);
179 /* deal with the user's key tracking and quota */ 179
180 if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { 180 kdebug("- %u", key->serial);
181 spin_lock(&key->user->lock); 181 key_check(key);
182 key->user->qnkeys--; 182
183 key->user->qnbytes -= key->quotalen; 183 security_key_free(key);
184 spin_unlock(&key->user->lock); 184
185 } 185 /* deal with the user's key tracking and quota */
186 if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
187 spin_lock(&key->user->lock);
188 key->user->qnkeys--;
189 key->user->qnbytes -= key->quotalen;
190 spin_unlock(&key->user->lock);
191 }
186 192
187 atomic_dec(&key->user->nkeys); 193 atomic_dec(&key->user->nkeys);
188 if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) 194 if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
189 atomic_dec(&key->user->nikeys); 195 atomic_dec(&key->user->nikeys);
190 196
191 key_user_put(key->user); 197 key_user_put(key->user);
192 198
193 /* now throw away the key memory */ 199 /* now throw away the key memory */
194 if (key->type->destroy) 200 if (key->type->destroy)
195 key->type->destroy(key); 201 key->type->destroy(key);
196 202
197 kfree(key->description); 203 kfree(key->description);
198 204
199#ifdef KEY_DEBUGGING 205#ifdef KEY_DEBUGGING
200 key->magic = KEY_DEBUG_MAGIC_X; 206 key->magic = KEY_DEBUG_MAGIC_X;
201#endif 207#endif
202 kmem_cache_free(key_jar, key); 208 kmem_cache_free(key_jar, key);
209 }
203} 210}
204 211
205/* 212/*
@@ -211,6 +218,7 @@ static noinline void key_gc_unused_key(struct key *key)
211 */ 218 */
212static void key_garbage_collector(struct work_struct *work) 219static void key_garbage_collector(struct work_struct *work)
213{ 220{
221 static LIST_HEAD(graveyard);
214 static u8 gc_state; /* Internal persistent state */ 222 static u8 gc_state; /* Internal persistent state */
215#define KEY_GC_REAP_AGAIN 0x01 /* - Need another cycle */ 223#define KEY_GC_REAP_AGAIN 0x01 /* - Need another cycle */
216#define KEY_GC_REAPING_LINKS 0x02 /* - We need to reap links */ 224#define KEY_GC_REAPING_LINKS 0x02 /* - We need to reap links */
@@ -316,15 +324,22 @@ maybe_resched:
316 key_schedule_gc(new_timer); 324 key_schedule_gc(new_timer);
317 } 325 }
318 326
319 if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) { 327 if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2) ||
320 /* Make sure everyone revalidates their keys if we marked a 328 !list_empty(&graveyard)) {
321 * bunch as being dead and make sure all keyring ex-payloads 329 /* Make sure that all pending keyring payload destructions are
322 * are destroyed. 330 * fulfilled and that people aren't now looking at dead or
331 * dying keys that they don't have a reference upon or a link
332 * to.
323 */ 333 */
324 kdebug("dead sync"); 334 kdebug("gc sync");
325 synchronize_rcu(); 335 synchronize_rcu();
326 } 336 }
327 337
338 if (!list_empty(&graveyard)) {
339 kdebug("gc keys");
340 key_gc_unused_keys(&graveyard);
341 }
342
328 if (unlikely(gc_state & (KEY_GC_REAPING_DEAD_1 | 343 if (unlikely(gc_state & (KEY_GC_REAPING_DEAD_1 |
329 KEY_GC_REAPING_DEAD_2))) { 344 KEY_GC_REAPING_DEAD_2))) {
330 if (!(gc_state & KEY_GC_FOUND_DEAD_KEY)) { 345 if (!(gc_state & KEY_GC_FOUND_DEAD_KEY)) {
@@ -359,7 +374,7 @@ found_unreferenced_key:
359 rb_erase(&key->serial_node, &key_serial_tree); 374 rb_erase(&key->serial_node, &key_serial_tree);
360 spin_unlock(&key_serial_lock); 375 spin_unlock(&key_serial_lock);
361 376
362 key_gc_unused_key(key); 377 list_add_tail(&key->graveyard_link, &graveyard);
363 gc_state |= KEY_GC_REAP_AGAIN; 378 gc_state |= KEY_GC_REAP_AGAIN;
364 goto maybe_resched; 379 goto maybe_resched;
365 380