diff options
-rw-r--r-- | security/keys/gc.c | 87 | ||||
-rw-r--r-- | security/keys/internal.h | 2 | ||||
-rw-r--r-- | security/keys/key.c | 72 |
3 files changed, 85 insertions, 76 deletions
diff --git a/security/keys/gc.c b/security/keys/gc.c index 89df6b5f203..b23db3fbb32 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -10,6 +10,8 @@ */ #include <linux/module.h> +#include <linux/slab.h> +#include <linux/security.h> #include <keys/keyring-type.h> #include "internal.h" @@ -19,12 +21,18 @@ unsigned key_gc_delay = 5 * 60; /* - * Reaper + * Reaper for unused keys. + */ +static void key_gc_unused_keys(struct work_struct *work); +DECLARE_WORK(key_gc_unused_work, key_gc_unused_keys); + +/* + * Reaper for links from keyrings to dead keys. */ static void key_gc_timer_func(unsigned long); -static void key_garbage_collector(struct work_struct *); +static void key_gc_dead_links(struct work_struct *); static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0); -static DECLARE_WORK(key_gc_work, key_garbage_collector); +static DECLARE_WORK(key_gc_work, key_gc_dead_links); static key_serial_t key_gc_cursor; /* the last key the gc considered */ static bool key_gc_again; static unsigned long key_gc_executing; @@ -108,10 +116,12 @@ do_gc: } /* - * Garbage collector for keys. This involves scanning the keyrings for dead, - * expired and revoked keys that have overstayed their welcome + * Garbage collector for links to dead keys. + * + * This involves scanning the keyrings for dead, expired and revoked keys that + * have overstayed their welcome */ -static void key_garbage_collector(struct work_struct *work) +static void key_gc_dead_links(struct work_struct *work) { struct rb_node *rb; key_serial_t cursor; @@ -220,3 +230,68 @@ reached_the_end: } kleave(" [end]"); } + +/* + * Garbage collector for unused keys. + * + * This is done in process context so that we don't have to disable interrupts + * all over the place. key_put() schedules this rather than trying to do the + * cleanup itself, which means key_put() doesn't have to sleep. + */ +static void key_gc_unused_keys(struct work_struct *work) +{ + struct rb_node *_n; + struct key *key; + +go_again: + /* look for a dead key in the tree */ + spin_lock(&key_serial_lock); + + for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { + key = rb_entry(_n, struct key, serial_node); + + if (atomic_read(&key->usage) == 0) + goto found_dead_key; + } + + spin_unlock(&key_serial_lock); + return; + +found_dead_key: + /* we found a dead key - once we've removed it from the tree, we can + * drop the lock */ + rb_erase(&key->serial_node, &key_serial_tree); + spin_unlock(&key_serial_lock); + + key_check(key); + + security_key_free(key); + + /* deal with the user's key tracking and quota */ + if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { + spin_lock(&key->user->lock); + key->user->qnkeys--; + key->user->qnbytes -= key->quotalen; + spin_unlock(&key->user->lock); + } + + atomic_dec(&key->user->nkeys); + if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) + atomic_dec(&key->user->nikeys); + + key_user_put(key->user); + + /* now throw away the key memory */ + if (key->type->destroy) + key->type->destroy(key); + + kfree(key->description); + +#ifdef KEY_DEBUGGING + key->magic = KEY_DEBUG_MAGIC_X; +#endif + kmem_cache_free(key_jar, key); + + /* there may, of course, be more than one key to destroy */ + goto go_again; +} diff --git a/security/keys/internal.h b/security/keys/internal.h index f375152a250..a7cd1a68232 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -75,6 +75,7 @@ extern unsigned key_quota_maxbytes; #define KEYQUOTA_LINK_BYTES 4 /* a link in a keyring is worth 4 bytes */ +extern struct kmem_cache *key_jar; extern struct rb_root key_serial_tree; extern spinlock_t key_serial_lock; extern struct mutex key_construction_mutex; @@ -146,6 +147,7 @@ extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, extern long join_session_keyring(const char *name); +extern struct work_struct key_gc_unused_work; extern unsigned key_gc_delay; extern void keyring_gc(struct key *keyring, time_t limit); extern void key_schedule_gc(time_t expiry_at); diff --git a/security/keys/key.c b/security/keys/key.c index f7f9d93f08d..991a15f1e85 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -21,7 +21,7 @@ #include <linux/user_namespace.h> #include "internal.h" -static struct kmem_cache *key_jar; +struct kmem_cache *key_jar; struct rb_root key_serial_tree; /* tree of keys indexed by serial */ DEFINE_SPINLOCK(key_serial_lock); @@ -36,9 +36,6 @@ unsigned int key_quota_maxbytes = 20000; /* general key space quota */ static LIST_HEAD(key_types_list); static DECLARE_RWSEM(key_types_sem); -static void key_cleanup(struct work_struct *work); -static DECLARE_WORK(key_cleanup_task, key_cleanup); - /* We serialise key instantiation and link */ DEFINE_MUTEX(key_construction_mutex); @@ -591,71 +588,6 @@ int key_reject_and_link(struct key *key, } EXPORT_SYMBOL(key_reject_and_link); -/* - * Garbage collect keys in process context so that we don't have to disable - * interrupts all over the place. - * - * key_put() schedules this rather than trying to do the cleanup itself, which - * means key_put() doesn't have to sleep. - */ -static void key_cleanup(struct work_struct *work) -{ - struct rb_node *_n; - struct key *key; - -go_again: - /* look for a dead key in the tree */ - spin_lock(&key_serial_lock); - - for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { - key = rb_entry(_n, struct key, serial_node); - - if (atomic_read(&key->usage) == 0) - goto found_dead_key; - } - - spin_unlock(&key_serial_lock); - return; - -found_dead_key: - /* we found a dead key - once we've removed it from the tree, we can - * drop the lock */ - rb_erase(&key->serial_node, &key_serial_tree); - spin_unlock(&key_serial_lock); - - key_check(key); - - security_key_free(key); - - /* deal with the user's key tracking and quota */ - if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { - spin_lock(&key->user->lock); - key->user->qnkeys--; - key->user->qnbytes -= key->quotalen; - spin_unlock(&key->user->lock); - } - - atomic_dec(&key->user->nkeys); - if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) - atomic_dec(&key->user->nikeys); - - key_user_put(key->user); - - /* now throw away the key memory */ - if (key->type->destroy) - key->type->destroy(key); - - kfree(key->description); - -#ifdef KEY_DEBUGGING - key->magic = KEY_DEBUG_MAGIC_X; -#endif - kmem_cache_free(key_jar, key); - - /* there may, of course, be more than one key to destroy */ - goto go_again; -} - /** * key_put - Discard a reference to a key. * @key: The key to discard a reference from. @@ -670,7 +602,7 @@ void key_put(struct key *key) key_check(key); if (atomic_dec_and_test(&key->usage)) - schedule_work(&key_cleanup_task); + schedule_work(&key_gc_unused_work); } } EXPORT_SYMBOL(key_put); |