[PATCH v2 8/8] crypto: qce - Use fallback for CCM with a fragmented payload

From: Bartosz Golaszewski

Date: Mon Jun 15 2026 - 12:00:53 EST


The crypto engine reliably processes CCM only when the message payload
is a single contiguous buffer. The associated data is already linearized
into a bounce buffer before being submitted, but when the payload itself
is split across multiple scatterlist entries the engine stalls waiting
for input and the request fails with a hardware operation error. This
was uncovered by the crypto self-tests, which feed the algorithms
randomly fragmented buffers.

Detect a payload that spans more than one scatterlist entry (in either
the source or the destination, skipping past the associated data) and
route the request to the software fallback.

Cc: stable@xxxxxxxxxxxxxxx
Fixes: 9363efb4181c ("crypto: qce - Add support for AEAD algorithms")
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@xxxxxxxxxxxxxxxx>
---
drivers/crypto/qce/aead.c | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/drivers/crypto/qce/aead.c b/drivers/crypto/qce/aead.c
index 46d3e3eb53b271e2ce755847bbcc83f81b9bda7e..2f4bb4fcd8265a0f7c2f568dfb8915b8c6b8ecee 100644
--- a/drivers/crypto/qce/aead.c
+++ b/drivers/crypto/qce/aead.c
@@ -500,7 +500,8 @@ static int qce_aead_crypt(struct aead_request *req, int encrypt)
struct qce_aead_reqctx *rctx = aead_request_ctx_dma(req);
struct qce_aead_ctx *ctx = crypto_aead_ctx(tfm);
struct qce_alg_template *tmpl = to_aead_tmpl(tfm);
- unsigned int blocksize = crypto_aead_blocksize(tfm);
+ unsigned int blocksize = crypto_aead_blocksize(tfm), authsize;
+ struct scatterlist __sg[2], *msg_sg;

rctx->flags = tmpl->alg_flags;
rctx->flags |= encrypt ? QCE_ENCRYPT : QCE_DECRYPT;
@@ -524,6 +525,27 @@ static int qce_aead_crypt(struct aead_request *req, int encrypt)
if (IS_CCM(rctx->flags) && !IS_ALIGNED(rctx->cryptlen, AES_BLOCK_SIZE))
ctx->need_fallback = true;

+ /*
+ * The CE reliably processes CCM only when the message payload is a
+ * single contiguous buffer. The associated data is linearized into a
+ * bounce buffer before being handed to the engine, but a fragmented
+ * payload makes the engine stall waiting for input, so route those
+ * requests to the fallback.
+ */
+ if (IS_CCM(rctx->flags) && rctx->cryptlen) {
+ authsize = ctx->authsize;
+
+ msg_sg = scatterwalk_ffwd(__sg, req->src, req->assoclen);
+ if (sg_nents_for_len(msg_sg, rctx->cryptlen +
+ (encrypt ? 0 : authsize)) > 1)
+ ctx->need_fallback = true;
+
+ msg_sg = scatterwalk_ffwd(__sg, req->dst, req->assoclen);
+ if (sg_nents_for_len(msg_sg, rctx->cryptlen +
+ (encrypt ? authsize : 0)) > 1)
+ ctx->need_fallback = true;
+ }
+
/* If fallback is needed, schedule and exit */
if (ctx->need_fallback) {
/* Reset need_fallback in case the same ctx is used for another transaction */

--
2.47.3