The following patch addresses the issue of unchecked calls to dma_map_sg() in safexcel_send_req() as these macros may return 0 in case of unsuccessful mapping. This outcome in turn requires unmapping of previously mapped buffers.
The fix has already been backported to the following stable branches: v6.6: https://lore.kernel.org/all/20240122235813.608624333@linuxfoundation.org/ v6.1: https://lore.kernel.org/all/20240122235752.938797245@linuxfoundation.org/
The issue in question can be fixed in 5.10 and 5.15 stable branches by backporting the following 2 upstream commits. Both can be cleanly applied to kernel versions mentioned above.
[PATCH 5.10/5.15 1/2] crypto: inside_secure - Avoid dma map if size is zero [PATCH 5.10/5.15 2/2] crypto: safexcel - Add error handling for dma_map_sg() calls
First patch is a prerequisite to main fix and removes warnings in case of a call to dma_map_sg() with size 0 and allows for clean application of the main fix.
Second (and main) patch adds proper handling of dma_map_sg() erroneous behaviour.
From: Peter Harliman Liem pliem@maxlinear.com
commit 49186a7d9e46ff132a0ed9b721ad6b6a58dba6c1 upstream.
From commit d03c54419274 ("dma-mapping: disallow .map_sg operations from returning zero on error"), dma_map_sg() produces warning if size is 0. This results in visible warnings if crypto length is zero. To avoid that, we avoid calling dma_map_sg if size is zero.
Signed-off-by: Peter Harliman Liem pliem@maxlinear.com Signed-off-by: Herbert Xu herbert@gondor.apana.org.au Signed-off-by: Nikita Zhandarovich n.zhandarovich@fintech.ru --- .../crypto/inside-secure/safexcel_cipher.c | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-)
diff --git a/drivers/crypto/inside-secure/safexcel_cipher.c b/drivers/crypto/inside-secure/safexcel_cipher.c index 9bcfb79a030f..b1f9deb59da8 100644 --- a/drivers/crypto/inside-secure/safexcel_cipher.c +++ b/drivers/crypto/inside-secure/safexcel_cipher.c @@ -641,10 +641,16 @@ static int safexcel_handle_req_result(struct safexcel_crypto_priv *priv, int rin safexcel_complete(priv, ring);
if (src == dst) { - dma_unmap_sg(priv->dev, src, sreq->nr_src, DMA_BIDIRECTIONAL); + if (sreq->nr_src > 0) + dma_unmap_sg(priv->dev, src, sreq->nr_src, + DMA_BIDIRECTIONAL); } else { - dma_unmap_sg(priv->dev, src, sreq->nr_src, DMA_TO_DEVICE); - dma_unmap_sg(priv->dev, dst, sreq->nr_dst, DMA_FROM_DEVICE); + if (sreq->nr_src > 0) + dma_unmap_sg(priv->dev, src, sreq->nr_src, + DMA_TO_DEVICE); + if (sreq->nr_dst > 0) + dma_unmap_sg(priv->dev, dst, sreq->nr_dst, + DMA_FROM_DEVICE); }
/* @@ -736,23 +742,29 @@ static int safexcel_send_req(struct crypto_async_request *base, int ring, max(totlen_src, totlen_dst)); return -EINVAL; } - dma_map_sg(priv->dev, src, sreq->nr_src, DMA_BIDIRECTIONAL); + if (sreq->nr_src > 0) + dma_map_sg(priv->dev, src, sreq->nr_src, + DMA_BIDIRECTIONAL); } else { if (unlikely(totlen_src && (sreq->nr_src <= 0))) { dev_err(priv->dev, "Source buffer not large enough (need %d bytes)!", totlen_src); return -EINVAL; } - dma_map_sg(priv->dev, src, sreq->nr_src, DMA_TO_DEVICE); + + if (sreq->nr_src > 0) + dma_map_sg(priv->dev, src, sreq->nr_src, DMA_TO_DEVICE);
if (unlikely(totlen_dst && (sreq->nr_dst <= 0))) { dev_err(priv->dev, "Dest buffer not large enough (need %d bytes)!", totlen_dst); - dma_unmap_sg(priv->dev, src, sreq->nr_src, - DMA_TO_DEVICE); - return -EINVAL; + ret = -EINVAL; + goto unmap; } - dma_map_sg(priv->dev, dst, sreq->nr_dst, DMA_FROM_DEVICE); + + if (sreq->nr_dst > 0) + dma_map_sg(priv->dev, dst, sreq->nr_dst, + DMA_FROM_DEVICE); }
memcpy(ctx->base.ctxr->data, ctx->key, ctx->key_len); @@ -882,12 +894,18 @@ static int safexcel_send_req(struct crypto_async_request *base, int ring, cdesc_rollback: for (i = 0; i < n_cdesc; i++) safexcel_ring_rollback_wptr(priv, &priv->ring[ring].cdr); - +unmap: if (src == dst) { - dma_unmap_sg(priv->dev, src, sreq->nr_src, DMA_BIDIRECTIONAL); + if (sreq->nr_src > 0) + dma_unmap_sg(priv->dev, src, sreq->nr_src, + DMA_BIDIRECTIONAL); } else { - dma_unmap_sg(priv->dev, src, sreq->nr_src, DMA_TO_DEVICE); - dma_unmap_sg(priv->dev, dst, sreq->nr_dst, DMA_FROM_DEVICE); + if (sreq->nr_src > 0) + dma_unmap_sg(priv->dev, src, sreq->nr_src, + DMA_TO_DEVICE); + if (sreq->nr_dst > 0) + dma_unmap_sg(priv->dev, dst, sreq->nr_dst, + DMA_FROM_DEVICE); }
return ret;
From: Nikita Zhandarovich n.zhandarovich@fintech.ru
commit 87e02063d07708cac5bfe9fd3a6a242898758ac8 upstream.
Macro dma_map_sg() may return 0 on error. This patch enables checks in case of the macro failure and ensures unmapping of previously mapped buffers with dma_unmap_sg().
Found by Linux Verification Center (linuxtesting.org) with static analysis tool SVACE.
Fixes: 49186a7d9e46 ("crypto: inside_secure - Avoid dma map if size is zero") Signed-off-by: Nikita Zhandarovich n.zhandarovich@fintech.ru Reviewed-by: Antoine Tenart atenart@kernel.org Signed-off-by: Herbert Xu herbert@gondor.apana.org.au Signed-off-by: Nikita Zhandarovich n.zhandarovich@fintech.ru --- .../crypto/inside-secure/safexcel_cipher.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
diff --git a/drivers/crypto/inside-secure/safexcel_cipher.c b/drivers/crypto/inside-secure/safexcel_cipher.c index b1f9deb59da8..5cb673d0fe89 100644 --- a/drivers/crypto/inside-secure/safexcel_cipher.c +++ b/drivers/crypto/inside-secure/safexcel_cipher.c @@ -742,9 +742,9 @@ static int safexcel_send_req(struct crypto_async_request *base, int ring, max(totlen_src, totlen_dst)); return -EINVAL; } - if (sreq->nr_src > 0) - dma_map_sg(priv->dev, src, sreq->nr_src, - DMA_BIDIRECTIONAL); + if (sreq->nr_src > 0 && + !dma_map_sg(priv->dev, src, sreq->nr_src, DMA_BIDIRECTIONAL)) + return -EIO; } else { if (unlikely(totlen_src && (sreq->nr_src <= 0))) { dev_err(priv->dev, "Source buffer not large enough (need %d bytes)!", @@ -752,8 +752,9 @@ static int safexcel_send_req(struct crypto_async_request *base, int ring, return -EINVAL; }
- if (sreq->nr_src > 0) - dma_map_sg(priv->dev, src, sreq->nr_src, DMA_TO_DEVICE); + if (sreq->nr_src > 0 && + !dma_map_sg(priv->dev, src, sreq->nr_src, DMA_TO_DEVICE)) + return -EIO;
if (unlikely(totlen_dst && (sreq->nr_dst <= 0))) { dev_err(priv->dev, "Dest buffer not large enough (need %d bytes)!", @@ -762,9 +763,11 @@ static int safexcel_send_req(struct crypto_async_request *base, int ring, goto unmap; }
- if (sreq->nr_dst > 0) - dma_map_sg(priv->dev, dst, sreq->nr_dst, - DMA_FROM_DEVICE); + if (sreq->nr_dst > 0 && + !dma_map_sg(priv->dev, dst, sreq->nr_dst, DMA_FROM_DEVICE)) { + ret = -EIO; + goto unmap; + } }
memcpy(ctx->base.ctxr->data, ctx->key, ctx->key_len);
linux-stable-mirror@lists.linaro.org