Re: [PATCH v2] dmaengine: xilinx: Fix wild pointer dereference on empty segment list

From: Frank Li

Date: Fri Nov 13 2026 - 15:25:20 EST


On Tue, Nov 03, 2026 at 04:32:39PM +0800, Li Ximo wrote:
> From: Wang Haoran <haoranwangsec@xxxxxxxxx>
>
> list_first_entry() does not return NULL on an empty list; it returns the
> result of container_of() on the list head, i.e. a wild pointer. In the
> Xilinx DMA driver, the prep functions xilinx_dma_prep_peripheral_dma_vec(),
> xilinx_dma_prep_slave_sg() and xilinx_mcdma_prep_slave_sg() can build zero
> segments while still returning a descriptor, and both they and
> xilinx_dma_get_metadata_ptr() then take list_first_entry() of the empty
> segment list, yielding a wild pointer that is dereferenced for
> desc->async_tx.phys or the metadata app words.
>
> Guard the three prep functions against an empty segment list by going
> through the existing error path (which frees the descriptor and returns
> NULL), and make xilinx_dma_get_metadata_ptr() return ERR_PTR(-ENODATA)
> in that case without touching the length outputs, matching the ERR_PTR()
> convention that dmaengine_desc_get_metadata_ptr() and its callers (e.g.
> axienet) check with IS_ERR().
>
> This issue was found by static analysis.
>
> Reported-by: Wang Haoran <haoranwangsec@xxxxxxxxx>
> Reported-by: Li Ximo <lixicaicai@xxxxxxxxx>
> Signed-off-by: Wang Haoran <haoranwangsec@xxxxxxxxx>
> ---

Reviewed-by: Frank Li <Frank.Li@xxxxxxx>

> v1: https://lore.kernel.org/r/20261023065806.3175584-1-lixicaicai@xxxxxxxxx
> v2: xilinx_dma_get_metadata_ptr() now returns ERR_PTR(-ENODATA) instead
> of NULL and no longer modifies *max_len/*payload_len on failure, per
> Frank Li's review; the prep function guards are unchanged.
>
> drivers/dma/xilinx/xilinx_dma.c | 16 ++++++++++++++--
> 1 file changed, 14 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
> index cffe7c6fa..8b9e3a13e 100644
> --- a/drivers/dma/xilinx/xilinx_dma.c
> +++ b/drivers/dma/xilinx/xilinx_dma.c
> @@ -649,9 +649,12 @@ static void *xilinx_dma_get_metadata_ptr(struct dma_async_tx_descriptor *tx,
> struct xilinx_dma_tx_descriptor *desc = to_dma_tx_descriptor(tx);
> struct xilinx_axidma_tx_segment *seg;
>
> + seg = list_first_entry_or_null(&desc->segments,
> + struct xilinx_axidma_tx_segment, node);
> + if (!seg)
> + return ERR_PTR(-ENODATA);
> +
> *max_len = *payload_len = sizeof(u32) * XILINX_DMA_NUM_APP_WORDS;
> - seg = list_first_entry(&desc->segments,
> - struct xilinx_axidma_tx_segment, node);
> return seg->hw.app;
> }
>
> @@ -2305,6 +2308,9 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_peripheral_dma_vec(
> }
> }
>
> + if (list_empty(&desc->segments))
> + goto error;
> +
> head = list_first_entry(&desc->segments, struct xilinx_axidma_tx_segment, node);
> desc->async_tx.phys = head->phys;
>
> @@ -2406,6 +2412,9 @@ static struct dma_async_tx_descriptor *xilinx_dma_prep_slave_sg(
> }
> }
>
> + if (list_empty(&desc->segments))
> + goto error;
> +
> segment = list_first_entry(&desc->segments,
> struct xilinx_axidma_tx_segment, node);
> desc->async_tx.phys = segment->phys;
> @@ -2613,6 +2622,9 @@ xilinx_mcdma_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
> }
> }
>
> + if (list_empty(&desc->segments))
> + goto error;
> +
> segment = list_first_entry(&desc->segments,
> struct xilinx_aximcdma_tx_segment, node);
> desc->async_tx.phys = segment->phys;
> --
> 2.30.2