From nobody Mon Jun 8 22:52:28 2026 Received: from mail-dl1-f53.google.com (mail-dl1-f53.google.com [74.125.82.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 72E52284B25 for ; Tue, 26 May 2026 02:48:58 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779763739; cv=none; b=OK1WGQlooqB19gosNdt4dKsBJEEmmtG2ku08sPq5nCkGk84sENOAWtWnLz8a8ycWV7xsLW7ObiC++ARgOdEz3RC2G0wY9+aB8k5U+3X1aH+tGaQ2SEdyJ6AuTGSDk5zhBfVvhf76IPdkbZPzv9cHhKMLfySe1XzW+l3FgZW4bR4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779763739; c=relaxed/simple; bh=saJVcll0Fh8M/y0WbTryiZRy3NgEFSzD4VO3x9LoqEw=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=jp+uHSCyb1SZ+8Fo3+uK/+hLkpEsif53uo5gkWzftvO4Lw3krukhoFKk5qglNqddPDWXs7uwggO0bYRcgEDeJGgbvilBerLSOKL4oZBu5ZPs3Ma87JiWr/+eihos0psSCQPC3+yvCPqyc9KJbfCZTFXwE7p9EHS/8osF7eS7tVw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=EmOBljky; arc=none smtp.client-ip=74.125.82.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="EmOBljky" Received: by mail-dl1-f53.google.com with SMTP id a92af1059eb24-13643f6d1a9so2171173c88.0 for ; Mon, 25 May 2026 19:48:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1779763737; x=1780368537; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=SciWLg3wzcud7J8i5XeFD4dn/8ICJfCrpXZP4khWrC0=; b=EmOBljkyKxelXrPDjI+MQMYG4PQDe+UVF0T0FMhkdT4MGjY+BGJ2BWXD5K6zzn80Dw iVYAbwPq1V//kCIonEiNFmTy0vcba9GtFTFtcWfyGe/teP/OBz7D+u+U5gnhmbpwX/2j cFmJ6Nx3oIhJPEd3iQBReAG7jlI4RF3kMvtKQaExS4OE03b0Kz+X+V9h170p5ro5UC4O cFSGjHn5phGhstbn2d+aEUAKWIOVpOFzz0gCocwfZ2hfoVbpwqop8FS6c58zCM9/ea4K BhfS0LF/usWr8X66gegyuIawhtL+ME28U0w1wHmjC8X37GExyYfI0Qa2b2F0lZj5JhdJ zJhA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779763737; x=1780368537; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=SciWLg3wzcud7J8i5XeFD4dn/8ICJfCrpXZP4khWrC0=; b=C2w2LkIf8ZmOSSALmjOzA/dxmXVcjSgX0jbrF3e4E8xQdccNmP2CrPKK1WqlzLI5Q5 LGxGjiECKsEHyV4n5/uA5Em57ZON4Y51M5/I6yHm42v1I0TX3x0zF9MFXrq3DODs5xuz EUOGqvwYqDjPlLMGc6Ejhmmy9T+TQRaxir+i2O8iCKX/7z3j8cT82Sq1jrMJinD5eWgh L7Q9XiVcZwOYUt/Rf12id7kxxSJdOBA0GxuWjE2lv4AqxXti/5LGASa/9AXG8lrTJ8KB opKTveCxod/wMOV6fuLBsnnZOhJdB8MIA48EjlI5YAU2tfAT7IcsRr6eSSzQrlHBlUw6 yR4A== X-Forwarded-Encrypted: i=1; AFNElJ+/wHBHHb4hatoQ+Np+SHes5Fz1iFyxBMAE9/PnkOwj1qZsPuWW3PshoGf1sOQx3QCVKBuLnqM5WnRY5L8=@vger.kernel.org X-Gm-Message-State: AOJu0YwIa6sP+evHL7JFnXX6XMlqcl3J/qVHKBHMoVWow/dvOxrCwLot uiPYA49rt3HOTmjIOTi24S1luF0xvHKmLNgfixsovnxqojHqghuDxje+ X-Gm-Gg: Acq92OEpAb843ebNYWp3V1LSqnWowl8jsKFZLgDTE+eYT0pf1KCFiV6gU0cExVtdpKO /YPAumj/sPHzEwsY7GghNgIR6lVd/LWb4Pq7W9+jRZupHIidHtAwp2d90sSErkptKnALMatkFhQ q/qqjShLTjrJoqBdX+y5U9Xl4cJOi1Xbpqvf4kFElh9HvMMYwheqm9CHO1bKMgpLUG0WlPKsyzJ i221Zl1tO82R2F6R7eYRVKFwOVi3As8a+YcEQJbdad4Sjs4jczpWNUSOnZsWIZ3qfwkTtqgbt6Z N4Dx4KkdZrB29+yyGvnL1VynbZ6ovE2aDZVbsZaQmtcOzmnTVIWipW1RUi1EuG5qu2SqNSnq3J7 vYU3DcKQrqppPM1Sl6D2EBiW2LB1uMJkI5n2RJkdG/PmcgXqBEDMaPb6r5r4gcTy8Q7yNMAMnrH cfUfvKssdrGsONPfePQkP+kFzUeaWBfiMfHroSHA== X-Received: by 2002:a05:7022:f401:b0:12d:de3f:f3d9 with SMTP id a92af1059eb24-1365fb62422mr5967399c88.35.1779763737313; Mon, 25 May 2026 19:48:57 -0700 (PDT) Received: from localhost.localdomain ([148.135.103.3]) by smtp.gmail.com with ESMTPSA id a92af1059eb24-1366aa9a8b2sm8652900c88.12.2026.05.25.19.48.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 25 May 2026 19:48:56 -0700 (PDT) From: Shaomin Chen To: keyrings@vger.kernel.org, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org Cc: David Howells , Jarkko Sakkinen , Paul Moore , James Morris , "Serge E. Hallyn" Subject: [PATCH] keys: Pin request_key_auth payload in instantiate paths Date: Tue, 26 May 2026 10:48:38 +0800 Message-ID: <20260526024838.3368409-1-eeesssooo020@gmail.com> X-Mailer: git-send-email 2.47.3 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" keyctl_instantiate_key_common() reads request_key_auth from the assumed auth key before copying an instantiation payload from userspace. The copy can fault and sleep. If the request completes and revokes the auth key in that window, the auth payload can be detached and freed before the instantiate path uses it again. A request-key helper reproducer can trigger this race. One helper child blocks in KEYCTL_INSTANTIATE_IOV while the original helper instantiates the requested key and returns. KASAN then reports a use-after-free from the stale request_key_auth payload in keyctl_instantiate_key_common(). 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. Reported-by: Shaomin Chen Closes: https://lore.kernel.org/r/20260519144403.436694-1-eeesssooo020@gmai= l.com Signed-off-by: Shaomin Chen --- include/keys/request_key_auth-type.h | 2 ++ security/keys/internal.h | 2 ++ security/keys/keyctl.c | 24 +++++++++++++++----- security/keys/request_key_auth.c | 33 ++++++++++++++++++++++++++-- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/include/keys/request_key_auth-type.h b/include/keys/request_ke= y_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 =20 #include +#include =20 /* * 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 2cffa6dc8255..b7b622bc36a1 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -208,6 +208,8 @@ extern struct key *request_key_auth_new(struct key *tar= get, 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); =20 extern struct key *key_get_instantiation_authkey(key_serial_t target_id); =20 diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index ef855d69c97a..d14ace88e529 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1197,9 +1197,13 @@ static long keyctl_instantiate_key_common(key_serial= _t id, if (!instkey) goto error; =20 - rka =3D instkey->payload.data[0]; - if (rka->target_key->serial !=3D id) + rka =3D request_key_auth_get(instkey); + if (!rka) { + ret =3D -EKEYREVOKED; goto error; + } + if (rka->target_key->serial !=3D id) + goto error_put_rka; =20 /* pull the payload in if one was supplied */ payload =3D NULL; @@ -1208,7 +1212,7 @@ static long keyctl_instantiate_key_common(key_serial_= t id, ret =3D -ENOMEM; payload =3D kvmalloc(plen, GFP_KERNEL); if (!payload) - goto error; + goto error_put_rka; =20 ret =3D -EFAULT; if (!copy_from_iter_full(payload, plen, from)) @@ -1234,6 +1238,8 @@ static long keyctl_instantiate_key_common(key_serial_= t id, =20 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 ti= meout, unsigned error, if (!instkey) goto error; =20 - rka =3D instkey->payload.data[0]; - if (rka->target_key->serial !=3D id) + rka =3D request_key_auth_get(instkey); + if (!rka) { + ret =3D -EKEYREVOKED; goto error; + } + if (rka->target_key->serial !=3D id) + goto error_put_rka; =20 /* find the destination keyring if present (which must also be * writable) */ ret =3D get_instantiation_keyring(ringid, rka, &dest_keyring); if (ret < 0) - goto error; + goto error_put_rka; =20 /* instantiate the key and link it into a keyring */ ret =3D key_reject_and_link(rka->target_key, timeout, error, @@ -1379,6 +1389,8 @@ long keyctl_reject_key(key_serial_t id, unsigned time= out, unsigned error, if (ret =3D=3D 0) keyctl_change_reqkey_auth(NULL); =20 +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_a= uth.c index a7d7538c1f70..282e09d8fa46 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 *); =20 /* * The request-key authorisation key type definition. @@ -115,6 +116,31 @@ static void free_request_key_auth(struct request_key_a= uth *rka) kfree(rka); } =20 +/* + * 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 =3D dereference_key_locked(authkey); + if (rka && !test_bit(KEY_FLAG_REVOKED, &authkey->flags)) + refcount_inc(&rka->usage); + else + rka =3D 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 */ @@ -136,8 +162,10 @@ static void request_key_auth_revoke(struct key *key) struct request_key_auth *rka =3D dereference_key_locked(key); =20 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); } =20 /* @@ -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); } } =20 @@ -174,6 +202,7 @@ struct key *request_key_auth_new(struct key *target, co= nst char *op, rka =3D kzalloc_obj(*rka); if (!rka) goto error; + refcount_set(&rka->usage, 1); rka->callout_info =3D kmemdup(callout_info, callout_len, GFP_KERNEL); if (!rka->callout_info) goto error_free_rka; --=20 2.47.3