On 9/28/26 15:02, [email protected] wrote:
...>> +   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

It'd be rejected, not truncated, see below.

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?

I can clear it, but unless I missed sth that's what e.g.
nvme_pci_setup_data_prp() already does.

nvme_pci_setup_data_prp() // goto bad_sgl:
        nvme_unmap_data()
                nvme_free_descriptors();


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).

It'll fail on the alignment check in this loop for [4096, 4097)

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()?

Going to fix.

--
Pavel Begunkov

Reply via email to