Re: [PATCH net-next v8 4/7] net: bcmgenet: add XDP_TX support

Nicolai Buchwitz <[email protected]>
Newsgroups dev.linux.lists.sashiko,org.kernel.vger.bpf
Message-ID <[email protected]>
On 29.4.2026 23:17, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 4 potential 
> issue(s) to consider:
> - [High] Out-of-bounds array access in `bcmgenet_tx_reclaim` during 
> interface teardown
> - [High] Incorrect DMA address calculation for local XDP_TX causes 
> information leak and packet corruption
> - [High] Prepending `status_64` block overwrites XDP metadata and 
> causes memory corruption
> - [Medium] XDP TX ring size is smaller than standard NAPI budget
> --
> 
> commit 9add891fa4e7cf62ec26683116338b5877c21662
> Author: Nicolai Buchwitz <[email protected]>
> 
> net: bcmgenet: add XDP_TX support
> 
> Implement XDP_TX using ring 16 (DESC_INDEX), the hardware default
> descriptor ring, dedicated to XDP TX for isolation from SKB TX queues.
> 
>> diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c 
>> b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
>> index 1b60571446e1..3c3b0c44ea8a 100644
>> --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
>> +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
>> @@ -48,8 +48,10 @@
>> 
>>  #define GENET_Q0_RX_BD_CNT	\
>>  	(TOTAL_DESC - priv->hw_params->rx_queues * 
>> priv->hw_params->rx_bds_per_q)
>> +#define GENET_Q16_TX_BD_CNT	32
> 
> Is it possible to drop packets here during bursts since this TX ring is 
> smaller
> than the standard NAPI budget?

Yes, by design. If the ring fills, frames are dropped, which is standard
XDP backpressure. Same approach as bnxt, mvneta, and other drivers.

> 
> The standard RX NAPI budget is 64, and TX completions are reclaimed 
> only once
> before the RX loop. If the RX loop processes a full budget of 64 
> packets and
> they all result in an XDP_TX action, the first 32 will fill the TX 
> ring.
> 
> The remaining 32 packets would fail in bcmgenet_xdp_xmit_frame() 
> because
> ring->free_bds < 1, and be dropped.
> 
> [ ... ]
>> @@ -2043,6 +2069,9 @@ static void bcmgenet_tx_reclaim_all(struct 
>> net_device *dev)
>>  	do {
>>  		bcmgenet_tx_reclaim(dev, &priv->tx_rings[i++], true);
>>  	} while (i <= priv->hw_params->tx_queues && 
>> netif_is_multiqueue(dev));
>> +
>> +	/* Also reclaim XDP TX ring */
>> +	bcmgenet_tx_reclaim(dev, &priv->xdp_tx_ring, true);
>>  }
> 
> Can this cause an out-of-bounds array access during interface teardown?
> 
> When bcmgenet_tx_reclaim_all() calls bcmgenet_tx_reclaim() with all == 
> true,
> it unconditionally calls netdev_tx_reset_queue(netdev_get_tx_queue(dev, 
> ring->index)).

bcmgenet_tx_reclaim() does not call netdev_tx_reset_queue() or
netdev_get_tx_queue(). The only netdev_get_tx_queue() in the reclaim
path (in __bcmgenet_tx_reclaim, for BQL) is guarded by
"if (ring->index != DESC_INDEX)". The netdev_tx_reset_queue() loop in
bcmgenet_fini_dma() iterates indices 0..tx_queues only.

> 
> The XDP TX ring has an index of 16 (DESC_INDEX), but the network device 
> is
> allocated with only GENET_MAX_MQ_CNT + 1 TX queues (typically 5). 
> Accessing
> index 16 of dev->_tx is out-of-bounds, and netdev_tx_reset_queue() will 
> write
> to this invalid memory.
> 
> [ ... ]
>> @@ -2299,11 +2328,96 @@ static struct sk_buff 
>> *bcmgenet_xdp_build_skb(struct bcmgenet_rx_ring *ring,
>>  	return skb;
>>  }
>> 
>> +static bool bcmgenet_xdp_xmit_frame(struct bcmgenet_priv *priv,
>> +				     struct xdp_frame *xdpf, bool dma_map)
>> +{
> [ ... ]
>> +	if (dma_map) {
> [ ... ]
>> +	} else {
>> +		struct page *page = virt_to_page(xdpf->data);
>> +
>> +		/* For local XDP_TX the caller already prepended the TSB
>> +		 * into xdpf->data/len, so dma_len == xdpf->len.
>> +		 */
>> +		dma_len = xdpf->len;
>> +		mapping = page_pool_get_dma_addr(page) +
>> +			  sizeof(*xdpf) + xdpf->headroom;
> 
> Does this calculation result in an incorrect DMA address?
> 
> The calculation implicitly assumes that xdpf resides exactly at the 
> beginning
> of the page. However, the driver configures the RX page pool with
> offset = XDP_PACKET_HEADROOM (typically 256 bytes).
> 
> By omitting the distance from the start of the page to xdpf, the 
> calculated
> DMA address points XDP_PACKET_HEADROOM bytes before the actual packet 
> data,
> which could lead to transmitting uninitialized memory.

Correct, xdpf does reside at the start of the page. 
xdp_convert_buff_to_frame()
stores the xdp_frame at xdp->data_hard_start, which equals 
page_address(page)
for our page_pool layout. page_pool_get_dma_addr() returns the DMA 
address
of the page start (not offset by .offset). sizeof(*xdpf) + 
xdpf->headroom
equals xdpf->data - data_hard_start, pointing to the prepended TSB.

> 
>> +		dma_sync_single_for_device(kdev, mapping, dma_len,
>> +					   DMA_BIDIRECTIONAL);
>> +	}
> [ ... ]
>> +	case XDP_TX:
>> +		/* Prepend a zeroed TSB (Transmit Status Block).  The GENET
>> +		 * MAC has TBUF_64B_EN set globally, so hardware expects every
>> +		 * TX buffer to begin with a 64-byte struct status_64.  Back
>> +		 * up xdp->data into the RSB area (which is no longer needed
>> +		 * after the BPF program ran) and zero it.
>> +		 */
>> +		if (xdp->data - xdp->data_hard_start <
>> +		    sizeof(struct status_64) + sizeof(struct xdp_frame)) {
>> +			page_pool_put_full_page(ring->page_pool, rx_page,
>> +						true);
>> +			return XDP_DROP;
>> +		}
>> +		xdp->data -= sizeof(struct status_64);
>> +		xdp->data_meta -= sizeof(struct status_64);
>> +		memset(xdp->data, 0, sizeof(struct status_64));
> 
> Could this overwrite XDP metadata and cause memory corruption?

xdp_convert_buff_to_frame() validates (headroom - metasize) >=
sizeof(struct xdp_frame) and returns NULL otherwise; we drop the frame
in that case. The memset writes to [xdp->data - 64, xdp->data] before
the data shift, which was previously the RSB area, not the metadata
area. Both data and data_meta are shifted equally, preserving the
metasize gap.

> 
> The space check does not account for metasize (xdp->data - 
> xdp->data_meta).
> If the BPF program appended metadata, the memset() here completely 
> overwrites
> it.
> 
> Furthermore, if metasize is > 0, the shifted xdp->data_meta pointer 
> could
> underflow and point exactly to or before xdp->data_hard_start. When
> xdp_convert_buff_to_frame() is subsequently called, it writes the 
> xdp_frame
> structure at data_hard_start, which would overlap with the shifted 
> data_meta.

Thanks
Nicolai
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.