* [PATCH v3 0/2] crypto: algif_skcipher - fix AIO IV race in stable trees
@ 2026-07-16 2:58 Muhammet Kaan KILINÇ
2026-07-16 2:58 ` [PATCH v3 1/2] crypto: algif_skcipher - force synchronous processing Muhammet Kaan KILINÇ
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Muhammet Kaan KILINÇ @ 2026-07-16 2:58 UTC (permalink / raw)
To: stable, gregkh, sashal
Cc: linux-crypto, herbert, ebiggers, Muhammet Kaan KILINÇ
The AF_ALG skcipher AIO path passes the socket-wide ctx->iv as a raw
pointer into the async request. After io_submit() the socket lock is
dropped and the request is processed by a worker, which dereferences
ctx->iv only later. A concurrent sendmsg(ALG_SET_IV) on the same socket
can overwrite ctx->iv inside this window, so the in-flight request runs
under an attacker-controlled IV. For CTR and other stream modes this is
IV/keystream reuse and lets an unprivileged user recover the plaintext
of a concurrent operation.
Both patches fix this the same way: force the operation synchronous, so
ctx->iv is only ever dereferenced under the socket lock held by
recvmsg(). They are split only because the surrounding code differs
between trees, not because the approach differs.
v2 used a per-request IV snapshot for the async path instead. Sasha
Levin pointed out that this is wrong, and it is: for ciphers with
statesize == 0 - which includes cbc and ctr - skcipher_prepare_alg()
installs skcipher_noimport()/skcipher_noexport(), so ctx->state carries
nothing and the MSG_MORE inter-chunk IV chaining is carried solely by
the in-place req->iv writeback. A snapshot redirects that writeback into
per-request memory that af_alg_free_resources() releases on completion,
so AIO + MSG_MORE with cbc/ctr silently produces wrong output. Writing
the IV back from the completion callback instead is not possible either:
that would require lock_sock() there, but the callback can run in
softirq/atomic context, so it must not sleep. Forcing the operation
synchronous avoids both problems and leaves the req->iv writeback
landing in ctx->iv as before.
Mainline removed the AIO socket path entirely in commit fcc77d33a34c
("net: Remove support for AIO on sockets"), which is what closes this
race upstream: the sync/async split no longer exists there, so mainline
is not affected. That removal landed after 7.1, so 7.1.y is still
affected along with every older supported tree. That commit is a
tree-wide removal across net/ and does not apply to these trees as-is,
so this is sent under option 3 of
stable-kernel-rules.rst: a change equivalent to one already mainlined,
adjusted for the older series. The equivalence is in the end state for
this file - algif_skcipher never processes an AIO request
asynchronously - and the deviation is that these patches touch only
crypto/algif_skcipher.c rather than removing AIO socket support across
the tree, which would be far too invasive for stable. io_submit() now
completes synchronously, which is valid for the AIO interface; AF_ALG
async is rarely used in practice.
The patches are not tagged as a port of that commit, because they share
no code with it: the equivalence is functional, not textual. It is
also checkable: after patch 1, _skcipher_recvmsg() matches mainline's
crypto/algif_skcipher.c as it stands today - no msg_iocb branch, no
algif_skcipher_done(), a single crypto_wait_req() - down to the same
now-dead -EIOCBQUEUED check left in place in skcipher_recvmsg().
All seven currently supported stable trees still carry the async branch
and are affected. Please apply, in two variants because the surrounding
code differs:
- patch 1 to 7.1.y, 6.18.y and 6.12.y, which have ctx->state, cflags
and algif_skcipher_export().
- patch 2 to 6.6.y, 6.1.y, 5.15.y and 5.10.y, which have none of
those.
Each patch applies cleanly to every tree listed for it, as of 7.1.3,
6.18.38, 6.12.95, 6.6.144, 6.1.177, 5.15.211 and 5.10.260.
This is distinct from CVE-2026-31677 (a skcipher receive-accounting
fix); it is an IV-handling race, not a receive-space guardrail.
Reported to security@kernel.org on 2026-06-07 (follow-up 2026-06-19, no
response). As the mainline removal is independent of that report and is
not backportable, I am sending the stable fix here.
Verified on 6.19.14. That tree is no longer supported, but
crypto/algif_skcipher.c is byte-identical there and on 7.1.y, 6.18.y
and 6.12.y, so this exercises exactly the code patch 1 targets. Same
tests across three kernels:
MSG_MORE chained AIO IV race over 200000
output correct? ops: times the attacker
IV was injected
unpatched yes 2857 (victim plaintext
recovered on all of them)
v2 snapshot NO, silently wrong 0
this patch, force-sync yes 0
- MSG_MORE chaining: two 16 byte chunks over separate io_submit()
calls must equal the single-shot 32 byte reference. Checked for
ctr(aes), cbc(aes) and the cryptd() variants of both, which
exercise deferred completion. The v2 snapshot fails all of them
(first chunk correct, second chunk encrypted under the stale IV);
unpatched and this patch both pass, byte-identical output.
- Race: a concurrent sendmsg(ALG_SET_IV) hammer against 200000 AIO
encrypt ops recovers the victim plaintext on the unpatched kernel
and injects nothing on the patched one.
This supersedes v1 and v2:
https://lore.kernel.org/linux-crypto/20260705220112.2522-1-muhammetkaankilinc@gmail.com
https://lore.kernel.org/linux-crypto/20260712022618.1665-1-muhammetkaankilinc@gmail.com
Changes in v3: both patches now force the operation synchronous. The v2
snapshot approach in patch 1 is dropped entirely, along with the framing
that it mirrors commit 5aa58c3a572b ("crypto: algif_aead - snapshot IV
for async AEAD requests"). That comparison was wrong: aead has no
inter-request IV chaining, skcipher does. Thanks to Sasha Levin for
catching this.
A working PoC is available to maintainers on request; it will be
published after the fix is picked up by the stable trees.
Muhammet Kaan KILINÇ (2):
crypto: algif_skcipher - force synchronous processing
crypto: algif_skcipher - force synchronous processing on trees without
ctx->state
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH v3 1/2] crypto: algif_skcipher - force synchronous processing
2026-07-16 2:58 [PATCH v3 0/2] crypto: algif_skcipher - fix AIO IV race in stable trees Muhammet Kaan KILINÇ
@ 2026-07-16 2:58 ` Muhammet Kaan KILINÇ
2026-07-16 2:58 ` [PATCH v3 2/2] crypto: algif_skcipher - force synchronous processing on trees without ctx->state Muhammet Kaan KILINÇ
2026-07-17 1:37 ` [PATCH v3 0/2] crypto: algif_skcipher - fix AIO IV race in stable trees Sasha Levin
2 siblings, 0 replies; 4+ messages in thread
From: Muhammet Kaan KILINÇ @ 2026-07-16 2:58 UTC (permalink / raw)
To: stable, gregkh, sashal
Cc: linux-crypto, herbert, ebiggers, Muhammet Kaan KILINÇ
The AIO/async path in skcipher_recvmsg() passes the socket-wide ctx->iv
directly into the skcipher request. After io_submit() the socket lock is
dropped and the request is processed asynchronously by a worker (e.g.
cryptd), which dereferences ctx->iv only later.
A concurrent sendmsg(ALG_SET_IV) on the same socket can overwrite ctx->iv
inside this window, so the in-flight request runs under an
attacker-controlled IV. For CTR and other stream modes this causes
IV/keystream reuse and allows an unprivileged user to recover the
plaintext of a concurrent operation.
Snapshotting ctx->iv into per-request storage for the async path is not
sufficient here. For ciphers with statesize == 0 - which includes cbc
and ctr - skcipher_prepare_alg() installs skcipher_noimport()/
skcipher_noexport(), so ctx->state carries nothing and the MSG_MORE
inter-chunk IV chaining is carried solely by the in-place req->iv
writeback. A snapshot redirects that writeback into per-request memory
that af_alg_free_resources() releases on completion, so AIO + MSG_MORE
with cbc/ctr would silently produce wrong output. Writing the IV back
from the completion callback instead is not possible either: that would
require lock_sock() there, but the callback can run in softirq/atomic
context, so it must not sleep.
Make the operation synchronous instead. ctx->iv is then only ever
dereferenced under the socket lock held by recvmsg(), which removes the
race, and the req->iv writeback lands in ctx->iv as before, which keeps
MSG_MORE chaining intact for statesize == 0 ciphers. The ctx->state
import/export path is unchanged for ciphers that do have state.
This is equivalent to the upstream resolution: commit fcc77d33a34c
("net: Remove support for AIO on sockets") removed the AIO socket path
across net/ entirely, producing the same end state for this file -
algif_skcipher never processes an AIO request asynchronously. After this
patch, _skcipher_recvmsg() matches mainline's crypto/algif_skcipher.c as
it stands today, including the same now-dead -EIOCBQUEUED check. This
patch deviates from that commit deliberately: rather than removing AIO
socket support tree-wide, which would be far too invasive for stable, it
removes only the AIO branch in crypto/algif_skcipher.c. io_submit() now
completes synchronously, which is valid for the AIO interface; AF_ALG
async is rarely used in practice.
The -EIOCBQUEUED check in skcipher_recvmsg() is now dead but harmless,
and is left alone to keep the fix minimal.
Fixes: e870456d8e7c ("crypto: algif_skcipher - overhaul memory management")
Cc: <stable@vger.kernel.org>
Reported-by: Muhammet Kaan KILINÇ <muhammetkaankilinc@gmail.com>
Signed-off-by: Muhammet Kaan KILINÇ <muhammetkaankilinc@gmail.com>
---
v3: Replaces the v2 per-request IV snapshot with forced synchronous
processing, after Sasha Levin pointed out that the snapshot silently
breaks MSG_MORE IV chaining for statesize == 0 ciphers (cbc, ctr).
The v2 framing of this as the counterpart of 5aa58c3a572b ("crypto:
algif_aead - snapshot IV for async AEAD requests") was wrong: aead
has no inter-request IV chaining, skcipher does.
No Closes: tag: the underlying vulnerability was reported privately to
security@kernel.org (2026-06-07), so there is no public report URL.
crypto/algif_skcipher.c | 75 +++++++++++++----------------------------
1 file changed, 24 insertions(+), 51 deletions(-)
diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c
index ba0a17f..35ebc3e 100644
--- a/crypto/algif_skcipher.c
+++ b/crypto/algif_skcipher.c
@@ -79,20 +79,6 @@ static int algif_skcipher_export(struct sock *sk, struct skcipher_request *req)
return err;
}
-static void algif_skcipher_done(void *data, int err)
-{
- struct af_alg_async_req *areq = data;
- struct sock *sk = areq->sk;
-
- if (err)
- goto out;
-
- err = algif_skcipher_export(sk, &areq->cra_u.skcipher_req);
-
-out:
- af_alg_async_cb(data, err);
-}
-
static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg,
size_t ignored, int flags)
{
@@ -171,43 +157,30 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg,
cflags |= CRYPTO_SKCIPHER_REQ_CONT;
}
- if (msg->msg_iocb && !is_sync_kiocb(msg->msg_iocb)) {
- /* AIO operation */
- sock_hold(sk);
- areq->iocb = msg->msg_iocb;
-
- /* Remember output size that will be generated. */
- areq->outlen = len;
-
- skcipher_request_set_callback(&areq->cra_u.skcipher_req,
- cflags |
- CRYPTO_TFM_REQ_MAY_SLEEP,
- algif_skcipher_done, areq);
- err = ctx->enc ?
- crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) :
- crypto_skcipher_decrypt(&areq->cra_u.skcipher_req);
-
- /* AIO operation in progress */
- if (err == -EINPROGRESS)
- return -EIOCBQUEUED;
-
- sock_put(sk);
- } else {
- /* Synchronous operation */
- skcipher_request_set_callback(&areq->cra_u.skcipher_req,
- cflags |
- CRYPTO_TFM_REQ_MAY_SLEEP |
- CRYPTO_TFM_REQ_MAY_BACKLOG,
- crypto_req_done, &ctx->wait);
- err = crypto_wait_req(ctx->enc ?
- crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) :
- crypto_skcipher_decrypt(&areq->cra_u.skcipher_req),
- &ctx->wait);
-
- if (!err)
- err = algif_skcipher_export(
- sk, &areq->cra_u.skcipher_req);
- }
+ /*
+ * Force synchronous processing. The async (AIO) path passed the
+ * socket-wide ctx->iv into the request, which the worker
+ * dereferenced after the socket lock had been dropped, letting a
+ * concurrent sendmsg(ALG_SET_IV) inject an attacker IV. Mainline
+ * removed the AIO socket path in commit fcc77d33a34c ("net: Remove
+ * support for AIO on sockets"); the minimal stable fix is to always
+ * complete synchronously, so ctx->iv is only ever dereferenced under
+ * the socket lock. This also keeps the IV chaining intact: for
+ * ciphers with statesize == 0 (e.g. ctr, cbc) the chained IV is
+ * carried by the req->iv writeback into ctx->iv, which is only
+ * consistent on the synchronous path.
+ */
+ skcipher_request_set_callback(&areq->cra_u.skcipher_req,
+ cflags |
+ CRYPTO_TFM_REQ_MAY_SLEEP |
+ CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &ctx->wait);
+ err = crypto_wait_req(ctx->enc ?
+ crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) :
+ crypto_skcipher_decrypt(&areq->cra_u.skcipher_req),
+ &ctx->wait);
+ if (!err)
+ err = algif_skcipher_export(sk, &areq->cra_u.skcipher_req);
free:
af_alg_free_resources(areq);
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v3 2/2] crypto: algif_skcipher - force synchronous processing on trees without ctx->state
2026-07-16 2:58 [PATCH v3 0/2] crypto: algif_skcipher - fix AIO IV race in stable trees Muhammet Kaan KILINÇ
2026-07-16 2:58 ` [PATCH v3 1/2] crypto: algif_skcipher - force synchronous processing Muhammet Kaan KILINÇ
@ 2026-07-16 2:58 ` Muhammet Kaan KILINÇ
2026-07-17 1:37 ` [PATCH v3 0/2] crypto: algif_skcipher - fix AIO IV race in stable trees Sasha Levin
2 siblings, 0 replies; 4+ messages in thread
From: Muhammet Kaan KILINÇ @ 2026-07-16 2:58 UTC (permalink / raw)
To: stable, gregkh, sashal
Cc: linux-crypto, herbert, ebiggers, Muhammet Kaan KILINÇ
The AIO/async path in skcipher_recvmsg() passes the socket-wide ctx->iv
directly into the skcipher request. After io_submit() the socket lock is
dropped and the request is processed asynchronously, so a concurrent
sendmsg(ALG_SET_IV) can overwrite ctx->iv and make the in-flight request
run under an attacker-controlled IV. For CTR/stream modes this is
IV/keystream reuse and lets an unprivileged user recover the plaintext of
a concurrent operation.
Snapshotting ctx->iv into per-request storage for the async path is not
sufficient. For ciphers with statesize == 0 - which includes cbc and ctr -
the MSG_MORE inter-chunk IV chaining is carried solely by the in-place
req->iv writeback, which a snapshot redirects into per-request memory that
af_alg_free_resources() releases on completion, silently producing wrong
output. Writing the IV back from the completion callback instead is not
possible either: that would require lock_sock() there, but the callback can
run in softirq/atomic context, so it must not sleep.
Make the operation synchronous instead, which removes both the IV race and
any writeback race. This is equivalent to the upstream resolution, commit
fcc77d33a34c ("net: Remove support for AIO on sockets"), which removed the
AIO socket path across net/ entirely and so produces the same end state for
this file. This patch deviates from that commit deliberately: rather than
removing AIO socket support tree-wide, which would be far too invasive for
stable, it removes only the AIO branch in crypto/algif_skcipher.c.
io_submit() now completes synchronously; AF_ALG async is rarely used in
practice.
The -EIOCBQUEUED check in skcipher_recvmsg() is now dead but harmless,
and is left alone to keep the fix minimal.
Tested on 6.6.y: attacker IV injection dropped from 2296/200000 to 0/200000
after the change; MSG_MORE chunked CTR output bit-identical to single-shot.
Reported-by: Muhammet Kaan KILINÇ <muhammetkaankilinc@gmail.com>
Fixes: e870456d8e7c ("crypto: algif_skcipher - overhaul memory management")
Cc: <stable@vger.kernel.org>
Signed-off-by: Muhammet Kaan KILINÇ <muhammetkaankilinc@gmail.com>
---
v3: No functional change to this patch. Patch 1 now uses the same
force-sync approach after Sasha Levin pointed out that the v2
snapshot silently breaks MSG_MORE IV chaining for statesize == 0
ciphers. The two patches remain separate only because the
surrounding code differs: 7.1.y, 6.18.y and 6.12.y have ctx->state,
cflags and algif_skcipher_export(); 6.6.y, 6.1.y, 5.15.y and 5.10.y
do not, so the diffs do not apply across both.
No Closes: tag: the underlying vulnerability was reported privately to
security@kernel.org (2026-06-07), so there is no public report URL.
crypto/algif_skcipher.c | 49 +++++++++++++++--------------------------
1 file changed, 18 insertions(+), 31 deletions(-)
diff --git a/crypto/algif_skcipher.c b/crypto/algif_skcipher.c
index e31b1da58..b12df4544 100644
--- a/crypto/algif_skcipher.c
+++ b/crypto/algif_skcipher.c
@@ -109,33 +109,20 @@ static int _skcipher_recvmsg(struct socket *sock, struct msghdr *msg,
- if (msg->msg_iocb && !is_sync_kiocb(msg->msg_iocb)) {
- /* AIO operation */
- sock_hold(sk);
- areq->iocb = msg->msg_iocb;
-
- /* Remember output size that will be generated. */
- areq->outlen = len;
-
- skcipher_request_set_callback(&areq->cra_u.skcipher_req,
- CRYPTO_TFM_REQ_MAY_SLEEP,
- af_alg_async_cb, areq);
- err = ctx->enc ?
- crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) :
- crypto_skcipher_decrypt(&areq->cra_u.skcipher_req);
-
- /* AIO operation in progress */
- if (err == -EINPROGRESS)
- return -EIOCBQUEUED;
-
- sock_put(sk);
- } else {
- /* Synchronous operation */
- skcipher_request_set_callback(&areq->cra_u.skcipher_req,
- CRYPTO_TFM_REQ_MAY_SLEEP |
- CRYPTO_TFM_REQ_MAY_BACKLOG,
- crypto_req_done, &ctx->wait);
- err = crypto_wait_req(ctx->enc ?
- crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) :
- crypto_skcipher_decrypt(&areq->cra_u.skcipher_req),
- &ctx->wait);
- }
+ /*
+ * Force synchronous processing. The async (AIO) path passed the
+ * socket-wide ctx->iv into the request, which the worker
+ * dereferenced after the socket lock was dropped, letting a
+ * concurrent sendmsg(ALG_SET_IV) inject an attacker IV. Mainline
+ * removed the AIO socket path in commit fcc77d33a34c ("net: Remove
+ * support for AIO on sockets"); these stable trees lack the
+ * per-request ctx->state used by newer kernels, so the minimal safe
+ * fix is to always complete synchronously.
+ */
+ skcipher_request_set_callback(&areq->cra_u.skcipher_req,
+ CRYPTO_TFM_REQ_MAY_SLEEP |
+ CRYPTO_TFM_REQ_MAY_BACKLOG,
+ crypto_req_done, &ctx->wait);
+ err = crypto_wait_req(ctx->enc ?
+ crypto_skcipher_encrypt(&areq->cra_u.skcipher_req) :
+ crypto_skcipher_decrypt(&areq->cra_u.skcipher_req),
+ &ctx->wait);
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v3 0/2] crypto: algif_skcipher - fix AIO IV race in stable trees
2026-07-16 2:58 [PATCH v3 0/2] crypto: algif_skcipher - fix AIO IV race in stable trees Muhammet Kaan KILINÇ
2026-07-16 2:58 ` [PATCH v3 1/2] crypto: algif_skcipher - force synchronous processing Muhammet Kaan KILINÇ
2026-07-16 2:58 ` [PATCH v3 2/2] crypto: algif_skcipher - force synchronous processing on trees without ctx->state Muhammet Kaan KILINÇ
@ 2026-07-17 1:37 ` Sasha Levin
2 siblings, 0 replies; 4+ messages in thread
From: Sasha Levin @ 2026-07-17 1:37 UTC (permalink / raw)
To: stable, gregkh
Cc: Sasha Levin, linux-crypto, herbert, ebiggers,
Muhammet Kaan KILINÇ
On Thu, Jul 16, 2026 at 02:58:35AM +0000, Muhammet Kaan KILINÇ wrote:
> Both patches fix this the same way: force the operation synchronous, so
> ctx->iv is only ever dereferenced under the socket lock held by
> recvmsg(). They are split only because the surrounding code differs
> between trees, not because the approach differs.
Queued patch 1 for 7.1, 6.18, and 6.12, and patch 2 for 6.6, 6.1,
5.15, and 5.10, thanks.
--
Thanks,
Sasha
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-07-17 1:38 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-16 2:58 [PATCH v3 0/2] crypto: algif_skcipher - fix AIO IV race in stable trees Muhammet Kaan KILINÇ
2026-07-16 2:58 ` [PATCH v3 1/2] crypto: algif_skcipher - force synchronous processing Muhammet Kaan KILINÇ
2026-07-16 2:58 ` [PATCH v3 2/2] crypto: algif_skcipher - force synchronous processing on trees without ctx->state Muhammet Kaan KILINÇ
2026-07-17 1:37 ` [PATCH v3 0/2] crypto: algif_skcipher - fix AIO IV race in stable trees Sasha Levin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox