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@vger.kernel.org Fixes: 9363efb4181c ("crypto: qce - Add support for AEAD algorithms") Tested-by: Kuldeep Singh Tested-by: Pankaj Patil Signed-off-by: Bartosz Golaszewski --- 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 f34e4feb3004d9bf63c2c8b2aeb3975d86ac486b..ecc33570804fa435db9ec9fd6fbf38c3c8708e2e 100644 --- a/drivers/crypto/qce/aead.c +++ b/drivers/crypto/qce/aead.c @@ -498,7 +498,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; bool fallback = ctx->need_fallback; rctx->flags = tmpl->alg_flags; @@ -523,6 +524,27 @@ static int qce_aead_crypt(struct aead_request *req, int encrypt) if (IS_CCM(rctx->flags) && !IS_ALIGNED(rctx->cryptlen, AES_BLOCK_SIZE)) 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)) { + 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) + fallback = true; + + msg_sg = scatterwalk_ffwd(__sg, req->dst, req->assoclen); + if (sg_nents_for_len(msg_sg, rctx->cryptlen + + (encrypt ? authsize : 0)) > 1) + fallback = true; + } + /* If fallback is needed, schedule and exit */ if (fallback) { aead_request_set_tfm(&rctx->fallback_req, ctx->fallback); -- 2.47.3