[PATCH bpf v5 0/2] bpf: tcp: Fix null-ptr-deref in arbitrary SYN Cookie

Jiayuan Chen
2026-04-01T11:05:36Z

From: Jiayuan Chen <jiayuan.chen@shopee.com>

bpf_sk_assign_tcp_reqsk() does not validate the L4 protocol of the skb,
only checking skb->protocol (L3). A BPF program that calls this kfunc on
a non-TCP skb (e.g. UDP) will succeed, attaching a TCP reqsk to the skb.

When the skb enters the UDP receive path, skb_steal_sock() returns the
TCP listener socket from the reqsk. The UDP code then casts this TCP
socket to udp_sock and accesses UDP-specific fields at invalid offsets,
causing a null pointer dereference:

BUG: KASAN: null-ptr-deref in __udp_enqueue_schedule_skb+0x19d/0x1df0
Read of size 4 at addr 0000000000000008 by task test_progs/537

CPU: 1 UID: 0 PID: 537 Comm: test_progs Not tainted 7.0.0-rc4+ #46 PREEMPT
Call Trace:
<IRQ>
dump_stack_lvl (lib/dump_stack.c:123)
print_report (mm/kasan/report.c:487)
kasan_report (mm/kasan/report.c:597)
__kasan_check_read (mm/kasan/shadow.c:32)
__udp_enqueue_schedule_skb (net/ipv4/udp.c:1719)
udp_queue_rcv_one_skb (net/ipv4/udp.c:2370 net/ipv4/udp.c:2500)
udp_queue_rcv_skb (net/ipv4/udp.c:2532)
udp_unicast_rcv_skb (net/ipv4/udp.c:2684)
__udp4_lib_rcv (net/ipv4/udp.c:2742)
udp_rcv (net/ipv4/udp.c:2937)
ip_protocol_deliver_rcu (net/ipv4/ip_input.c:209)
ip_local_deliver_finish (./include/linux/rcupdate.h:879 net/ipv4/ip_input.c:242)
ip_local_deliver (net/ipv4/ip_input.c:265)
__netif_receive_skb_one_core (net/core/dev.c:6164 (discriminator 4))
__netif_receive_skb (net/core/dev.c:6280)

Solution

Patch 1: Add L4 protocol validation in bpf_sk_assign_tcp_reqsk(). Check
ip_hdr(skb)->protocol (IPv4) and ipv6_hdr(skb)->nexthdr (IPv6) against
IPPROTO_TCP, returning -EINVAL for non-TCP skbs.
Patch 2: Add selftest that calls bpf_sk_assign_tcp_reqsk() on a UDP skb
and verifies the kfunc rejects it.

---
v1: https://lore.kernel.org/bpf/20260323105510.51990-1-jiayuan.chen@linux.dev/
v2: https://lore.kernel.org/bpf/20260326062657.88446-1-jiayuan.chen@linux.dev/
v3: https://lore.kernel.org/bpf/20260327133915.286037-1-jiayuan.chen@linux.dev/
v4: https://lore.kernel.org/bpf/20260330080746.319680-1-jiayuan.chen@linux.dev/

Changes in v5:
- Add pskb_may_pull before accessing IP/IPv6 headers in kfunc
- Use buf[] instead of buf[32], verify recv data with ASSERT_STREQ
- Remove unnecessary variable initializations in selftest and BPF

Changes in v4:
- Check if assign_ret is EINVAL instead of checking if it is 0

Changes in v3:
- Add IPv6 test coverage, reuse test_cases[] to iterate over both
address families
- Share TCP/UDP port to simplify BPF program, remove unnecessary
global variables
- Use connect_to_fd() + send()/recv() instead of manual sockaddr
construction
- Suggested by Kuniyuki Iwashima

Changes in v2:
- Add Reviewed-by tag from Kuniyuki Iwashima for patch 1
- Use UDP socket recv() instead of kern_sync_rcu() for synchronization
in selftest

Jiayuan Chen (2):
bpf: tcp: Reject non-TCP skb in bpf_sk_assign_tcp_reqsk()
selftests/bpf: Add protocol check test for bpf_sk_assign_tcp_reqsk()

net/core/filter.c | 12 ++
.../bpf/prog_tests/tcp_custom_syncookie.c | 93 ++++++++++++++-
.../bpf/progs/test_tcp_custom_syncookie.c | 109 ++++++++++++++++++
3 files changed, 210 insertions(+), 4 deletions(-)

--
2.43.0