Merge branch 'fix-skb-length-accounting-after-xdp-frag-adjustment'

Sun Jian says:

====================
fix skb length accounting after XDP frag adjustment

This series fixes skb length accounting after an XDP program adjusts its
fragment area, in both the generic XDP path (net/core/dev.c) and the veth
native path (drivers/net/veth.c). When the fragment area is resized,
skb->len and skb->data_len can go out of sync, and in the reproduced UDP
receive path this leaked skb_shared_info contents (including a kernel
pointer) to userspace while truncating real payload.
====================

Link: https://patch.msgid.link/20260804054040.613675-1-sun.jian.kdev@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2026-08-07 17:01:10 -07:00
commit d47b06aa3e
2 changed files with 21 additions and 11 deletions

View File

@ -865,18 +865,24 @@ static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq,
skb_reset_mac_header(skb);
/* check if bpf_xdp_adjust_tail was used */
off = xdp->data_end - orig_data_end;
if (off != 0)
__skb_put(skb, off); /* positive on grow, negative on shrink */
/* XDP frag metadata (e.g. nr_frags) are updated in eBPF helpers
* (e.g. bpf_xdp_adjust_tail), we need to update data_len here.
* (e.g. bpf_xdp_adjust_tail). Remove the old fragment contribution
* from skb->len before updating data_len, then add the new one back.
*/
if (xdp_buff_has_frags(xdp))
skb->len -= skb->data_len;
if (xdp_buff_has_frags(xdp)) {
skb->data_len = skb_shinfo(skb)->xdp_frags_size;
else
skb->len += skb->data_len;
} else {
skb->data_len = 0;
}
/* Synchronize the skb tail with XDP's updated linear area. */
off = xdp->data_end - orig_data_end;
if (off != 0) {
skb_set_tail_pointer(skb, xdp->data_end - xdp->data);
skb->len += off; /* positive on grow, negative on shrink */
}
skb->protocol = eth_type_trans(skb, rq->dev);

View File

@ -5517,12 +5517,16 @@ u32 bpf_prog_run_generic_xdp(struct sk_buff *skb, struct xdp_buff *xdp,
}
/* XDP frag metadata (e.g. nr_frags) are updated in eBPF helpers
* (e.g. bpf_xdp_adjust_tail), we need to update data_len here.
* (e.g. bpf_xdp_adjust_tail). Remove the old fragment contribution
* from skb->len before updating data_len, then add the new one back.
*/
if (xdp_buff_has_frags(xdp))
skb->len -= skb->data_len;
if (xdp_buff_has_frags(xdp)) {
skb->data_len = skb_shinfo(skb)->xdp_frags_size;
else
skb->len += skb->data_len;
} else {
skb->data_len = 0;
}
/* check if XDP changed eth hdr such SKB needs update */
eth = (struct ethhdr *)xdp->data;