summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShaomin Chen <eeesssooo020@gmail.com>2026-06-10 13:10:05 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2026-07-04 13:42:25 +0200
commit35ab4db86774d82389e4b9559e26ab7f68d8e395 (patch)
tree5116f3ed8c8e8088a96e2a2f5542491ebd86e7bc
parent5966e4e2ba213ab7ad559166152eb4f1f170dd2c (diff)
downloadlinux-stable-35ab4db86774d82389e4b9559e26ab7f68d8e395.tar.gz
linux-stable-35ab4db86774d82389e4b9559e26ab7f68d8e395.zip
keys: Pin request_key_auth payload in instantiate paths
commit fd15b457a86939c38aa12116adabd8ff686c5e51 upstream. A: request_key() B: KEYCTL_INSTANTIATE_IOV ================ ========================= create auth key store rka in auth key wait for helper get auth key load rka from auth key copy user payload sleep on #PF helper completed detach and free rka destroy auth key wake up use rka->target_key **USE-AFTER-FREE** Give request_key_auth payloads a refcount. Take a payload reference while authkey->sem stabilizes the payload and revocation state. Hold that reference across the instantiate and reject paths. Drop the auth key owning reference from revoke and destroy. [jarkko: Replaced the first two paragraphs of text with an actual concurrency scenario.] Cc: stable@vger.kernel.org # v5.10+ Fixes: b5f545c880a2 ("[PATCH] keys: Permit running process to instantiate keys") Reported-by: Shaomin Chen <eeesssooo020@gmail.com> Closes: https://lore.kernel.org/r/20260519144403.436694-1-eeesssooo020@gmail.com Signed-off-by: Shaomin Chen <eeesssooo020@gmail.com> Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--include/keys/request_key_auth-type.h2
-rw-r--r--security/keys/internal.h2
-rw-r--r--security/keys/keyctl.c24
-rw-r--r--security/keys/request_key_auth.c33
4 files changed, 53 insertions, 8 deletions
diff --git a/include/keys/request_key_auth-type.h b/include/keys/request_key_auth-type.h
index 36b89a933310..01e42ee5f409 100644
--- a/include/keys/request_key_auth-type.h
+++ b/include/keys/request_key_auth-type.h
@@ -9,12 +9,14 @@
#define _KEYS_REQUEST_KEY_AUTH_TYPE_H
#include <linux/key.h>
+#include <linux/refcount.h>
/*
* Authorisation record for request_key().
*/
struct request_key_auth {
struct rcu_head rcu;
+ refcount_t usage;
struct key *target_key;
struct key *dest_keyring;
const struct cred *cred;
diff --git a/security/keys/internal.h b/security/keys/internal.h
index ec2ec335b613..b919495243e7 100644
--- a/security/keys/internal.h
+++ b/security/keys/internal.h
@@ -215,6 +215,8 @@ extern struct key *request_key_auth_new(struct key *target,
const void *callout_info,
size_t callout_len,
struct key *dest_keyring);
+struct request_key_auth *request_key_auth_get(struct key *authkey);
+void request_key_auth_put(struct request_key_auth *rka);
extern struct key *key_get_instantiation_authkey(key_serial_t target_id);
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index aa1dc43b16dd..127c17191785 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1196,9 +1196,13 @@ static long keyctl_instantiate_key_common(key_serial_t id,
if (!instkey)
goto error;
- rka = instkey->payload.data[0];
- if (rka->target_key->serial != id)
+ rka = request_key_auth_get(instkey);
+ if (!rka) {
+ ret = -EKEYREVOKED;
goto error;
+ }
+ if (rka->target_key->serial != id)
+ goto error_put_rka;
/* pull the payload in if one was supplied */
payload = NULL;
@@ -1207,7 +1211,7 @@ static long keyctl_instantiate_key_common(key_serial_t id,
ret = -ENOMEM;
payload = kvmalloc(plen, GFP_KERNEL);
if (!payload)
- goto error;
+ goto error_put_rka;
ret = -EFAULT;
if (!copy_from_iter_full(payload, plen, from))
@@ -1233,6 +1237,8 @@ static long keyctl_instantiate_key_common(key_serial_t id,
error2:
kvfree_sensitive(payload, plen);
+error_put_rka:
+ request_key_auth_put(rka);
error:
return ret;
}
@@ -1358,15 +1364,19 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error,
if (!instkey)
goto error;
- rka = instkey->payload.data[0];
- if (rka->target_key->serial != id)
+ rka = request_key_auth_get(instkey);
+ if (!rka) {
+ ret = -EKEYREVOKED;
goto error;
+ }
+ if (rka->target_key->serial != id)
+ goto error_put_rka;
/* find the destination keyring if present (which must also be
* writable) */
ret = get_instantiation_keyring(ringid, rka, &dest_keyring);
if (ret < 0)
- goto error;
+ goto error_put_rka;
/* instantiate the key and link it into a keyring */
ret = key_reject_and_link(rka->target_key, timeout, error,
@@ -1379,6 +1389,8 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error,
if (ret == 0)
keyctl_change_reqkey_auth(NULL);
+error_put_rka:
+ request_key_auth_put(rka);
error:
return ret;
}
diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c
index 8f33cd170e42..bd34317b58cc 100644
--- a/security/keys/request_key_auth.c
+++ b/security/keys/request_key_auth.c
@@ -23,6 +23,7 @@ static void request_key_auth_describe(const struct key *, struct seq_file *);
static void request_key_auth_revoke(struct key *);
static void request_key_auth_destroy(struct key *);
static long request_key_auth_read(const struct key *, char *, size_t);
+static void request_key_auth_rcu_disposal(struct rcu_head *);
/*
* The request-key authorisation key type definition.
@@ -116,6 +117,31 @@ static void free_request_key_auth(struct request_key_auth *rka)
}
/*
+ * Take a reference to the request-key authorisation payload so callers can
+ * drop authkey->sem before doing operations that may sleep.
+ */
+struct request_key_auth *request_key_auth_get(struct key *authkey)
+{
+ struct request_key_auth *rka;
+
+ down_read(&authkey->sem);
+ rka = dereference_key_locked(authkey);
+ if (rka && !test_bit(KEY_FLAG_REVOKED, &authkey->flags))
+ refcount_inc(&rka->usage);
+ else
+ rka = NULL;
+ up_read(&authkey->sem);
+
+ return rka;
+}
+
+void request_key_auth_put(struct request_key_auth *rka)
+{
+ if (rka && refcount_dec_and_test(&rka->usage))
+ call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
+}
+
+/*
* Dispose of the request_key_auth record under RCU conditions
*/
static void request_key_auth_rcu_disposal(struct rcu_head *rcu)
@@ -136,8 +162,10 @@ static void request_key_auth_revoke(struct key *key)
struct request_key_auth *rka = dereference_key_locked(key);
kenter("{%d}", key->serial);
+ if (!rka)
+ return;
rcu_assign_keypointer(key, NULL);
- call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
+ request_key_auth_put(rka);
}
/*
@@ -150,7 +178,7 @@ static void request_key_auth_destroy(struct key *key)
kenter("{%d}", key->serial);
if (rka) {
rcu_assign_keypointer(key, NULL);
- call_rcu(&rka->rcu, request_key_auth_rcu_disposal);
+ request_key_auth_put(rka);
}
}
@@ -174,6 +202,7 @@ struct key *request_key_auth_new(struct key *target, const char *op,
rka = kzalloc(sizeof(*rka), GFP_KERNEL);
if (!rka)
goto error;
+ refcount_set(&rka->usage, 1);
rka->callout_info = kmemdup(callout_info, callout_len, GFP_KERNEL);
if (!rka->callout_info)
goto error_free_rka;