Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373// SPDX-License-Identifier: GPL-2.0-or-later/* Key garbage collector * * Copyright (C) 2009-2011 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com) */
#include <linux/slab.h>#include <linux/security.h>#include <keys/keyring-type.h>#include "internal.h"
/* * Delay between key revocation/expiry in seconds */unsigned key_gc_delay = 5 * 60;
/* * Reaper for unused keys. */static void key_garbage_collector(struct work_struct *work);DECLARE_WORK(key_gc_work, key_garbage_collector);
/* * Reaper for links from keyrings to dead keys. */static void key_gc_timer_func(struct timer_list *);static DEFINE_TIMER(key_gc_timer, key_gc_timer_func);
static time64_t key_gc_next_run = TIME64_MAX;static struct key_type *key_gc_dead_keytype;
static unsigned long key_gc_flags;#define KEY_GC_KEY_EXPIRED 0 /* A key expired and needs unlinking */#define KEY_GC_REAP_KEYTYPE 1 /* A keytype is being unregistered */#define KEY_GC_REAPING_KEYTYPE 2 /* Cleared when keytype reaped */
/* * Any key whose type gets unregistered will be re-typed to this if it can't be * immediately unlinked. */struct key_type key_type_dead = { .name = ".dead",};
/* * Schedule a garbage collection run. * - time precision isn't particularly important */void key_schedule_gc(time64_t gc_at){ unsigned long expires; time64_t now = ktime_get_real_seconds();
kenter("%lld", gc_at - now);
if (gc_at <= now || test_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) { kdebug("IMMEDIATE"); schedule_work(&key_gc_work); } else if (gc_at < key_gc_next_run) { kdebug("DEFERRED"); key_gc_next_run = gc_at; expires = jiffies + (gc_at - now) * HZ; mod_timer(&key_gc_timer, expires); }}
/* * Set the expiration time on a key. */void key_set_expiry(struct key *key, time64_t expiry){ key->expiry = expiry; if (expiry != TIME64_MAX) { if (!(key->type->flags & KEY_TYPE_INSTANT_REAP)) expiry += key_gc_delay; key_schedule_gc(expiry); }}
/* * Schedule a dead links collection run. */void key_schedule_gc_links(void){ set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags); schedule_work(&key_gc_work);}
/* * Some key's cleanup time was met after it expired, so we need to get the * reaper to go through a cycle finding expired keys. */static void key_gc_timer_func(struct timer_list *unused){ kenter(""); key_gc_next_run = TIME64_MAX; key_schedule_gc_links();}
/* * Reap keys of dead type. * * We use three flags to make sure we see three complete cycles of the garbage * collector: the first to mark keys of that type as being dead, the second to * collect dead links and the third to clean up the dead keys. We have to be * careful as there may already be a cycle in progress. * * The caller must be holding key_types_sem. */void key_gc_keytype(struct key_type *ktype){ kenter("%s", ktype->name);
key_gc_dead_keytype = ktype; set_bit(KEY_GC_REAPING_KEYTYPE, &key_gc_flags); smp_mb(); set_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags);
kdebug("schedule"); schedule_work(&key_gc_work);
kdebug("sleep"); wait_on_bit(&key_gc_flags, KEY_GC_REAPING_KEYTYPE, TASK_UNINTERRUPTIBLE);
key_gc_dead_keytype = NULL; kleave("");}
/* * Garbage collect a list of unreferenced, detached keys */static noinline void key_gc_unused_keys(struct list_head *keys){ while (!list_empty(keys)) { struct key *key = list_entry(keys->next, struct key, graveyard_link); short state = key->state;
list_del(&key->graveyard_link);
kdebug("- %u", key->serial); key_check(key);
#ifdef CONFIG_KEY_NOTIFICATIONS remove_watch_list(key->watchers, key->serial); key->watchers = NULL;#endif
/* Throw away the key data if the key is instantiated */ if (state == KEY_IS_POSITIVE && key->type->destroy) key->type->destroy(key);
security_key_free(key);
atomic_dec(&key->user->nkeys); if (state != KEY_IS_UNINSTANTIATED) atomic_dec(&key->user->nikeys);
key_user_put(key->user); key_put_tag(key->domain_tag); kfree(key->description);
memzero_explicit(key, sizeof(*key)); kmem_cache_free(key_jar, key); }}
/* * 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_garbage_collector(struct work_struct *work){ static LIST_HEAD(graveyard); static u8 gc_state; /* Internal persistent state */#define KEY_GC_REAP_AGAIN 0x01 /* - Need another cycle */#define KEY_GC_REAPING_LINKS 0x02 /* - We need to reap links */#define KEY_GC_REAPING_DEAD_1 0x10 /* - We need to mark dead keys */#define KEY_GC_REAPING_DEAD_2 0x20 /* - We need to reap dead key links */#define KEY_GC_REAPING_DEAD_3 0x40 /* - We need to reap dead keys */#define KEY_GC_FOUND_DEAD_KEY 0x80 /* - We found at least one dead key */
struct rb_node *cursor; struct key *key; time64_t new_timer, limit, expiry;
kenter("[%lx,%x]", key_gc_flags, gc_state);
limit = ktime_get_real_seconds();
/* Work out what we're going to be doing in this pass */ gc_state &= KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2; gc_state <<= 1; if (test_and_clear_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags)) gc_state |= KEY_GC_REAPING_LINKS;
if (test_and_clear_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) gc_state |= KEY_GC_REAPING_DEAD_1; kdebug("new pass %x", gc_state);
new_timer = TIME64_MAX;
/* As only this function is permitted to remove things from the key * serial tree, if cursor is non-NULL then it will always point to a * valid node in the tree - even if lock got dropped. */ spin_lock(&key_serial_lock); cursor = rb_first(&key_serial_tree);
continue_scanning: while (cursor) { key = rb_entry(cursor, struct key, serial_node); cursor = rb_next(cursor);
if (!test_bit_acquire(KEY_FLAG_USER_ALIVE, &key->flags)) { /* Clobber key->user after final put seen. */ goto found_unreferenced_key; }
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) { if (key->type == key_gc_dead_keytype) { gc_state |= KEY_GC_FOUND_DEAD_KEY; set_bit(KEY_FLAG_DEAD, &key->flags); key->perm = 0; goto skip_dead_key; } else if (key->type == &key_type_keyring && key->restrict_link) { goto found_restricted_keyring; } }
expiry = key->expiry; if (expiry != TIME64_MAX) { if (!(key->type->flags & KEY_TYPE_INSTANT_REAP)) expiry += key_gc_delay; if (expiry > limit && expiry < new_timer) { kdebug("will expire %x in %lld", key_serial(key), key->expiry - limit); new_timer = key->expiry; } }
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) if (key->type == key_gc_dead_keytype) gc_state |= KEY_GC_FOUND_DEAD_KEY;
if ((gc_state & KEY_GC_REAPING_LINKS) || unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) { if (key->type == &key_type_keyring) goto found_keyring; }
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_3)) if (key->type == key_gc_dead_keytype) goto destroy_dead_key;
skip_dead_key: if (spin_is_contended(&key_serial_lock) || need_resched()) goto contended; }
contended: spin_unlock(&key_serial_lock);
maybe_resched: if (cursor) { cond_resched(); spin_lock(&key_serial_lock); goto continue_scanning; }
/* We've completed the pass. Set the timer if we need to and queue a * new cycle if necessary. We keep executing cycles until we find one * where we didn't reap any keys. */ kdebug("pass complete");
if (new_timer != TIME64_MAX) { new_timer += key_gc_delay; key_schedule_gc(new_timer); }
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2) || !list_empty(&graveyard)) { /* Make sure that all pending keyring payload destructions are * fulfilled and that people aren't now looking at dead or * dying keys that they don't have a reference upon or a link * to. */ kdebug("gc sync"); synchronize_rcu(); }
if (!list_empty(&graveyard)) { kdebug("gc keys"); key_gc_unused_keys(&graveyard); }
if (unlikely(gc_state & (KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2))) { if (!(gc_state & KEY_GC_FOUND_DEAD_KEY)) { /* No remaining dead keys: short circuit the remaining * keytype reap cycles. */ kdebug("dead short"); gc_state &= ~(KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2); gc_state |= KEY_GC_REAPING_DEAD_3; } else { gc_state |= KEY_GC_REAP_AGAIN; } }
if (unlikely(gc_state & KEY_GC_REAPING_DEAD_3)) { kdebug("dead wake"); clear_and_wake_up_bit(KEY_GC_REAPING_KEYTYPE, &key_gc_flags); }
if (gc_state & KEY_GC_REAP_AGAIN) schedule_work(&key_gc_work); kleave(" [end %x]", gc_state); return;
/* We found an unreferenced key - once we've removed it from the tree, * we can safely drop the lock. */found_unreferenced_key: kdebug("unrefd key %d", key->serial); rb_erase(&key->serial_node, &key_serial_tree); spin_unlock(&key_serial_lock);
list_add_tail(&key->graveyard_link, &graveyard); gc_state |= KEY_GC_REAP_AGAIN; goto maybe_resched;
/* We found a restricted keyring and need to update the restriction if * it is associated with the dead key type. */found_restricted_keyring: spin_unlock(&key_serial_lock); keyring_restriction_gc(key, key_gc_dead_keytype); goto maybe_resched;
/* We found a keyring and we need to check the payload for links to * dead or expired keys. We don't flag another reap immediately as we * have to wait for the old payload to be destroyed by RCU before we * can reap the keys to which it refers. */found_keyring: spin_unlock(&key_serial_lock); keyring_gc(key, limit); goto maybe_resched;
/* We found a dead key that is still referenced. Reset its type and * destroy its payload with its semaphore held. */destroy_dead_key: spin_unlock(&key_serial_lock); kdebug("destroy key %d", key->serial); down_write(&key->sem); key->type = &key_type_dead; if (key_gc_dead_keytype->destroy) key_gc_dead_keytype->destroy(key); memset(&key->payload, KEY_DESTROY, sizeof(key->payload)); up_write(&key->sem); goto maybe_resched;}