All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Pavel Begunkov" <asml.silence@gmail.com>
Cc: media-ci@linuxtv.org, dri-devel@lists.freedesktop.org
Subject: Re: [PATCH v7 06/13] nvme-pci: implement dma-buf backed requests
Date: Mon, 28 Sep 2026 14:02:07 +0000	[thread overview]
Message-ID: <20260928140208.0C56B1F000FF@smtp.kernel.org> (raw)
In-Reply-To: <e99dd5221c71cbf9aafb5737468d814cb83b9174.1790602223.git.asml.silence@gmail.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] block: out-of-bounds heap read in nvme_rq_setup_dmabuf_map allows arbitrary DMA
- [High] mm: invalid kfree() on kvmalloc_flex() allocation in nvme_dma_buf_io_map
- [High] mm: heap buffer overflow in nvme_dma_buf_io_map due to truncated nr_entries
- [High] mm: double free of PRP lists on nvme_rq_setup_dmabuf_map error path
--

commit 0b541f35b5346961c2c0d5b3cd4aa8c755c332a2
Author: Pavel Begunkov <asml.silence@gmail.com>

nvme-pci: implement dma-buf backed requests

Enable BIO_DMABUF_MAP backed requests. On registration we map the
dma-buf and store it as a prp list, which is then used to initialise
requests. All attached contexts are stored in a new list dmabuf_ctxs,
and additions/removals are synchronised with dmabuf_lock.

> diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
> --- a/drivers/nvme/host/pci.c
> +++ b/drivers/nvme/host/pci.c

[ ... ]

> +static void nvme_rq_clean_dmabuf_map(struct nvme_dev *dev,
> +		struct request *req)
> +{
> +	struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
> +
> +	nvme_dmabuf_map_sync_for_cpu(dev, req);
> +
> +	if (iod->nr_descriptors)
> +		nvme_free_descriptors(req);
> +}

[ ... ]

> +static blk_status_t nvme_rq_setup_dmabuf_map(struct request *req,
> +		struct nvme_queue *nvmeq)
> +{
> +	struct nvme_iod *iod = blk_mq_rq_to_pdu(req);
> +	struct bio *bio = req->bio;
> +	struct nvme_dmabuf_map *map = to_nvme_dmabuf_map(bio->bi_dmabuf_map);
> +	unsigned bvec_done = bio->bi_iter.bi_offset;
> +	unsigned map_idx = bvec_done / NVME_CTRL_PAGE_SIZE;
> +	unsigned offset = bvec_done & (NVME_CTRL_PAGE_SIZE - 1);
> +	int length = blk_rq_payload_bytes(req) - (NVME_CTRL_PAGE_SIZE - offset);
> +	dma_addr_t *dma_list = map->dma_list;
> +	u64 prp1_dma = dma_list[map_idx++] + offset;
> +	u64 dma_addr, prp2_dma;
> +	dma_addr_t prp_dma;
> +	__le64 *prp_list;
> +	unsigned i;
> +
> +	nvme_dmabuf_map_sync_for_device(nvmeq->dev, req);
> +
> +	if (length <= 0) {
> +		prp2_dma = 0;
> +		goto done;
> +	}
> +
> +	if (length <= NVME_CTRL_PAGE_SIZE) {
> +		prp2_dma = dma_list[map_idx];

[Severity: Critical]
Is map_idx checked against map->nr_entries here?

If dmabuf->size is 4608 bytes, nr_entries is truncated to 1, allocating 1
array element. Userspace could submit a 4608-byte IO. The driver calculates
'length' up to 4608 and advances 'map_idx' to 1 for the remaining 512 bytes.
It reads 'dma_list[1]' (which is out-of-bounds) and uses it as 'prp2_dma',
potentially resulting in arbitrary physical DMA (read or write).

> +		goto done;
> +	}

[ ... ]

> +free_prps:
> +	iod->cmd.common.dptr.prp1 = cpu_to_le64(prp1_dma);
> +	iod->cmd.common.dptr.prp2 = cpu_to_le64(prp2_dma);
> +	nvme_free_descriptors(req);
> +	return BLK_STS_RESOURCE;

[Severity: High]
Does this code leave iod->nr_descriptors greater than 0 on the error path?

If dma_pool_alloc() fails in nvme_rq_setup_dmabuf_map(), it jumps to
'free_prps:', which calls nvme_free_descriptors() but leaves
'iod->nr_descriptors' unchanged. The block layer may requeue the failed
request.

If the queue is subsequently torn down, the cancellation path invokes
nvme_unmap_data() -> nvme_rq_clean_dmabuf_map(). Seeing the non-zero
'nr_descriptors', it calls nvme_free_descriptors() again, resulting in a
double free.

> +}

[ ... ]

> +static struct dma_buf_io_map *nvme_dma_buf_io_map(struct dma_buf_io_ctx *ctx)
> +{
> +	unsigned nr_entries = ctx->dmabuf->size / NVME_CTRL_PAGE_SIZE;
> +	struct nvme_dma_buf_io_ctx *nvme_ctx = ctx->dev_priv;
> +	struct dma_buf_attachment *attach = nvme_ctx->attach;
> +	unsigned long tmp, i = 0;
> +	struct nvme_dmabuf_map *map;
> +	struct scatterlist *sg;
> +	struct sg_table *sgt;
> +	int ret;
> +
> +	dma_resv_assert_held(ctx->dmabuf->resv);
> +
> +	map = kvmalloc_flex(*map, dma_list, nr_entries);
> +	if (!map)
> +		return ERR_PTR(-ENOMEM);
> +
> +	sgt = dma_buf_map_attachment(attach, ctx->dir);
> +	if (IS_ERR(sgt)) {
> +		ret = PTR_ERR(sgt);
> +		sgt = NULL;
> +		goto err;
> +	}
> +
> +	for_each_sgtable_dma_sg(sgt, sg, tmp) {
> +		dma_addr_t dma_addr = sg_dma_address(sg);
> +		unsigned long sg_len = sg_dma_len(sg);
> +
> +		if ((sg_len % NVME_CTRL_PAGE_SIZE) ||
> +		    (dma_addr % NVME_CTRL_PAGE_SIZE)) {
> +			ret = -EINVAL;
> +			goto err;
> +		}
> +		while (sg_len) {
> +			map->dma_list[i++] = dma_addr;

[Severity: High]
Does this loop overflow dma_list[]?

'nr_entries' is computed via integer division of dmabuf->size. If the dmabuf
size is not page-aligned (e.g., 4097 bytes), nr_entries truncates down (to 1).
However, exporters often pad sg_table allocations to full pages (e.g., 8192
bytes).

The loop unconditionally writes all sg_len chunks to 'dma_list', exceeding the
allocated 'nr_entries' capacity.

> +			dma_addr += NVME_CTRL_PAGE_SIZE;
> +			sg_len -= NVME_CTRL_PAGE_SIZE;
> +		}
> +	}
> +
> +	ret = dma_buf_io_init_map(ctx, &map->base, sgt);
> +	if (ret)
> +		goto err;
> +	map->nr_entries = nr_entries;
> +	map->sgt = sgt;
> +	return &map->base;
> +err:
> +	if (sgt)
> +		dma_buf_unmap_attachment(attach, sgt, ctx->dir);
> +	kfree(map);

[Severity: High]
Is it safe to free a kvmalloc_flex() allocation with kfree()?

If the dmabuf setup fails, the code jumps to the 'err' label and executes
kfree(map). Calling kfree() on memory allocated via vmalloc() causes undefined
behavior, typically a kernel panic.

> +	return ERR_PTR(ret);
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1790602223.git.asml.silence@gmail.com?part=6

  reply	other threads:[~2026-09-28 14:02 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-28 13:32 [PATCH v7 00/13] Add dmabuf read/write via io_uring Pavel Begunkov
2026-09-28 13:32 ` [PATCH v7 01/13] dma-buf: introduce initial file I/O infrastructure Pavel Begunkov
2026-09-28 13:46   ` sashiko-bot
2026-09-29 16:12   ` Christophe JAILLET
2026-09-28 13:32 ` [PATCH v7 02/13] iov_iter: add iterator type for dmabuf maps Pavel Begunkov
2026-09-28 13:49   ` sashiko-bot
2026-10-05  9:10   ` Christoph Hellwig
2026-10-05 11:44     ` Pavel Begunkov
2026-09-28 13:32 ` [PATCH v7 03/13] block: always adjust bi_offset on bio_advance_iter Pavel Begunkov
2026-09-28 13:32 ` [PATCH v7 04/13] block: introduce dma map backed bio type Pavel Begunkov
2026-09-28 13:32 ` [PATCH v7 05/13] block: add dma-buf support for raw bdev Pavel Begunkov
2026-09-28 13:46   ` sashiko-bot
2026-10-05  9:11   ` Christoph Hellwig
2026-10-05 11:43     ` Pavel Begunkov
2026-09-28 13:32 ` [PATCH v7 06/13] nvme-pci: implement dma-buf backed requests Pavel Begunkov
2026-09-28 14:02   ` sashiko-bot [this message]
2026-09-28 14:41     ` Pavel Begunkov
2026-09-29 15:45   ` Christophe JAILLET
2026-09-30 10:09     ` Pavel Begunkov
2026-10-05  9:12     ` Christoph Hellwig
2026-10-05  9:15   ` Christoph Hellwig
2026-10-05 11:45     ` Pavel Begunkov
2026-09-28 13:32 ` [PATCH v7 07/13] nvme-pci: rename nvme_pci_sgl_set_data to nvme_pci_dma_iter_set_sgl Pavel Begunkov
2026-09-28 13:32 ` [PATCH v7 08/13] nvme-pci: add SGL support for the dmabuf path Pavel Begunkov
2026-09-28 13:32 ` [PATCH v7 09/13] io_uring/rsrc: introduce buf registration structure Pavel Begunkov
2026-09-28 13:32 ` [PATCH v7 10/13] io_uring/rsrc: extend buffer update Pavel Begunkov
2026-09-28 13:32 ` [PATCH v7 11/13] io_uring/rsrc: add uncloneable regbuf flag Pavel Begunkov
2026-09-28 13:32 ` [PATCH v7 12/13] io_uring/rsrc: add regbuf import flags Pavel Begunkov
2026-09-28 13:32 ` [PATCH v7 13/13] io_uring/rsrc: add dmabuf backed registered buffers Pavel Begunkov
2026-09-28 14:03   ` sashiko-bot

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260928140208.0C56B1F000FF@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=asml.silence@gmail.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=media-ci@linuxtv.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.