From ba871255f796d1f599ba710f03e1803d1b374a75 Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Sat, 29 Aug 2026 21:43:14 +0200 Subject: [PATCH] crypto: lskcipher - preserve state across unaligned chunks 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 Signed-off-by: Herbert Xu --- crypto/lskcipher.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crypto/lskcipher.c b/crypto/lskcipher.c index a8b07594005d..6c30436a1135 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;