[PATCH v2 23/30] crypto: sa2ul - propagate DMA transfer errors

From: Manorit Chawdhry

Date: Wed Nov 04 2026 - 00:51:00 EST


DMA completion callbacks discard the transfer result and process output
metadata even when a transfer fails. This can report failed cipher or
hash requests as successful, or an AEAD transfer failure as a bad tag.

Use result-aware callbacks and return -EIO for failed or incomplete
transfers. Skip IV, digest and tag processing on error while preserving
DMA cleanup and completing each request once.

Signed-off-by: Manorit Chawdhry <m-chawdhry@xxxxxx>
---
drivers/crypto/sa2ul.c | 31 +++++++++++++++++++++----------
1 file changed, 21 insertions(+), 10 deletions(-)

diff --git a/drivers/crypto/sa2ul.c b/drivers/crypto/sa2ul.c
index b33fac66c93f..18a69c890ee2 100644
--- a/drivers/crypto/sa2ul.c
+++ b/drivers/crypto/sa2ul.c
@@ -199,7 +199,7 @@ struct sa_rx_data {
* @enc: true if this is an encode request
* @src: source data
* @dst: destination data
- * @callback: DMA callback for the request
+ * @callback: DMA callback receiving the transfer result
* @mdata_size: metadata size passed to DMA
*/
struct sa_req {
@@ -216,7 +216,7 @@ struct sa_req {
bool enc;
struct scatterlist *src;
struct scatterlist *dst;
- dma_async_tx_callback callback;
+ dma_async_tx_callback_result callback;
u16 mdata_size;
};

@@ -951,19 +951,21 @@ static void sa_free_sa_rx_data(struct sa_rx_data *rxd)
kfree(rxd);
}

-static void sa_aes_dma_in_callback(void *data)
+static void sa_aes_dma_in_callback(void *data,
+ const struct dmaengine_result *dma_result)
{
struct sa_rx_data *rxd = data;
struct skcipher_request *req;
u8 *result;
__be32 *mdptr;
size_t ml, pl;
+ int err = dma_result->result == DMA_TRANS_NOERROR && !dma_result->residue ? 0 : -EIO;
int i;

sa_sync_from_device(rxd);
req = container_of(rxd->req, struct skcipher_request, base);

- if (req->iv) {
+ if (!err && req->iv) {
mdptr = (__be32 *)dmaengine_desc_get_metadata_ptr(rxd->tx_in, &pl,
&ml);
result = req->iv;
@@ -975,7 +977,7 @@ static void sa_aes_dma_in_callback(void *data)

sa_free_sa_rx_data(rxd);

- skcipher_request_complete(req, 0);
+ skcipher_request_complete(req, err);
}

static void
@@ -1179,7 +1181,7 @@ static int sa_run(struct sa_req *req)
rxd->enc = req->enc;
rxd->iv_idx = req->ctx->iv_idx;
rxd->enc_iv_size = cmdl_upd_info.enc_iv.size;
- rxd->tx_in->callback = req->callback;
+ rxd->tx_in->callback_result = req->callback;
rxd->tx_in->callback_param = rxd;

tx_out = dmaengine_prep_slave_sg(pdata->dma_tx, src,
@@ -1274,7 +1276,8 @@ static int sa_decrypt(struct skcipher_request *req)
return sa_cipher_run(req, req->iv, 0);
}

-static void sa_sha_dma_in_callback(void *data)
+static void sa_sha_dma_in_callback(void *data,
+ const struct dmaengine_result *dma_result)
{
struct sa_rx_data *rxd = data;
struct ahash_request *req;
@@ -1284,9 +1287,12 @@ static void sa_sha_dma_in_callback(void *data)
size_t ml, pl;
u32 *result;
__be32 *mdptr;
+ int err = dma_result->result == DMA_TRANS_NOERROR && !dma_result->residue ? 0 : -EIO;

sa_sync_from_device(rxd);
req = container_of(rxd->req, struct ahash_request, base);
+ if (err)
+ goto out;
tfm = crypto_ahash_reqtfm(req);
authsize = crypto_ahash_digestsize(tfm);

@@ -1296,9 +1302,10 @@ static void sa_sha_dma_in_callback(void *data)
for (i = 0; i < (authsize / 4); i++)
result[i] = be32_to_cpu(mdptr[i + 4]);

+out:
sa_free_sa_rx_data(rxd);

- ahash_request_complete(req, 0);
+ ahash_request_complete(req, err);
}

static int zero_message_process(struct ahash_request *req)
@@ -1584,7 +1591,8 @@ static void sa_sha_cra_exit(struct crypto_tfm *tfm)
sa_free_ctx_info(&ctx->enc, data);
}

-static void sa_aead_dma_in_callback(void *data)
+static void sa_aead_dma_in_callback(void *data,
+ const struct dmaengine_result *dma_result)
{
struct sa_rx_data *rxd = data;
struct aead_request *req;
@@ -1594,11 +1602,13 @@ static void sa_aead_dma_in_callback(void *data)
u8 auth_tag[SA_MAX_AUTH_TAG_SZ];
size_t pl, ml;
int i;
- int err = 0;
+ int err = dma_result->result == DMA_TRANS_NOERROR && !dma_result->residue ? 0 : -EIO;
u32 *mdptr;

sa_sync_from_device(rxd);
req = container_of(rxd->req, struct aead_request, base);
+ if (err)
+ goto out;
tfm = crypto_aead_reqtfm(req);
start = req->assoclen + req->cryptlen;
authsize = crypto_aead_authsize(tfm);
@@ -1618,6 +1628,7 @@ static void sa_aead_dma_in_callback(void *data)
err = crypto_memneq(&mdptr[4], auth_tag, authsize) ? -EBADMSG : 0;
}

+out:
sa_free_sa_rx_data(rxd);

aead_request_complete(req, err);

--
2.43.0