Re: [PATCH 2/2] crypto: caam - Use bounce buffer for unaligned RSA destination buffers

From: Kepplinger-Novakovic Martin

Date: Tue Sep 29 2026 - 08:32:28 EST


Am Freitag, dem 21.08.2026 um 14:55 +1000 schrieb Changwei Zou:
> The rsa-caam driver directly DMA-maps the destination buffer supplied by
> the caller via req->dst without checking whether it meets the cacheline
> alignment requirements of DMA-incoherent hardware such as i.MX8.
>
> On CPUs with non-coherent DMA caches, if the destination buffer shares a
> cacheline with other data (i.e. it is not cacheline-aligned), cache
> writeback/invalidation during DMA can corrupt adjacent memory or cause
> stale data to be read back. This manifests as intermittent -EKEYREJECTED
> errors when loading signed kernel modules.
>
> When any segment of req->dst is not cacheline-aligned in either its
> start offset or length, allocate a single contiguous aligned bounce
> buffer covering the full dst_len rounded up to a cacheline multiple,
> redirect the operation to it, and copy the result back to the original
> destination once the hardware has completed successfully.
>
> Introduce a helper sg_is_dma_aligned() that checks both sg->offset and
> sg->length for cacheline alignment. Checking sg->offset suffices for
> the start address since physical pages are always page-aligned and
> PAGE_SIZE is a multiple of dma_get_cache_alignment(). sg->length is
> also checked to ensure the buffer end does not share a cacheline with
> adjacent memory.
>
> The intermittent error 'Key was rejected by service' on i.MX8 with CAAM
> can be triggered when loading signed kernel modules:
>
>     for i in $(seq 1 100); do
>         sudo modprobe xfs 2>&1 && echo "SUCCESS on attempt $i" \
>         && sudo rmmod xfs || echo "FAILED on attempt $i"
>     done
>
> Signed-off-by: Changwei Zou <changwei.zou@xxxxxxxxxxxxx>
> Assisted-by: OpenCode:claude-sonnet-4.6
> ---
>  drivers/crypto/caam/caampkc.c | 77 ++++++++++++++++++++++++++++++++++-
>  drivers/crypto/caam/caampkc.h |  6 +++
>  2 files changed, 82 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/crypto/caam/caampkc.c b/drivers/crypto/caam/caampkc.c
> index 840271840cce..11c6b07f5dad 100644
> --- a/drivers/crypto/caam/caampkc.c
> +++ b/drivers/crypto/caam/caampkc.c
> @@ -59,6 +59,40 @@ static void rsa_io_unmap(struct device *dev, struct rsa_edesc *edesc,
>   DMA_TO_DEVICE);
>  }
>  
> +static int do_rsa_bounce_buf(struct akcipher_request *req, int req_err)
> +{
> + struct caam_rsa_req_ctx *req_ctx = akcipher_request_ctx(req);
> + int nents, err = 0;
> +
> + if (!req_ctx->bounce_buf)
> + return 0;
> +
> + /* Only copy back to the original destination on success */
> + if (!req_err) {
> + nents = sg_nents_for_len(req_ctx->orig_dst, req->dst_len);
> + if (nents < 0)
> + err = nents;
> + else if (sg_copy_from_buffer(req_ctx->orig_dst, nents,
> +      req_ctx->bounce_buf,
> +      req->dst_len) != req->dst_len)
> + err = -EFAULT;
> + }
> +
> + kfree(req_ctx->bounce_buf);
> + req_ctx->bounce_buf = NULL;
> + req->dst = req_ctx->orig_dst;
> +
> + return err;
> +}
> +
> +static inline void rsa_bounce_buf_done(struct akcipher_request *req, int *err)
> +{
> + int cperr = do_rsa_bounce_buf(req, *err);
> +
> + if (!*err)
> + *err = cperr;
> +}
> +
>  static void rsa_pub_unmap(struct device *dev, struct rsa_edesc *edesc,
>     struct akcipher_request *req)
>  {
> @@ -138,6 +172,7 @@ static void rsa_pub_done(struct device *dev, u32 *desc, u32 err, void *context)
>   rsa_pub_unmap(dev, edesc, req);
>   rsa_io_unmap(dev, edesc, req);
>   kfree(edesc);
> + rsa_bounce_buf_done(req, &ecode);
>  
>   /*
>   * If no backlog flag, the completion of the request is done
> @@ -181,6 +216,7 @@ static void rsa_priv_f_done(struct device *dev, u32 *desc, u32 err,
>  
>   rsa_io_unmap(dev, edesc, req);
>   kfree(edesc);
> + rsa_bounce_buf_done(req, &ecode);
>  
>   /*
>   * If no backlog flag, the completion of the request is done
> @@ -246,6 +282,12 @@ static int caam_rsa_count_leading_zeros(struct scatterlist *sgl,
>   return tbytes - nbytes;
>  }
>  
> +static inline bool sg_is_dma_aligned(struct scatterlist *sg)
> +{
> + return IS_ALIGNED(sg->offset, dma_get_cache_alignment()) &&
> +        IS_ALIGNED(sg->length, dma_get_cache_alignment());
> +}
> +
>  static struct rsa_edesc *rsa_edesc_alloc(struct akcipher_request *req,
>   size_t desclen)
>  {
> @@ -291,11 +333,34 @@ static struct rsa_edesc *rsa_edesc_alloc(struct akcipher_request *req,
>        req_ctx->fixup_src_len);
>   dst_nents = sg_nents_for_len(req->dst, req->dst_len);
>  
> + req_ctx->bounce_buf = NULL;
> + req_ctx->orig_dst = req->dst;
> + if (req->dst_len > 0) {
> + struct scatterlist *sg;
> + int i;
> +
> + for_each_sg(req->dst, sg, dst_nents, i) {
> + if (!sg_is_dma_aligned(sg)) {
> + req_ctx->bounce_buf =
> + kzalloc(ALIGN(req->dst_len,
> +       dma_get_cache_alignment()),
> + flags);
> + if (!req_ctx->bounce_buf)
> + return ERR_PTR(-ENOMEM);
> + sg_init_one(&req_ctx->dst, req_ctx->bounce_buf,
> +     req->dst_len);
> + req->dst = &req_ctx->dst;
> + dst_nents = 1;
> + break;
> + }
> + }
> + }
> +
>   mapped_src_nents = dma_map_sg(dev, req_ctx->fixup_src, src_nents,
>         DMA_TO_DEVICE);
>   if (unlikely(!mapped_src_nents)) {
>   dev_err(dev, "unable to map source\n");
> - return ERR_PTR(-ENOMEM);
> + goto bounce_fail;
>   }
>   mapped_dst_nents = dma_map_sg(dev, req->dst, dst_nents,
>         DMA_FROM_DEVICE);
> @@ -368,6 +433,10 @@ static struct rsa_edesc *rsa_edesc_alloc(struct akcipher_request *req,
>   dma_unmap_sg(dev, req->dst, dst_nents, DMA_FROM_DEVICE);
>  src_fail:
>   dma_unmap_sg(dev, req_ctx->fixup_src, src_nents, DMA_TO_DEVICE);
> +bounce_fail:
> + kfree(req_ctx->bounce_buf);
> + req_ctx->bounce_buf = NULL;
> + req->dst = req_ctx->orig_dst;
>   return ERR_PTR(-ENOMEM);
>  }
>  
> @@ -409,6 +478,7 @@ static int akcipher_do_one_req(struct crypto_engine *engine, void *areq)
>   }
>   rsa_io_unmap(jrdev, req_ctx->edesc, req);
>   kfree(req_ctx->edesc);
> + rsa_bounce_buf_done(req, &ret);
>   } else {
>   ret = 0;
>   }
> @@ -723,6 +793,7 @@ static int akcipher_enqueue_req(struct device *jrdev,
>   }
>   rsa_io_unmap(jrdev, edesc, req);
>   kfree(edesc);
> + rsa_bounce_buf_done(req, &ret);
>   }
>  
>   return ret;
> @@ -764,6 +835,7 @@ static int caam_rsa_enc(struct akcipher_request *req)
>  init_fail:
>   rsa_io_unmap(jrdev, edesc, req);
>   kfree(edesc);
> + rsa_bounce_buf_done(req, &ret);
>   return ret;
>  }
>  
> @@ -793,6 +865,7 @@ static int caam_rsa_dec_priv_f1(struct akcipher_request *req)
>  init_fail:
>   rsa_io_unmap(jrdev, edesc, req);
>   kfree(edesc);
> + rsa_bounce_buf_done(req, &ret);
>   return ret;
>  }
>  
> @@ -822,6 +895,7 @@ static int caam_rsa_dec_priv_f2(struct akcipher_request *req)
>  init_fail:
>   rsa_io_unmap(jrdev, edesc, req);
>   kfree(edesc);
> + rsa_bounce_buf_done(req, &ret);
>   return ret;
>  }
>  
> @@ -851,6 +925,7 @@ static int caam_rsa_dec_priv_f3(struct akcipher_request *req)
>  init_fail:
>   rsa_io_unmap(jrdev, edesc, req);
>   kfree(edesc);
> + rsa_bounce_buf_done(req, &ret);
>   return ret;
>  }
>  
> diff --git a/drivers/crypto/caam/caampkc.h b/drivers/crypto/caam/caampkc.h
> index 96d03704c9be..efad91d6058f 100644
> --- a/drivers/crypto/caam/caampkc.h
> +++ b/drivers/crypto/caam/caampkc.h
> @@ -103,6 +103,9 @@ struct caam_rsa_ctx {
>   * @src           : input scatterlist (stripped of leading zeros)
>   * @fixup_src     : input scatterlist (that might be stripped of leading zeros)
>   * @fixup_src_len : length of the fixup_src input scatterlist
> + * @dst           : destination scatterlist backed by bounce buffer (if needed)
> + * @bounce_buf    : DMA-aligned bounce buffer for destination (or NULL)
> + * @orig_dst      : original destination scatterlist (before bounce substitution)
>   * @edesc         : s/w-extended rsa descriptor
>   * @akcipher_op_done : callback used when operation is done
>   */
> @@ -110,6 +113,9 @@ struct caam_rsa_req_ctx {
>   struct scatterlist src[2];
>   struct scatterlist *fixup_src;
>   unsigned int fixup_src_len;
> + struct scatterlist dst;
> + u8 *bounce_buf;
> + struct scatterlist *orig_dst;
>   struct rsa_edesc *edesc;
>   void (*akcipher_op_done)(struct device *jrdev, u32 *desc, u32 err,
>   void *context);


hi Changwei,

I now test booting a signed squashfs rootfs using DM_VERITY on imx8mp and basically get similar issues like I did
during my tests on imx6ul earlier: https://lore.kernel.org/linux-crypto/6029acc0f0ddfe25e2537c2866d54fd7f54bc182.camel@xxxxxxxxxxxxx/T/#u

Your 2 patches applied fix this for me.

It's new to me, but quite simple if you know secureboot better than I do:
I create a keypair. I create a hashtree and append it to the squashfs. And after adding cert.pem to
CONFIG_SYSTEM_TRUSTED_KEYS it should verify and mount successfully.

the image is ok, and with crypto-debug enabled, I saw errors like this:

PKCS7: Sig 1: Issuing X.509 cert not found (#0da1e6a5e8314eb32932bc88de47509476e4ec85 ... "Ginzinger GESB rootfs signing key")
device-mapper: table: 254:0: verity: Root hash verification failed (-ENOKEY)

But also the regdb cert failed to load during boot:

[ 0.283510] Loading compiled-in X.509 certificates
[ 0.288607] Problem loading in-kernel X.509 certificate (-22)

which works now as well.


Tested-by: Martin Kepplinger-Novakovic <martin.kepplinger-novakovic@xxxxxxxxxxxxx>

Do you think this can be merged or should I disabled CONFIG_CRYPTO_DEV_FSL_CAAM_PKC_API for now?

thank you!

martin