From: Karl Mehltretter <kmehltretter@gmail.com>
To: Herbert Xu <herbert@gondor.apana.org.au>,
"David S. Miller" <davem@davemloft.net>
Cc: Karl Mehltretter <kmehltretter@gmail.com>,
linux-crypto@vger.kernel.org, linux-kernel@vger.kernel.org,
bpf@vger.kernel.org
Subject: [PATCH] crypto: lskcipher - preserve state across unaligned chunks
Date: Sat, 29 Aug 2026 21:43:14 +0200 [thread overview]
Message-ID: <20260829194314.42685-1-kmehltretter@gmail.com> (raw)
crypto_lskcipher_crypt_unaligned() splits a request into page-sized
chunks but never sets CRYPTO_LSKCIPHER_FLAG_CONT, so a stateful algorithm
restarts from its keyed state at every page boundary. It also marks every
chunk CRYPTO_LSKCIPHER_FLAG_FINAL, which is wrong but harmless: chunks are
trimmed to a multiple of the chunk size, so the trailing partial block that
FINAL guards against is caught after the loop instead.
ARC4 is the only lskcipher with internal state, and cbc, the other
in-tree user of the direct API, never passes more than one block, so
this went unnoticed. bpf_crypto_crypt() however hands
__bpf_dynptr_data() to crypto_lskcipher_encrypt() unchecked, leaving
both alignment and length to the BPF program. An 8192-byte ARC4 request
offset by one byte comes back with its second page identical to its
first: the keystream is reused.
Set FINAL only on the last chunk and CONT after the first, mirroring the
progression used by crypto_lskcipher_crypt_sg().
Fixes: 0ae4dcc1ebf6 ("crypto: skcipher - Add internal state support")
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@gmail.com>
---
Reproduced on a Raspberry Pi 400 (Cortex-A72) with a BPF program that
creates a "skcipher"/"arc4" context, takes a dynptr over an 8192-byte map
value, offsets it by one with bpf_dynptr_adjust() and calls
bpf_crypto_encrypt(). Unpatched, the second page of the output equals the
first; patched, it matches an aligned run exactly, final state included.
Rebuilding that needs two programs: bpf_crypto_ctx_create() is KF_SLEEPABLE
and only available to BPF_PROG_TYPE_SYSCALL, bpf_crypto_encrypt() only to
SCHED_CLS/SCHED_ACT/XDP, so the context passes between them as a kptr.
With CONFIG_CRYPTO_ARC4=m, load arc4 first: arc4.ko advertises only the
legacy "ecb(arc4)" alias, so a cold create with algo "arc4" fails with
-EOPNOTSUPP before reaching this path. CONFIG_CRYPTO_ARC4=y also works.
ecb(aes), cbc(aes) and cbc(camellia) were checked the same way before and
after at several lengths and misalignments and are unchanged; with generic
ciphers they have alignmask 0 and never enter the helper.
The helper is not dead code, in case removing it looks tempting: ecb() and
cbc() inherit the wrapped cipher's alignmask (crypto/ecb.c), and geode-aes,
padlock-aes and sparc64 camellia still declare one, so those instances use
it today. They are all stateless, hence unaffected by the flag handling.
Not an unprivileged surface: the crypto kfuncs need CAP_BPF, and
bpf_crypto_encrypt() additionally CAP_NET_ADMIN.
crypto/lskcipher.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/crypto/lskcipher.c b/crypto/lskcipher.c
index a8b07594005db..6c30436a11354 100644
--- a/crypto/lskcipher.c
+++ b/crypto/lskcipher.c
@@ -75,6 +75,7 @@ static int crypto_lskcipher_crypt_unaligned(
unsigned ivsize = crypto_lskcipher_ivsize(tfm);
unsigned bs = crypto_lskcipher_blocksize(tfm);
unsigned cs = crypto_lskcipher_chunksize(tfm);
+ u32 flags = 0;
int err;
u8 *tiv;
u8 *p;
@@ -98,13 +99,16 @@ static int crypto_lskcipher_crypt_unaligned(
if (chunk > cs)
chunk &= ~(cs - 1);
+ if (chunk == len)
+ flags |= CRYPTO_LSKCIPHER_FLAG_FINAL;
memcpy(p, src, chunk);
- err = crypt(tfm, p, p, chunk, tiv, CRYPTO_LSKCIPHER_FLAG_FINAL);
+ err = crypt(tfm, p, p, chunk, tiv, flags);
if (err)
goto out;
memcpy(dst, p, chunk);
+ flags |= CRYPTO_LSKCIPHER_FLAG_CONT;
src += chunk;
dst += chunk;
len -= chunk;
base-commit: cf72cbb39da84b6f02f90c07f33b102fc10b16f0
--
2.53.0
next reply other threads:[~2026-08-29 19:43 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-29 19:43 Karl Mehltretter [this message]
2026-09-11 5:31 ` [PATCH] crypto: lskcipher - preserve state across unaligned chunks Herbert Xu
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=20260829194314.42685-1-kmehltretter@gmail.com \
--to=kmehltretter@gmail.com \
--cc=bpf@vger.kernel.org \
--cc=davem@davemloft.net \
--cc=herbert@gondor.apana.org.au \
--cc=linux-crypto@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
/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