All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jiayuan Chen <jiayuan.chen@linux.dev>
To: netdev@vger.kernel.org
Cc: Jiayuan Chen <jiayuan.chen@shopee.com>,
	Jiayuan Chen <jiayuan.chen@linux.dev>,
	Martin KaFai Lau <martin.lau@linux.dev>,
	Daniel Borkmann <daniel@iogearbox.net>,
	John Fastabend <john.fastabend@gmail.com>,
	Stanislav Fomichev <sdf@fomichev.me>,
	Alexei Starovoitov <ast@kernel.org>,
	Andrii Nakryiko <andrii@kernel.org>,
	Eduard Zingerman <eddyz87@gmail.com>, Song Liu <song@kernel.org>,
	Yonghong Song <yonghong.song@linux.dev>,
	KP Singh <kpsingh@kernel.org>, Hao Luo <haoluo@google.com>,
	Jiri Olsa <jolsa@kernel.org>,
	"David S. Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Simon Horman <horms@kernel.org>, Shuah Khan <shuah@kernel.org>,
	Kuniyuki Iwashima <kuniyu@google.com>,
	bpf@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-kselftest@vger.kernel.org
Subject: [PATCH bpf v2 2/2] selftests/bpf: Add protocol check test for bpf_sk_assign_tcp_reqsk()
Date: Thu, 26 Mar 2026 14:26:36 +0800	[thread overview]
Message-ID: <20260326062657.88446-3-jiayuan.chen@linux.dev> (raw)
In-Reply-To: <20260326062657.88446-1-jiayuan.chen@linux.dev>

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

Add test_tcp_custom_syncookie_protocol_check to verify that
bpf_sk_assign_tcp_reqsk() rejects non-TCP skbs. The test sends a UDP
packet through TC ingress where a BPF program calls
bpf_sk_assign_tcp_reqsk() on it and checks that the kfunc returns an
error. A UDP server recv() is used as synchronization to ensure the
BPF program has finished processing before checking the result.

Without the fix in bpf_sk_assign_tcp_reqsk(), the kfunc succeeds and
attaches a TCP reqsk to the UDP skb, which causes a null pointer
dereference panic when the kernel processes it through the UDP receive
path.

Test result:

  ./test_progs -a tcp_custom_syncookie_protocol_check -v
  setup_netns:PASS:create netns 0 nsec
  setup_netns:PASS:ip 0 nsec
  write_sysctl:PASS:open sysctl 0 nsec
  write_sysctl:PASS:write sysctl 0 nsec
  setup_netns:PASS:write_sysctl 0 nsec
  test_tcp_custom_syncookie_protocol_check:PASS:open_and_load 0 nsec
  test_tcp_custom_syncookie_protocol_check:PASS:start tcp_server 0 nsec
  test_tcp_custom_syncookie_protocol_check:PASS:start udp_server 0 nsec
  setup_tc:PASS:qdisc add dev lo clsact 0 nsec
  setup_tc:PASS:filter add dev lo ingress 0 nsec
  test_tcp_custom_syncookie_protocol_check:PASS:udp socket 0 nsec
  test_tcp_custom_syncookie_protocol_check:PASS:sendto udp 0 nsec
  test_tcp_custom_syncookie_protocol_check:PASS:recv udp 0 nsec
  test_tcp_custom_syncookie_protocol_check:PASS:udp_intercepted 0 nsec
  test_tcp_custom_syncookie_protocol_check:PASS:assign_ret 0 nsec
  #471     tcp_custom_syncookie_protocol_check:OK
  Summary: 1/0 PASSED, 0 SKIPPED, 0 FAILED

Cc: Jiayuan Chen <jiayuan.chen@linux.dev>
Signed-off-by: Jiayuan Chen <jiayuan.chen@shopee.com>
---
 .../bpf/prog_tests/tcp_custom_syncookie.c     | 84 ++++++++++++++++++-
 .../bpf/progs/test_tcp_custom_syncookie.c     | 79 +++++++++++++++++
 2 files changed, 159 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/bpf/prog_tests/tcp_custom_syncookie.c b/tools/testing/selftests/bpf/prog_tests/tcp_custom_syncookie.c
index eaf441dc7e79..c50b76f70988 100644
--- a/tools/testing/selftests/bpf/prog_tests/tcp_custom_syncookie.c
+++ b/tools/testing/selftests/bpf/prog_tests/tcp_custom_syncookie.c
@@ -5,6 +5,7 @@
 #include <sched.h>
 #include <stdlib.h>
 #include <net/if.h>
+#include <netinet/in.h>
 
 #include "test_progs.h"
 #include "cgroup_helpers.h"
@@ -47,11 +48,10 @@ static int setup_netns(void)
 	return -1;
 }
 
-static int setup_tc(struct test_tcp_custom_syncookie *skel)
+static int setup_tc(int prog_fd)
 {
 	LIBBPF_OPTS(bpf_tc_hook, qdisc_lo, .attach_point = BPF_TC_INGRESS);
-	LIBBPF_OPTS(bpf_tc_opts, tc_attach,
-		    .prog_fd = bpf_program__fd(skel->progs.tcp_custom_syncookie));
+	LIBBPF_OPTS(bpf_tc_opts, tc_attach, .prog_fd = prog_fd);
 
 	qdisc_lo.ifindex = if_nametoindex("lo");
 	if (!ASSERT_OK(bpf_tc_hook_create(&qdisc_lo), "qdisc add dev lo clsact"))
@@ -127,7 +127,7 @@ void test_tcp_custom_syncookie(void)
 	if (!ASSERT_OK_PTR(skel, "open_and_load"))
 		return;
 
-	if (setup_tc(skel))
+	if (setup_tc(bpf_program__fd(skel->progs.tcp_custom_syncookie)))
 		goto destroy_skel;
 
 	for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
@@ -145,6 +145,82 @@ void test_tcp_custom_syncookie(void)
 
 destroy_skel:
 	system("tc qdisc del dev lo clsact");
+	test_tcp_custom_syncookie__destroy(skel);
+}
 
+/* Test: bpf_sk_assign_tcp_reqsk() should reject non-TCP skb.
+ *
+ * Send a UDP packet through TC ingress where a BPF program calls
+ * bpf_sk_assign_tcp_reqsk() on it. The kfunc should return an error
+ * because the skb carries UDP, not TCP.
+ */
+void test_tcp_custom_syncookie_protocol_check(void)
+{
+	int tcp_server = -1, udp_server = -1, udp_client = -1;
+	struct test_tcp_custom_syncookie *skel;
+	struct sockaddr_in udp_addr;
+	char buf[32] = "test";
+	int udp_port, ret;
+
+	if (setup_netns())
+		return;
+
+	skel = test_tcp_custom_syncookie__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "open_and_load"))
+		return;
+
+	/* Create a TCP listener so the BPF can find a LISTEN socket */
+	tcp_server = start_server(AF_INET, SOCK_STREAM, "127.0.0.1", 0, 0);
+	if (!ASSERT_NEQ(tcp_server, -1, "start tcp_server"))
+		goto destroy_skel;
+
+	/* Create a UDP server to receive the packet as synchronization */
+	udp_server = start_server(AF_INET, SOCK_DGRAM, "127.0.0.1", 0, 0);
+	if (!ASSERT_NEQ(udp_server, -1, "start udp_server"))
+		goto close_tcp;
+
+	skel->bss->tcp_listener_port = ntohs(get_socket_local_port(tcp_server));
+	udp_port = ntohs(get_socket_local_port(udp_server));
+	skel->bss->udp_test_port = udp_port;
+
+	ret = bpf_program__fd(skel->progs.tcp_custom_syncookie_badproto);
+	if (setup_tc(ret))
+		goto close_udp_server;
+
+	udp_client = socket(AF_INET, SOCK_DGRAM, 0);
+	if (!ASSERT_NEQ(udp_client, -1, "udp socket"))
+		goto cleanup_tc;
+
+	memset(&udp_addr, 0, sizeof(udp_addr));
+	udp_addr.sin_family = AF_INET;
+	udp_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	udp_addr.sin_port = htons(udp_port);
+
+	ret = sendto(udp_client, buf, sizeof(buf), 0,
+		     (struct sockaddr *)&udp_addr, sizeof(udp_addr));
+	if (!ASSERT_EQ(ret, sizeof(buf), "sendto udp"))
+		goto cleanup_tc;
+
+	/* recv() ensures TC ingress BPF has processed the skb */
+	ret = recv(udp_server, buf, sizeof(buf), 0);
+	if (!ASSERT_EQ(ret, sizeof(buf), "recv udp"))
+		goto cleanup_tc;
+
+	ASSERT_EQ(skel->bss->udp_intercepted, true, "udp_intercepted");
+
+	/* assign_ret == 0 means kfunc accepted UDP skb (bug).
+	 * assign_ret < 0 means kfunc correctly rejected it (fixed).
+	 */
+	ASSERT_NEQ(skel->data->assign_ret, 0, "assign_ret");
+
+cleanup_tc:
+	system("tc qdisc del dev lo clsact");
+	if (udp_client >= 0)
+		close(udp_client);
+close_udp_server:
+	close(udp_server);
+close_tcp:
+	close(tcp_server);
+destroy_skel:
 	test_tcp_custom_syncookie__destroy(skel);
 }
diff --git a/tools/testing/selftests/bpf/progs/test_tcp_custom_syncookie.c b/tools/testing/selftests/bpf/progs/test_tcp_custom_syncookie.c
index 7d5293de1952..386705b6c9f2 100644
--- a/tools/testing/selftests/bpf/progs/test_tcp_custom_syncookie.c
+++ b/tools/testing/selftests/bpf/progs/test_tcp_custom_syncookie.c
@@ -588,4 +588,83 @@ int tcp_custom_syncookie(struct __sk_buff *skb)
 	return tcp_handle_ack(&ctx);
 }
 
+/* Test: call bpf_sk_assign_tcp_reqsk() on a UDP skb.
+ * The kfunc should reject it, but currently it doesn't check L4 protocol.
+ */
+__u16 tcp_listener_port = 0;
+__u16 udp_test_port = 0;
+int assign_ret = -1;
+bool udp_intercepted = false;
+
+SEC("tc")
+int tcp_custom_syncookie_badproto(struct __sk_buff *skb)
+{
+	void *data = (void *)(long)skb->data;
+	void *data_end = (void *)(long)skb->data_end;
+	struct bpf_sock_tuple tuple = {};
+	struct bpf_tcp_req_attrs attrs = {};
+	struct ethhdr *eth;
+	struct iphdr *iph;
+	struct udphdr *udp;
+	struct bpf_sock *skc;
+	struct sock *sk;
+
+	eth = (struct ethhdr *)data;
+	if (eth + 1 > data_end)
+		return TC_ACT_OK;
+
+	if (bpf_ntohs(eth->h_proto) != ETH_P_IP)
+		return TC_ACT_OK;
+
+	iph = (struct iphdr *)(eth + 1);
+	if (iph + 1 > data_end)
+		return TC_ACT_OK;
+
+	if (iph->protocol != IPPROTO_UDP)
+		return TC_ACT_OK;
+
+	udp = (struct udphdr *)(iph + 1);
+	if (udp + 1 > data_end)
+		return TC_ACT_OK;
+
+	if (bpf_ntohs(udp->dest) != udp_test_port)
+		return TC_ACT_OK;
+
+	udp_intercepted = true;
+
+	tuple.ipv4.saddr = iph->saddr;
+	tuple.ipv4.daddr = iph->daddr;
+	tuple.ipv4.sport = udp->source;
+	tuple.ipv4.dport = bpf_htons(tcp_listener_port);
+
+	skc = bpf_skc_lookup_tcp(skb, &tuple, sizeof(tuple.ipv4), -1, 0);
+	if (!skc)
+		return TC_ACT_OK;
+
+	if (skc->state != TCP_LISTEN) {
+		bpf_sk_release(skc);
+		return TC_ACT_OK;
+	}
+
+	sk = (struct sock *)bpf_skc_to_tcp_sock(skc);
+	if (!sk) {
+		bpf_sk_release(skc);
+		return TC_ACT_OK;
+	}
+
+	attrs.mss = 1460;
+	attrs.wscale_ok = 1;
+	attrs.snd_wscale = 7;
+	attrs.rcv_wscale = 7;
+	attrs.sack_ok = 1;
+
+	/* Call bpf_sk_assign_tcp_reqsk on a UDP skb. */
+	assign_ret = bpf_sk_assign_tcp_reqsk(skb, sk, &attrs, sizeof(attrs));
+
+	bpf_sk_release(skc);
+
+	/* Let the packet continue into the kernel */
+	return TC_ACT_OK;
+}
+
 char _license[] SEC("license") = "GPL";
-- 
2.43.0


  parent reply	other threads:[~2026-03-26  6:27 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-03-26  6:26 [PATCH bpf v2 0/2] bpf: tcp: Fix null-ptr-deref in arbitrary SYN Cookie Jiayuan Chen
2026-03-26  6:26 ` [PATCH bpf v2 1/2] bpf: tcp: Reject non-TCP skb in bpf_sk_assign_tcp_reqsk() Jiayuan Chen
2026-03-26  6:26 ` Jiayuan Chen [this message]
2026-03-26 17:33   ` [PATCH bpf v2 2/2] selftests/bpf: Add protocol check test for bpf_sk_assign_tcp_reqsk() Kuniyuki Iwashima
2026-03-27  1:52     ` Jiayuan Chen

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=20260326062657.88446-3-jiayuan.chen@linux.dev \
    --to=jiayuan.chen@linux.dev \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=davem@davemloft.net \
    --cc=eddyz87@gmail.com \
    --cc=edumazet@google.com \
    --cc=haoluo@google.com \
    --cc=horms@kernel.org \
    --cc=jiayuan.chen@shopee.com \
    --cc=john.fastabend@gmail.com \
    --cc=jolsa@kernel.org \
    --cc=kpsingh@kernel.org \
    --cc=kuba@kernel.org \
    --cc=kuniyu@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=martin.lau@linux.dev \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=sdf@fomichev.me \
    --cc=shuah@kernel.org \
    --cc=song@kernel.org \
    --cc=yonghong.song@linux.dev \
    /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 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.