* [PATCH net v2 0/2] Fix skb length accounting after XDP frag adjustment
@ 2026-07-31 3:23 Sun Jian
2026-07-31 3:23 ` [PATCH net v2 1/2] net: fix skb length accounting after generic " Sun Jian
2026-07-31 3:23 ` [PATCH net v2 2/2] veth: fix skb length accounting after " Sun Jian
0 siblings, 2 replies; 8+ messages in thread
From: Sun Jian @ 2026-07-31 3:23 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Kuniyuki Iwashima, Hangbin Liu, Krishna Kumar, Samiullah Khawaja,
Martin Karsten, Lorenzo Bianconi,
Toke Høiland-Jørgensen, linux-kernel, bpf, Sun Jian,
maciej.fijalkowski
Hi,
This series fixes skb length accounting after XDP fragment adjustment in
generic XDP and veth.
v1:
https://lore.kernel.org/bpf/20260727032535.13469-1-sun.jian.kdev@gmail.com/
Changes in v2:
- Move the veth fragment accounting before the linear tail adjustment, so
__skb_put() observes a linear skb after a shrink removes all fragments.
- Fold the skb->len update into the xdp_buff_has_frags() branch, as suggested
by Lorenzo Bianconi.
- Clarify why the old data_len contribution must be removed before adding the
updated one, as requested by Maciej Fijalkowski.
Sun Jian (2):
net: fix skb length accounting after generic XDP frag adjustment
veth: fix skb length accounting after XDP frag adjustment
drivers/net/veth.c | 23 +++++++++++++++--------
net/core/dev.c | 10 +++++++---
2 files changed, 22 insertions(+), 11 deletions(-)
Range-diff against v1:
1: b914fb40f348 ! 1: 2d716a1bd570 net: fix skb length accounting after generic XDP frag adjustment
@@ Commit message
Fixes: e6d5dbdd20aa ("xdp: add multi-buff support for xdp running in generic mode")
Cc: stable@vger.kernel.org
- Link: https://lore.kernel.org/r/20260720141859.19FF41F000E9@smtp.kernel.org
Link: https://lore.kernel.org/bpf/al9T9Eto%2FhRIzP5W@boxer/
Signed-off-by: Sun Jian <sun.jian.kdev@gmail.com>
@@ net/core/dev.c: u32 bpf_prog_run_generic_xdp(struct sk_buff *skb, struct xdp_buf
/* 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), update skb length fields 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))
++ if (xdp_buff_has_frags(xdp)) {
skb->data_len = skb_shinfo(skb)->xdp_frags_size;
- else
+- else
++ skb->len += skb->data_len;
++ } else {
skb->data_len = 0;
-+ skb->len += skb->data_len;
++ }
/* check if XDP changed eth hdr such SKB needs update */
eth = (struct ethhdr *)xdp->data;
2: 59c79966c84a ! 2: e5b9383bc46e veth: fix skb length accounting after XDP frag adjustment
@@ Commit message
Subtract the old data_len before replacing it and add the new data_len
afterwards, keeping skb->len and skb->data_len synchronized.
+ The fragment accounting must run before the linear tail adjustment:
+ when bpf_xdp_adjust_tail() shrinks the packet into the linear area it
+ releases all fragments, and __skb_put() requires skb->data_len == 0
+ by that point.
+
A 60000-byte UDP datagram on a veth pair with MTU 64000 was shortened by
1024 bytes from its fragment area. Before the fix, all 10 runs produced
corrupted payloads. After the fix, all 10 runs matched the expected
@@ Commit message
Fixes: 718a18a0c8a6 ("veth: Rework veth_xdp_rcv_skb in order to accept non-linear skb")
Cc: stable@vger.kernel.org
- Link: https://lore.kernel.org/r/20260720141859.19FF41F000E9@smtp.kernel.org
Link: https://lore.kernel.org/bpf/al9T9Eto%2FhRIzP5W@boxer/
Signed-off-by: Sun Jian <sun.jian.kdev@gmail.com>
## drivers/net/veth.c ##
@@ drivers/net/veth.c: static struct sk_buff *veth_xdp_rcv_skb(struct veth_rq *rq,
- __skb_put(skb, off); /* positive on grow, negative on shrink */
+ 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), update skb length fields 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.
++ * This must precede the linear tail adjustment below: a changed
++ * data_end implies that no fragments remain, and __skb_put() requires
++ * a linear skb.
*/
+- if (xdp_buff_has_frags(xdp))
+ skb->len -= skb->data_len;
- if (xdp_buff_has_frags(xdp))
++ if (xdp_buff_has_frags(xdp)) {
skb->data_len = skb_shinfo(skb)->xdp_frags_size;
- else
+- else
++ skb->len += skb->data_len;
++ } else {
skb->data_len = 0;
-+ skb->len += skb->data_len;
++ }
++
++ /* 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 */
skb->protocol = eth_type_trans(skb, rq->dev);
base-commit: 97ac08560d236ca17f6606d9e671118e5eae5721
--
2.43.0
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH net v2 1/2] net: fix skb length accounting after generic XDP frag adjustment
2026-07-31 3:23 [PATCH net v2 0/2] Fix skb length accounting after XDP frag adjustment Sun Jian
@ 2026-07-31 3:23 ` Sun Jian
2026-07-31 15:45 ` Mohsin Bashir
` (2 more replies)
2026-07-31 3:23 ` [PATCH net v2 2/2] veth: fix skb length accounting after " Sun Jian
1 sibling, 3 replies; 8+ messages in thread
From: Sun Jian @ 2026-07-31 3:23 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Kuniyuki Iwashima, Hangbin Liu, Krishna Kumar, Samiullah Khawaja,
Martin Karsten, Lorenzo Bianconi,
Toke Høiland-Jørgensen, linux-kernel, bpf, Sun Jian,
maciej.fijalkowski, stable
Generic XDP exposes non-linear skb fragments through an xdp_buff. If an
XDP program adjusts the fragment area, bpf_prog_run_generic_xdp() copies
xdp_frags_size back to skb->data_len but leaves skb->len containing the
old fragment contribution.
After a fragment shrink, this makes skb_headlen() larger than the actual
linear area. In the reproduced UDP receive path, __skb_datagram_iter()
copied 1024 bytes past the actual linear tail to userspace, starting at
struct skb_shared_info. The copied bytes included the affected skb's
nr_frags, xdp_frags_size and a kernel pointer from
skb_shinfo(skb)->frags[0]. Real packet data was displaced by the same
amount and truncated at the end.
Subtract the old data_len before replacing it and add the new data_len
afterwards, keeping skb->len and skb->data_len synchronized.
A 60000-byte UDP datagram on a veth pair with MTU 64000 was shortened by
1024 bytes from its fragment area. Before the fix, all 10 runs produced
corrupted payloads. After the fix, all 10 runs matched the expected
payload exactly.
Fixes: e6d5dbdd20aa ("xdp: add multi-buff support for xdp running in generic mode")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/bpf/al9T9Eto%2FhRIzP5W@boxer/
Signed-off-by: Sun Jian <sun.jian.kdev@gmail.com>
---
net/core/dev.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/net/core/dev.c b/net/core/dev.c
index 5933c5dab09e..5c37cf6c4aa1 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -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;
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH net v2 2/2] veth: fix skb length accounting after XDP frag adjustment
2026-07-31 3:23 [PATCH net v2 0/2] Fix skb length accounting after XDP frag adjustment Sun Jian
2026-07-31 3:23 ` [PATCH net v2 1/2] net: fix skb length accounting after generic " Sun Jian
@ 2026-07-31 3:23 ` Sun Jian
2026-07-31 16:04 ` Lorenzo Bianconi
2026-07-31 16:14 ` Mohsin Bashir
1 sibling, 2 replies; 8+ messages in thread
From: Sun Jian @ 2026-07-31 3:23 UTC (permalink / raw)
To: netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Kuniyuki Iwashima, Hangbin Liu, Krishna Kumar, Samiullah Khawaja,
Martin Karsten, Lorenzo Bianconi,
Toke Høiland-Jørgensen, linux-kernel, bpf, Sun Jian,
maciej.fijalkowski, stable
veth exposes non-linear skb fragments through an xdp_buff. If an XDP
program adjusts the fragment area, veth_xdp_rcv_skb() copies
xdp_frags_size back to skb->data_len but leaves skb->len containing the
old fragment contribution.
After a fragment shrink, this makes skb_headlen() larger than the actual
linear area. In the reproduced UDP receive path, __skb_datagram_iter()
copied 1024 bytes past the actual linear tail to userspace, starting at
struct skb_shared_info. The copied bytes included the affected skb's
nr_frags, xdp_frags_size and a kernel pointer from
skb_shinfo(skb)->frags[0]. Real packet data was displaced by the same
amount and truncated at the end.
Subtract the old data_len before replacing it and add the new data_len
afterwards, keeping skb->len and skb->data_len synchronized.
The fragment accounting must run before the linear tail adjustment:
when bpf_xdp_adjust_tail() shrinks the packet into the linear area it
releases all fragments, and __skb_put() requires skb->data_len == 0
by that point.
A 60000-byte UDP datagram on a veth pair with MTU 64000 was shortened by
1024 bytes from its fragment area. Before the fix, all 10 runs produced
corrupted payloads. After the fix, all 10 runs matched the expected
payload exactly.
Fixes: 718a18a0c8a6 ("veth: Rework veth_xdp_rcv_skb in order to accept non-linear skb")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/bpf/al9T9Eto%2FhRIzP5W@boxer/
Signed-off-by: Sun Jian <sun.jian.kdev@gmail.com>
---
drivers/net/veth.c | 23 +++++++++++++++--------
1 file changed, 15 insertions(+), 8 deletions(-)
diff --git a/drivers/net/veth.c b/drivers/net/veth.c
index 00e34afd858e..348391e87e14 100644
--- a/drivers/net/veth.c
+++ b/drivers/net/veth.c
@@ -865,18 +865,25 @@ 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.
+ * This must precede the linear tail adjustment below: a changed
+ * data_end implies that no fragments remain, and __skb_put() requires
+ * a linear skb.
*/
- 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 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 */
skb->protocol = eth_type_trans(skb, rq->dev);
--
2.43.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH net v2 1/2] net: fix skb length accounting after generic XDP frag adjustment
2026-07-31 3:23 ` [PATCH net v2 1/2] net: fix skb length accounting after generic " Sun Jian
@ 2026-07-31 15:45 ` Mohsin Bashir
2026-07-31 15:51 ` Lorenzo Bianconi
2026-08-01 3:24 ` sashiko-bot
2 siblings, 0 replies; 8+ messages in thread
From: Mohsin Bashir @ 2026-07-31 15:45 UTC (permalink / raw)
To: Sun Jian, netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Kuniyuki Iwashima, Hangbin Liu, Krishna Kumar, Samiullah Khawaja,
Martin Karsten, Lorenzo Bianconi,
Toke Høiland-Jørgensen, linux-kernel, bpf,
maciej.fijalkowski, stable
On 7/30/26 8:23 PM, Sun Jian wrote:
> Generic XDP exposes non-linear skb fragments through an xdp_buff. If an
> XDP program adjusts the fragment area, bpf_prog_run_generic_xdp() copies
> xdp_frags_size back to skb->data_len but leaves skb->len containing the
> old fragment contribution.
>
> After a fragment shrink, this makes skb_headlen() larger than the actual
> linear area. In the reproduced UDP receive path, __skb_datagram_iter()
> copied 1024 bytes past the actual linear tail to userspace, starting at
> struct skb_shared_info. The copied bytes included the affected skb's
> nr_frags, xdp_frags_size and a kernel pointer from
> skb_shinfo(skb)->frags[0]. Real packet data was displaced by the same
> amount and truncated at the end.
>
> Subtract the old data_len before replacing it and add the new data_len
> afterwards, keeping skb->len and skb->data_len synchronized.
>
> A 60000-byte UDP datagram on a veth pair with MTU 64000 was shortened by
> 1024 bytes from its fragment area. Before the fix, all 10 runs produced
> corrupted payloads. After the fix, all 10 runs matched the expected
> payload exactly.
>
> Fixes: e6d5dbdd20aa ("xdp: add multi-buff support for xdp running in generic mode")
> Cc: stable@vger.kernel.org
> Link: https://lore.kernel.org/bpf/al9T9Eto%2FhRIzP5W@boxer/
> Signed-off-by: Sun Jian <sun.jian.kdev@gmail.com>
> ---
> net/core/dev.c | 10 +++++++---
> 1 file changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/net/core/dev.c b/net/core/dev.c
> index 5933c5dab09e..5c37cf6c4aa1 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -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;
Reviewed-by: Mohsin Bashir <hmohsin@meta.com>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net v2 1/2] net: fix skb length accounting after generic XDP frag adjustment
2026-07-31 3:23 ` [PATCH net v2 1/2] net: fix skb length accounting after generic " Sun Jian
2026-07-31 15:45 ` Mohsin Bashir
@ 2026-07-31 15:51 ` Lorenzo Bianconi
2026-08-01 3:24 ` sashiko-bot
2 siblings, 0 replies; 8+ messages in thread
From: Lorenzo Bianconi @ 2026-07-31 15:51 UTC (permalink / raw)
To: Sun Jian
Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Alexei Starovoitov,
Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
Stanislav Fomichev, Kuniyuki Iwashima, Hangbin Liu, Krishna Kumar,
Samiullah Khawaja, Martin Karsten,
Toke Høiland-Jørgensen, linux-kernel, bpf,
maciej.fijalkowski, stable
[-- Attachment #1: Type: text/plain, Size: 2415 bytes --]
> Generic XDP exposes non-linear skb fragments through an xdp_buff. If an
> XDP program adjusts the fragment area, bpf_prog_run_generic_xdp() copies
> xdp_frags_size back to skb->data_len but leaves skb->len containing the
> old fragment contribution.
>
> After a fragment shrink, this makes skb_headlen() larger than the actual
> linear area. In the reproduced UDP receive path, __skb_datagram_iter()
> copied 1024 bytes past the actual linear tail to userspace, starting at
> struct skb_shared_info. The copied bytes included the affected skb's
> nr_frags, xdp_frags_size and a kernel pointer from
> skb_shinfo(skb)->frags[0]. Real packet data was displaced by the same
> amount and truncated at the end.
>
> Subtract the old data_len before replacing it and add the new data_len
> afterwards, keeping skb->len and skb->data_len synchronized.
>
> A 60000-byte UDP datagram on a veth pair with MTU 64000 was shortened by
> 1024 bytes from its fragment area. Before the fix, all 10 runs produced
> corrupted payloads. After the fix, all 10 runs matched the expected
> payload exactly.
>
> Fixes: e6d5dbdd20aa ("xdp: add multi-buff support for xdp running in generic mode")
> Cc: stable@vger.kernel.org
> Link: https://lore.kernel.org/bpf/al9T9Eto%2FhRIzP5W@boxer/
> Signed-off-by: Sun Jian <sun.jian.kdev@gmail.com>
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
> net/core/dev.c | 10 +++++++---
> 1 file changed, 7 insertions(+), 3 deletions(-)
>
> diff --git a/net/core/dev.c b/net/core/dev.c
> index 5933c5dab09e..5c37cf6c4aa1 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -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;
> --
> 2.43.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net v2 2/2] veth: fix skb length accounting after XDP frag adjustment
2026-07-31 3:23 ` [PATCH net v2 2/2] veth: fix skb length accounting after " Sun Jian
@ 2026-07-31 16:04 ` Lorenzo Bianconi
2026-07-31 16:14 ` Mohsin Bashir
1 sibling, 0 replies; 8+ messages in thread
From: Lorenzo Bianconi @ 2026-07-31 16:04 UTC (permalink / raw)
To: Sun Jian
Cc: netdev, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, Alexei Starovoitov,
Daniel Borkmann, Jesper Dangaard Brouer, John Fastabend,
Stanislav Fomichev, Kuniyuki Iwashima, Hangbin Liu, Krishna Kumar,
Samiullah Khawaja, Martin Karsten,
Toke Høiland-Jørgensen, linux-kernel, bpf,
maciej.fijalkowski, stable
[-- Attachment #1: Type: text/plain, Size: 3184 bytes --]
> veth exposes non-linear skb fragments through an xdp_buff. If an XDP
> program adjusts the fragment area, veth_xdp_rcv_skb() copies
> xdp_frags_size back to skb->data_len but leaves skb->len containing the
> old fragment contribution.
>
> After a fragment shrink, this makes skb_headlen() larger than the actual
> linear area. In the reproduced UDP receive path, __skb_datagram_iter()
> copied 1024 bytes past the actual linear tail to userspace, starting at
> struct skb_shared_info. The copied bytes included the affected skb's
> nr_frags, xdp_frags_size and a kernel pointer from
> skb_shinfo(skb)->frags[0]. Real packet data was displaced by the same
> amount and truncated at the end.
>
> Subtract the old data_len before replacing it and add the new data_len
> afterwards, keeping skb->len and skb->data_len synchronized.
>
> The fragment accounting must run before the linear tail adjustment:
> when bpf_xdp_adjust_tail() shrinks the packet into the linear area it
> releases all fragments, and __skb_put() requires skb->data_len == 0
> by that point.
>
> A 60000-byte UDP datagram on a veth pair with MTU 64000 was shortened by
> 1024 bytes from its fragment area. Before the fix, all 10 runs produced
> corrupted payloads. After the fix, all 10 runs matched the expected
> payload exactly.
>
> Fixes: 718a18a0c8a6 ("veth: Rework veth_xdp_rcv_skb in order to accept non-linear skb")
> Cc: stable@vger.kernel.org
> Link: https://lore.kernel.org/bpf/al9T9Eto%2FhRIzP5W@boxer/
> Signed-off-by: Sun Jian <sun.jian.kdev@gmail.com>
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
> ---
> drivers/net/veth.c | 23 +++++++++++++++--------
> 1 file changed, 15 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/net/veth.c b/drivers/net/veth.c
> index 00e34afd858e..348391e87e14 100644
> --- a/drivers/net/veth.c
> +++ b/drivers/net/veth.c
> @@ -865,18 +865,25 @@ 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.
> + * This must precede the linear tail adjustment below: a changed
> + * data_end implies that no fragments remain, and __skb_put() requires
> + * a linear skb.
> */
> - 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 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 */
>
> skb->protocol = eth_type_trans(skb, rq->dev);
>
> --
> 2.43.0
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net v2 2/2] veth: fix skb length accounting after XDP frag adjustment
2026-07-31 3:23 ` [PATCH net v2 2/2] veth: fix skb length accounting after " Sun Jian
2026-07-31 16:04 ` Lorenzo Bianconi
@ 2026-07-31 16:14 ` Mohsin Bashir
1 sibling, 0 replies; 8+ messages in thread
From: Mohsin Bashir @ 2026-07-31 16:14 UTC (permalink / raw)
To: Sun Jian, netdev
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Alexei Starovoitov, Daniel Borkmann,
Jesper Dangaard Brouer, John Fastabend, Stanislav Fomichev,
Kuniyuki Iwashima, Hangbin Liu, Krishna Kumar, Samiullah Khawaja,
Martin Karsten, Lorenzo Bianconi,
Toke Høiland-Jørgensen, linux-kernel, bpf,
maciej.fijalkowski, stable
On 7/30/26 8:23 PM, Sun Jian wrote:
> veth exposes non-linear skb fragments through an xdp_buff. If an XDP
> program adjusts the fragment area, veth_xdp_rcv_skb() copies
> xdp_frags_size back to skb->data_len but leaves skb->len containing the
> old fragment contribution.
>
> After a fragment shrink, this makes skb_headlen() larger than the actual
> linear area. In the reproduced UDP receive path, __skb_datagram_iter()
> copied 1024 bytes past the actual linear tail to userspace, starting at
> struct skb_shared_info. The copied bytes included the affected skb's
> nr_frags, xdp_frags_size and a kernel pointer from
> skb_shinfo(skb)->frags[0]. Real packet data was displaced by the same
> amount and truncated at the end.
>
> Subtract the old data_len before replacing it and add the new data_len
> afterwards, keeping skb->len and skb->data_len synchronized.
>
> The fragment accounting must run before the linear tail adjustment:
> when bpf_xdp_adjust_tail() shrinks the packet into the linear area it
> releases all fragments, and __skb_put() requires skb->data_len == 0
> by that point.
>
> A 60000-byte UDP datagram on a veth pair with MTU 64000 was shortened by
> 1024 bytes from its fragment area. Before the fix, all 10 runs produced
> corrupted payloads. After the fix, all 10 runs matched the expected
> payload exactly.
>
> Fixes: 718a18a0c8a6 ("veth: Rework veth_xdp_rcv_skb in order to accept non-linear skb")
> Cc: stable@vger.kernel.org
> Link: https://lore.kernel.org/bpf/al9T9Eto%2FhRIzP5W@boxer/
> Signed-off-by: Sun Jian <sun.jian.kdev@gmail.com>
> ---
> drivers/net/veth.c | 23 +++++++++++++++--------
> 1 file changed, 15 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/net/veth.c b/drivers/net/veth.c
> index 00e34afd858e..348391e87e14 100644
> --- a/drivers/net/veth.c
> +++ b/drivers/net/veth.c
> @@ -865,18 +865,25 @@ 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.
> + * This must precede the linear tail adjustment below: a changed
> + * data_end implies that no fragments remain, and __skb_put() requires
> + * a linear skb.
> */
> - 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 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 */
>
> skb->protocol = eth_type_trans(skb, rq->dev);
>
I am most likely missing something here but what happens if we have
frags and we attempt to advance data_end while leaving some frags
present (e.g., bpf_xdp_pull_data())? Looks like, in that case we would
issue __skb_put(skb, off) with off > 0 and we would hit
SKB_LINEAR_ASSERT() because skb is still non-linear?
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH net v2 1/2] net: fix skb length accounting after generic XDP frag adjustment
2026-07-31 3:23 ` [PATCH net v2 1/2] net: fix skb length accounting after generic " Sun Jian
2026-07-31 15:45 ` Mohsin Bashir
2026-07-31 15:51 ` Lorenzo Bianconi
@ 2026-08-01 3:24 ` sashiko-bot
2 siblings, 0 replies; 8+ messages in thread
From: sashiko-bot @ 2026-08-01 3:24 UTC (permalink / raw)
To: Sun Jian; +Cc: bpf
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
Pre-existing issues:
- [Critical] The exact same metadata desynchronization bug that causes OOB reads in generic XDP also exists in drivers/net/veth.c, including an additional kernel panic vulnerability via BUG_ON.
- [Critical] bpf_xdp_frags_shrink_tail() incorrectly frees system_page_pool pages using page_frag_free() for Generic XDP.
- [High] Preexisting BUG_ON() panic in generic XDP when the head is pulled by more than the original linear length.
--
--- Patch [1]: [PATCH net v2 1/2] net: fix skb length accounting after generic XDP frag adjustment ---
commit 98b02c083e22c12a3a47fab8bce5b83fb6fad2b7
Author: Sun Jian <sun.jian.kdev@gmail.com>
net: fix skb length accounting after generic XDP frag adjustment
This commit fixes an out-of-bounds read caused by generic XDP fragment
adjustment. It subtracts the old data_len from skb->len before updating
it to the new fragment size, keeping the lengths synchronized.
Link: https://lore.kernel.org/bpf/al9T9Eto%2FhRIzP5W@boxer/
> diff --git a/net/core/dev.c b/net/core/dev.c
> index 5933c5dab09ee..5c37cf6c4aa10 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -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;
[Severity: High]
This is a pre-existing issue, but does bpf_prog_run_generic_xdp() still
panic if the head is pulled by more than the original linear length?
If an XDP program shrinks a packet's fragments to 0, it can subsequently
grow the linear part and pull the head pointer (via bpf_xdp_adjust_head)
by an amount greater than the original linear length.
In bpf_prog_run_generic_xdp(), the head adjustment is processed before
the tail and data_len updates:
if (off > 0)
__skb_pull(skb, off);
Since __skb_pull() asserts that skb->len >= skb->data_len and skb->data_len
still holds its original value, wouldn't skb->len -= off fall below
skb->data_len and trigger a kernel panic?
[Severity: Critical]
This is a pre-existing issue, but does veth_xdp_rcv_skb() in
drivers/net/veth.c have the exact same metadata desynchronization bug?
If a fragmented packet is processed by a tail-adjusting XDP program in
veth_xdp_rcv_skb(), skb->len is unmodified while skb->data_len changes:
/* 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 */
If the packet is shrunk enough to remove all fragments and reduce the linear
part, wouldn't calling __skb_put(skb, off) with a negative offset trigger a
BUG_ON since skb->data_len is still non-zero at this point?
[Severity: Critical]
This is a pre-existing issue, but does bpf_xdp_shrink_data() incorrectly
free system_page_pool pages using page_frag_free() for Generic XDP?
When generic XDP processes a non-linear SKB, fragments are allocated from
system_page_pool. However, xdp_buff is initialized using the driver's
rxqueue->xdp_rxq (often MEM_TYPE_PAGE_SHARED).
If an XDP program shrinks the packet tail, bpf_xdp_shrink_data() drops
fragments and extracts the driver's mem_type:
enum xdp_mem_type mem_type = xdp->rxq->mem.type;
...
if (release) {
__xdp_return(netmem, mem_type, false, zc_frag);
}
Because the memory type relies on the driver's default, wouldn't
__xdp_return() incorrectly use page_frag_free() instead of returning the
page to the page_pool?
case MEM_TYPE_PAGE_SHARED:
page_frag_free(__netmem_address(netmem));
break;
Could this bypass the page_pool recycling logic and free a page_pool page
directly to the buddy allocator, triggering a bad_page() BUG?
> + 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;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260731032357.6114-1-sun.jian.kdev@gmail.com?part=1
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-08-01 3:24 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-31 3:23 [PATCH net v2 0/2] Fix skb length accounting after XDP frag adjustment Sun Jian
2026-07-31 3:23 ` [PATCH net v2 1/2] net: fix skb length accounting after generic " Sun Jian
2026-07-31 15:45 ` Mohsin Bashir
2026-07-31 15:51 ` Lorenzo Bianconi
2026-08-01 3:24 ` sashiko-bot
2026-07-31 3:23 ` [PATCH net v2 2/2] veth: fix skb length accounting after " Sun Jian
2026-07-31 16:04 ` Lorenzo Bianconi
2026-07-31 16:14 ` Mohsin Bashir
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.