Re: [RFC net-next 01/15] xdp: Size zero-copy skb heads by their contents
From: Maciej Fijalkowski
Date: Thu Oct 15 2026 - 14:03:51 EST
On Fri, Oct 02, 2026 at 09:00:02PM +0200, Björn Töpel wrote:
> xdp_build_skb_from_zc() sizes the skb head by the XSK frame size,
> which leaves no room for skb_shared_info. With 2 KiB chunks, a
> 1514 byte frame overwrites 48 bytes of it. Both copies also round
> the length up to LARGEST_ALIGN, past the end of the new buffers.
>
> Size the head from headroom plus data, copy exactly the data, and
> drop frames whose head does not fit in a page.
>
> Discovered by an AI code review agent. Reproduced on fbnic in QEMU
> with AF_XDP zero-copy from the page-pool series: an XDP program that
> adds metadata, grows the frame to its end and passes it gets UMEM
> bytes in the skb's shared info. A new xskxceiver test,
> XDP_PASS_FULL_FRAME, checks this and passes with the fix. The test
> will be posted separately.
>
> Fixes: 560d958c6c68 ("xsk: add generic XSk &xdp_buff -> skb conversion")
> Signed-off-by: Björn Töpel <bjorn@xxxxxxxxxx>
Late on the ball here, but I believe this is the case Clement reported and
we rejected it as the reproducer was OOT module.
https://lore.kernel.org/bpf/20260629072300.1664622-1-clecigne@xxxxxxxxxx/
Should we proceed with Clement's fix then?
> ---
> net/core/xdp.c | 14 ++++++++++----
> 1 file changed, 10 insertions(+), 4 deletions(-)
>
> diff --git a/net/core/xdp.c b/net/core/xdp.c
> index 1d679e8fd649..386240bd24c9 100644
> --- a/net/core/xdp.c
> +++ b/net/core/xdp.c
> @@ -709,7 +709,7 @@ static noinline bool xdp_copy_frags_from_zc(struct sk_buff *skb,
> }
>
> memcpy(page_address(page) + offset, skb_frag_address(frag),
> - LARGEST_ALIGN(len));
> + len);
> __skb_fill_page_desc_noacc(sinfo, i, page, offset, len);
>
> tsize += truesize;
> @@ -738,17 +738,23 @@ static noinline bool xdp_copy_frags_from_zc(struct sk_buff *skb,
> */
> struct sk_buff *xdp_build_skb_from_zc(struct xdp_buff *xdp)
> {
> + u32 headroom = xdp->data_meta - xdp->data_hard_start;
> const struct xdp_rxq_info *rxq = xdp->rxq;
> u32 len = xdp->data_end - xdp->data_meta;
> - u32 truesize = xdp->frame_sz;
> struct sk_buff *skb = NULL;
> struct page_pool *pp;
> + u32 truesize;
> int metalen;
> void *data;
>
> if (!IS_ENABLED(CONFIG_PAGE_POOL))
> return NULL;
>
> + /* The XSK frame size leaves no room for skb_shared_info. */
> + truesize = SKB_HEAD_ALIGN(headroom + len);
> + if (unlikely(truesize > PAGE_SIZE))
> + return NULL;
> +
> local_lock_nested_bh(&system_page_pool.bh_lock);
> pp = this_cpu_read(system_page_pool.pool);
> data = page_pool_dev_alloc_va(pp, &truesize);
> @@ -762,9 +768,9 @@ struct sk_buff *xdp_build_skb_from_zc(struct xdp_buff *xdp)
> }
>
> skb_mark_for_recycle(skb);
> - skb_reserve(skb, xdp->data_meta - xdp->data_hard_start);
> + skb_reserve(skb, headroom);
>
> - memcpy(__skb_put(skb, len), xdp->data_meta, LARGEST_ALIGN(len));
> + memcpy(__skb_put(skb, len), xdp->data_meta, len);
>
> metalen = xdp->data - xdp->data_meta;
> if (metalen > 0) {
> --
> 2.55.0
>