From: Jarkko Sakkinen <jarkko@kernel.org>
To: David Howells <dhowells@redhat.com>
Cc: Eric Biggers <ebiggers@kernel.org>,
linux-integrity@vger.kernel.org, Mimi Zohar <zohar@linux.ibm.com>,
Jarkko Sakkinen <jarkko@kernel.org>,
Roberto Sassu <roberto.sassu@huawei.com>,
Dmitry Kasatkin <dmitry.kasatkin@gmail.com>,
Eric Snowberg <eric.snowberg@oracle.com>,
Paul Moore <paul@paul-moore.com>,
James Morris <jmorris@namei.org>,
"Serge E. Hallyn" <serge@hallyn.com>,
linux-security-module@vger.kernel.org (open list:SECURITY
SUBSYSTEM), linux-kernel@vger.kernel.org (open list),
keyrings@vger.kernel.org (open list:KEYS/KEYRINGS)
Subject: [RFC PATCH 3/4] KEYS: Remove key_type_dead
Date: Mon, 2 Nov 2026 14:15:14 +0200 [thread overview]
Message-ID: <20261102121518.1197453-4-jarkko@kernel.org> (raw)
In-Reply-To: <20261102121518.1197453-1-jarkko@kernel.org>
unregister_key_type() no longer needs to retype live keys as keys and
restrictions pin the module while they reference it.
Remove key_type_dead and associated code from the keyring GC.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
security/keys/gc.c | 136 ++-------------------------------------
security/keys/internal.h | 4 --
security/keys/key.c | 9 +--
security/keys/keyring.c | 50 --------------
4 files changed, 9 insertions(+), 190 deletions(-)
diff --git a/security/keys/gc.c b/security/keys/gc.c
index df5c614e4d1d..876c30c019ad 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -29,22 +29,8 @@ 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
@@ -56,7 +42,7 @@ void key_schedule_gc(time64_t gc_at)
kenter("%lld", gc_at - now);
- if (gc_at <= now || test_bit(KEY_GC_REAP_KEYTYPE, &key_gc_flags)) {
+ if (gc_at <= now) {
kdebug("IMMEDIATE");
schedule_work(&key_gc_work);
} else if (gc_at < key_gc_next_run) {
@@ -100,36 +86,6 @@ static void key_gc_timer_func(struct timer_list *unused)
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
*/
@@ -181,30 +137,20 @@ static noinline void key_gc_unused_keys(struct list_head *keys)
static void key_garbage_collector(struct work_struct *work)
{
static LIST_HEAD(graveyard);
- static u8 gc_state; /* Internal persistent state */
+ u8 gc_state = 0;
#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);
+ kenter("[%lx]", key_gc_flags);
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;
@@ -226,18 +172,6 @@ static void key_garbage_collector(struct work_struct *work)
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))
@@ -249,21 +183,10 @@ static void key_garbage_collector(struct work_struct *work)
}
}
- 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;
+ if ((gc_state & KEY_GC_REAPING_LINKS) &&
+ key->type == &key_type_keyring)
+ goto found_keyring;
- skip_dead_key:
if (spin_is_contended(&key_serial_lock) || need_resched())
goto contended;
}
@@ -289,8 +212,7 @@ static void key_garbage_collector(struct work_struct *work)
key_schedule_gc(new_timer);
}
- if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2) ||
- !list_empty(&graveyard)) {
+ if (!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
@@ -298,32 +220,10 @@ static void key_garbage_collector(struct work_struct *work)
*/
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);
@@ -341,14 +241,6 @@ static void key_garbage_collector(struct work_struct *work)
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
@@ -358,18 +250,4 @@ static void key_garbage_collector(struct work_struct *work)
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;
}
diff --git a/security/keys/internal.h b/security/keys/internal.h
index b7b622bc36a1..005d609ae2df 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -38,7 +38,6 @@ struct iovec;
no_printk(KERN_DEBUG FMT"\n", ##__VA_ARGS__)
#endif
-extern struct key_type key_type_dead;
extern struct key_type key_type_user;
extern struct key_type key_type_logon;
@@ -165,12 +164,9 @@ extern void key_change_session_keyring(struct callback_head *twork);
extern struct work_struct key_gc_work;
extern unsigned key_gc_delay;
extern void keyring_gc(struct key *keyring, time64_t limit);
-extern void keyring_restriction_gc(struct key *keyring,
- struct key_type *dead_type);
void key_set_expiry(struct key *key, time64_t expiry);
extern void key_schedule_gc(time64_t gc_at);
extern void key_schedule_gc_links(void);
-extern void key_gc_keytype(struct key_type *ktype);
extern int key_task_permission(const key_ref_t key_ref,
const struct cred *cred,
diff --git a/security/keys/key.c b/security/keys/key.c
index 5ba8810f903d..f396e56d640f 100644
--- a/security/keys/key.c
+++ b/security/keys/key.c
@@ -1281,18 +1281,14 @@ EXPORT_SYMBOL(__register_key_type);
* unregister_key_type - Unregister a type of key.
* @ktype: The key type.
*
- * Unregister a key type and mark all the extant keys of this type as dead.
- * Those keys of this type are then destroyed to get rid of their payloads and
- * they and their links will be garbage collected as soon as possible.
+ * Unregister a key type.
*/
void unregister_key_type(struct key_type *ktype)
{
down_write(&key_types_sem);
list_del_init(&ktype->link);
- downgrade_write(&key_types_sem);
- key_gc_keytype(ktype);
pr_notice("Key type %s unregistered\n", ktype->name);
- up_read(&key_types_sem);
+ up_write(&key_types_sem);
}
EXPORT_SYMBOL(unregister_key_type);
@@ -1307,7 +1303,6 @@ void __init key_init(void)
/* add the special key types */
list_add_tail(&key_type_keyring.link, &key_types_list);
- list_add_tail(&key_type_dead.link, &key_types_list);
list_add_tail(&key_type_user.link, &key_types_list);
list_add_tail(&key_type_logon.link, &key_types_list);
diff --git a/security/keys/keyring.c b/security/keys/keyring.c
index 2061fc2022a5..94a886a84bd6 100644
--- a/security/keys/keyring.c
+++ b/security/keys/keyring.c
@@ -1787,53 +1787,3 @@ void keyring_gc(struct key *keyring, time64_t limit)
up_write(&keyring->sem);
kleave(" [gc]");
}
-
-/*
- * Garbage collect restriction pointers from a keyring.
- *
- * Keyring restrictions are associated with a key type, and must be cleaned
- * up if the key type is unregistered. The restriction is altered to always
- * reject additional keys so a keyring cannot be opened up by unregistering
- * a key type.
- *
- * Not called with any keyring locks held. The keyring's key struct will not
- * be deallocated under us as only our caller may deallocate it.
- *
- * The caller is required to hold key_types_sem and dead_type->sem. This is
- * fulfilled by key_gc_keytype() holding the locks on behalf of
- * key_garbage_collector(), which it invokes on a workqueue.
- */
-void keyring_restriction_gc(struct key *keyring, struct key_type *dead_type)
-{
- struct key_restriction *keyres;
-
- kenter("%x{%s}", keyring->serial, keyring->description ?: "");
-
- /*
- * keyring->restrict_link is only assigned at key allocation time
- * or with the key type locked, so the only values that could be
- * concurrently assigned to keyring->restrict_link are for key
- * types other than dead_type. Given this, it's ok to check
- * the key type before acquiring keyring->sem.
- */
- if (!dead_type || !keyring->restrict_link ||
- keyring->restrict_link->keytype != dead_type) {
- kleave(" [no restriction gc]");
- return;
- }
-
- /* Lock the keyring to ensure that a link is not in progress */
- down_write(&keyring->sem);
-
- keyres = keyring->restrict_link;
-
- keyres->check = restrict_link_reject;
-
- key_put(keyres->key);
- keyres->key = NULL;
- keyres->keytype = NULL;
-
- up_write(&keyring->sem);
-
- kleave(" [restriction gc]");
-}
--
2.47.3
next prev parent reply other threads:[~2026-11-02 12:16 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-02 12:15 [RFC PATCH 0/4] KEYS: sanitize the garbage collector behavior Jarkko Sakkinen
2026-11-02 12:15 ` [RFC PATCH 1/4] KEYS: trusted: Disable sysfs bind attributes for TEE driver Jarkko Sakkinen
2026-11-02 12:27 ` sashiko-bot
2026-11-02 12:15 ` [RFC PATCH 2/4] KEYS: Pin the backing module for struct key_type Jarkko Sakkinen
2026-11-02 12:22 ` Jarkko Sakkinen
2026-11-02 12:32 ` sashiko-bot
2026-11-02 12:41 ` Jarkko Sakkinen
2026-11-02 12:15 ` Jarkko Sakkinen [this message]
2026-11-02 12:21 ` [RFC PATCH 3/4] KEYS: Remove key_type_dead Jarkko Sakkinen
2026-11-02 12:35 ` sashiko-bot
2026-11-02 12:15 ` [RFC PATCH 4/4] KEYS: key_garbage_collector(): process in chunks Jarkko Sakkinen
2026-11-02 12:31 ` Jarkko Sakkinen
2026-11-02 12:32 ` sashiko-bot
2026-11-02 13:34 ` Jarkko Sakkinen
2026-11-02 12:19 ` [RFC PATCH 0/4] KEYS: sanitize the garbage collector behavior Jarkko Sakkinen
2026-11-03 0:03 ` Jarkko Sakkinen
2026-11-03 0:42 ` Jarkko Sakkinen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261102121518.1197453-4-jarkko@kernel.org \
--to=jarkko@kernel.org \
--cc=dhowells@redhat.com \
--cc=dmitry.kasatkin@gmail.com \
--cc=ebiggers@kernel.org \
--cc=eric.snowberg@oracle.com \
--cc=jmorris@namei.org \
--cc=keyrings@vger.kernel.org \
--cc=linux-integrity@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=paul@paul-moore.com \
--cc=roberto.sassu@huawei.com \
--cc=serge@hallyn.com \
--cc=zohar@linux.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.