From: sashiko-bot@kernel.org
To: "Jarkko Sakkinen" <jarkko@kernel.org>,
"David Howells" <dhowells@redhat.com>
Cc: "Eric Biggers" <ebiggers@kernel.org>,
"Mimi Zohar" <zohar@linux.ibm.com>,
"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>,
"open list:SECURITY SUBSYSTEM"
<linux-security-module@vger.kernel.org>,
"open list:KEYS/KEYRINGS" <keyrings@vger.kernel.org>
Subject: Re: [RFC PATCH 3/4] KEYS: Remove key_type_dead
Date: Mon, 02 Nov 2026 12:35:10 +0000 [thread overview]
Message-ID: <sashiko-outbox-193425@kernel.org> (raw)
In-Reply-To: <20261102121518.1197453-4-jarkko@kernel.org>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] KEYS: unregister_key_type() leaves live keys active for runtime callers
- [High] KEYS: UAF in unregister_key_type() when module_init fails after registration
--
commit 5a5ac1aac59bbfa4e21a7a0f1fe3391d7d6ec2c7
Author: Jarkko Sakkinen <jarkko@kernel.org>
KEYS: Remove key_type_dead
This commit removes key_type_dead, key_gc_keytype(), and
keyring_restriction_gc(), assuming that pinning the key type's owning module
while keys or restrictions exist prevents unregister_key_type() from being
called while keys of that type are alive.
> diff --git a/security/keys/key.c b/security/keys/key.c
> index 9f1f08fcbe863..c0efb87b6705d 100644
> --- a/security/keys/key.c
> +++ b/security/keys/key.c
> @@ -1279,18 +1279,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);
> }
[Severity: High]
What happens when unregister_key_type() is called at runtime from a device
driver removal callback or error recovery path rather than module unload?
Pinning ktype->owner in key_alloc() and keyring_restrict() prevents
delete_module() from running module_exit, but it does not stop runtime calls
to unregister_key_type() while the module remains loaded or when the key type
is built-in.
For example, in security/keys/trusted-keys/trusted_tee.c, when the parent
driver unbinds and optee_remove_common() -> optee_unregister_devices() ->
device_unregister() invokes trusted_key_remove(), it unregisters
key_type_trusted and immediately frees pvt_data.ctx:
security/keys/trusted-keys/trusted_tee.c:trusted_key_remove() {
unregister_key_type(&key_type_trusted);
tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
tee_client_close_context(pvt_data.ctx);
}
Without key_gc_keytype() marking extant keys dead, unlinking them, destroying
their payloads via trusted_destroy(), and retyping them to key_type_dead,
existing trusted keys remain active. If userspace subsequently updates or
instantiates one of those keys via keyctl_update_key() or
keyctl_instantiate_key(), trusted_update() or trusted_instantiate() calls into
trusted_tee_seal(), trusted_tee_unseal(), or trusted_tee_get_random() and
dereferences the freed pvt_data.ctx and pvt_data.dev:
security/keys/trusted-keys/trusted_tee.c:trusted_tee_seal() {
reg_shm = tee_shm_register_kernel_buf(pvt_data.ctx, p->key,
sizeof(p->key) + sizeof(p->blob));
if (IS_ERR(reg_shm)) {
dev_err(pvt_data.dev, "shm register failed\n");
return PTR_ERR(reg_shm);
}
}
Similarly, in arch/s390/kernel/cert_store.c, where key_type_cert_store_key is
built-in (owner is null), a runtime certificate store refresh via
create_cs_keyring() -> cleanup_cs_keys() relies on unregister_key_type() to
force-reap and destroy extant cert_store_key keys when per-key invalidation
fails before re-registering key_type_cert_store_key:
arch/s390/kernel/cert_store.c:cleanup_cs_keys() {
if (invalidate_keyring_keys(cs_keyring))
unregister_key_type(&key_type_cert_store_key);
}
In both paths, if register_key_type() is later called again on the same static
struct key_type (in create_cs_keyring() or trusted_key_probe()) while keys
from the previous registration are still alive, __register_key_type() will
also zero ktype->lock_class while live keys still have key->sem registered
with lockdep using &ktype->lock_class:
security/keys/key.c:__register_key_type() {
ktype->owner = owner;
memset(&ktype->lock_class, 0, sizeof(ktype->lock_class));
}
Doesn't unregister_key_type() still need to reap and retype extant keys so
runtime callers cannot leave live keys referencing freed driver resources or
active lockdep classes?
[Severity: High]
Could removing key_gc_keytype() and keyring_restriction_gc() from
unregister_key_type() also cause a use-after-free when a module's init
function fails after calling register_key_type()?
Several loadable modules register key types before performing subsequent
fallible initialization steps and call unregister_key_type() on their init
error unwind path, such as af_rxrpc_init() in net/rxrpc/af_rxrpc.c,
turris_signing_key_init() in drivers/platform/cznic/turris-signing-key.c,
init_nfs_fs() in fs/nfs/inode.c, nfs_idmap_init() in fs/nfs/nfs4idmap.c,
init_cifs() in fs/smb/client/cifsfs.c, and init_ceph_lib() in
net/ceph/ceph_common.c:
drivers/platform/cznic/turris-signing-key.c:turris_signing_key_init() {
if (IS_ERR(turris_signing_keyring)) {
pr_err("Cannot allocate Turris keyring\n");
unregister_key_type(&turris_signing_key_type);
return PTR_ERR(turris_signing_keyring);
}
}
While mod->init() is running in do_init_module() in kernel/module/main.c,
module_is_live(mod) is true, so try_module_get(type->owner) in key_alloc() and
keyring_restrict() succeeds as soon as register_key_type() publishes the key
type. If a concurrent task allocates a key or restricts a keyring with that
key type and a later step in mod->init() fails, the error path calls
unregister_key_type() and do_init_module() unconditionally frees the module
regardless of its reference count:
kernel/module/main.c:do_init_module() {
fail:
/* Try to protect us from buggy refcounters. */
mod->state = MODULE_STATE_GOING;
synchronize_rcu();
module_put(mod);
blocking_notifier_call_chain(&module_notify_list,
MODULE_STATE_GOING, mod);
klp_module_going(mod);
ftrace_release_mod(mod);
free_module(mod);
}
Previously, key_gc_keytype() synchronously destroyed key payloads, retyped
those keys to key_type_dead, and cleared keyring restrictions via
keyring_restriction_gc() before unregister_key_type() returned. Without that
cleanup, won't surviving keys and restrictions retain dangling pointers into
freed module memory when later accessed or garbage-collected in
key_gc_unused_keys() in security/keys/gc.c and keyring_destroy() in
security/keys/keyring.c:
security/keys/gc.c:key_gc_unused_keys() {
struct key *key =
list_entry(keys->next, struct key, graveyard_link);
struct module *owner = key->type->owner;
short state = key->state;
...
if (state == KEY_IS_POSITIVE && key->type->destroy)
key->type->destroy(key);
...
module_put(owner);
}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261102121518.1197453-1-jarkko@kernel.org?part=3
next prev parent reply other threads:[~2026-11-02 12:35 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 ` [RFC PATCH 3/4] KEYS: Remove key_type_dead Jarkko Sakkinen
2026-11-02 12:21 ` Jarkko Sakkinen
2026-11-02 12:35 ` sashiko-bot [this message]
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=sashiko-outbox-193425@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=dhowells@redhat.com \
--cc=dmitry.kasatkin@gmail.com \
--cc=ebiggers@kernel.org \
--cc=eric.snowberg@oracle.com \
--cc=jarkko@kernel.org \
--cc=jmorris@namei.org \
--cc=keyrings@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=paul@paul-moore.com \
--cc=roberto.sassu@huawei.com \
--cc=sashiko-reviews@lists.linux.dev \
--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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox