All of lore.kernel.org
 help / color / mirror / Atom feed
From: Yonghong Song <yonghong.song@linux.dev>
To: bpf@vger.kernel.org
Cc: Alexei Starovoitov <ast@kernel.org>,
	Andrii Nakryiko <andrii@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>,
	kernel-team@fb.com, Martin KaFai Lau <martin.lau@kernel.org>
Subject: [PATCH bpf-next 2/2] selftests/bpf: Add a test with bpf_unreachable() kfunc
Date: Sun, 11 May 2025 11:27:49 -0700	[thread overview]
Message-ID: <20250511182749.1807380-1-yonghong.song@linux.dev> (raw)
In-Reply-To: <20250511182744.1806792-1-yonghong.song@linux.dev>

The test case is from [1] reported by Marc Suñé. When compiled with [2],
the object code looks like:

  0000000000000000 <repro>:
  ; {
       0:       bf 16 00 00 00 00 00 00 r6 = r1
  ;       bpf_printk("Start");
       1:       18 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r1 = 0x0 ll
                0000000000000008:  R_BPF_64_64  .rodata
       3:       b4 02 00 00 06 00 00 00 w2 = 0x6
       4:       85 00 00 00 06 00 00 00 call 0x6
  ; DEFINE_FUNC_CTX_POINTER(data)
       5:       61 61 4c 00 00 00 00 00 w1 = *(u32 *)(r6 + 0x4c)
  ;       bpf_printk("pre ipv6_hdrlen_offset");
       6:       18 01 00 00 06 00 00 00 00 00 00 00 00 00 00 00 r1 = 0x6 ll
                0000000000000030:  R_BPF_64_64  .rodata
       8:       b4 02 00 00 17 00 00 00 w2 = 0x17
       9:       85 00 00 00 06 00 00 00 call 0x6
      10:       85 10 00 00 ff ff ff ff call -0x1
                0000000000000050:  R_BPF_64_32  bpf_unreachable

You can see the last insn is bpf_unreachable() func and bpf verifier
can take advantage of such information and emits the following error
message:
  (85) call bpf_unreachable#74465
  last insn is bpf_unreachable, due to uninitialized var?

  [1] https://github.com/msune/clang_bpf/blob/main/Makefile#L3
  [2] https://github.com/llvm/llvm-project/pull/131731

Signed-off-by: Yonghong Song <yonghong.song@linux.dev>
---
 .../selftests/bpf/prog_tests/verifier.c       |   2 +
 .../selftests/bpf/progs/verifier_uninit_var.c | 203 ++++++++++++++++++
 2 files changed, 205 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/progs/verifier_uninit_var.c

diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index e66a57970d28..bc2765d130c0 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -87,6 +87,7 @@
 #include "verifier_tailcall_jit.skel.h"
 #include "verifier_typedef.skel.h"
 #include "verifier_uninit.skel.h"
+#include "verifier_uninit_var.skel.h"
 #include "verifier_unpriv.skel.h"
 #include "verifier_unpriv_perf.skel.h"
 #include "verifier_value_adj_spill.skel.h"
@@ -220,6 +221,7 @@ void test_verifier_subreg(void)               { RUN(verifier_subreg); }
 void test_verifier_tailcall_jit(void)         { RUN(verifier_tailcall_jit); }
 void test_verifier_typedef(void)              { RUN(verifier_typedef); }
 void test_verifier_uninit(void)               { RUN(verifier_uninit); }
+void test_verifier_uninit_var(void)           { RUN(verifier_uninit_var); }
 void test_verifier_unpriv(void)               { RUN(verifier_unpriv); }
 void test_verifier_unpriv_perf(void)          { RUN(verifier_unpriv_perf); }
 void test_verifier_value_adj_spill(void)      { RUN(verifier_value_adj_spill); }
diff --git a/tools/testing/selftests/bpf/progs/verifier_uninit_var.c b/tools/testing/selftests/bpf/progs/verifier_uninit_var.c
new file mode 100644
index 000000000000..5ec01b492623
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_uninit_var.c
@@ -0,0 +1,203 @@
+#include <linux/bpf.h>
+#include <linux/pkt_cls.h>
+#include <linux/if_ether.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/udp.h>
+#include <linux/tcp.h>
+#include <stdbool.h>
+#include <linux/icmpv6.h>
+#include <linux/in.h>
+
+#include <bpf/bpf_endian.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include "bpf_misc.h"
+
+union macaddr {
+	struct {
+		__u32 p1;
+		__u16 p2;
+	};
+	__u8 addr[6];
+};
+
+union v6addr {
+	struct {
+		__u32 p1;
+		__u32 p2;
+		__u32 p3;
+		__u32 p4;
+	};
+	struct {
+		__u64 d1;
+		__u64 d2;
+	};
+	__u8 addr[16];
+};
+
+/* Number of extension headers that can be skipped */
+#define IPV6_MAX_HEADERS 4
+
+#define NEXTHDR_HOP             0       /* Hop-by-hop option header. */
+#define NEXTHDR_TCP             6       /* TCP segment. */
+#define NEXTHDR_UDP             17      /* UDP message. */
+#define NEXTHDR_IPV6            41      /* IPv6 in IPv6 */
+#define NEXTHDR_ROUTING         43      /* Routing header. */
+#define NEXTHDR_FRAGMENT        44      /* Fragmentation/reassembly header. */
+#define NEXTHDR_GRE             47      /* GRE header. */
+#define NEXTHDR_ESP             50      /* Encapsulating security payload. */
+#define NEXTHDR_AUTH            51      /* Authentication header. */
+#define NEXTHDR_ICMP            58      /* ICMP for IPv6. */
+#define NEXTHDR_NONE            59      /* No next header */
+#define NEXTHDR_DEST            60      /* Destination options header. */
+#define NEXTHDR_SCTP            132     /* SCTP message. */
+#define NEXTHDR_MOBILITY        135     /* Mobility header. */
+
+#define NEXTHDR_MAX             255
+
+#define IPV6_SADDR_OFF		offsetof(struct ipv6hdr, saddr)
+#define IPV6_DADDR_OFF		offsetof(struct ipv6hdr, daddr)
+
+#define NEXTHDR_ICMP 58
+#define ICMP6_NS_MSG_TYPE		135
+
+#define DROP_INVALID		-134
+#define DROP_INVALID_EXTHDR	-156
+#define DROP_FRAG_NOSUPPORT	-157
+
+#define ctx_load_bytes		skb_load_bytes
+
+#define DEFINE_FUNC_CTX_POINTER(FIELD)						\
+static __always_inline void *							\
+ctx_ ## FIELD(const struct __sk_buff *ctx)					\
+{										\
+	void *ptr;								\
+										\
+	/* LLVM may generate u32 assignments of ctx->{data,data_end,data_meta}.	\
+	 * With this inline asm, LLVM loses track of the fact this field is on	\
+	 * 32 bits.								\
+	 */									\
+	asm volatile("%0 = *(u32 *)(%1 + %2)"					\
+		     : "=r"(ptr)						\
+		     : "r"(ctx), "i"(offsetof(struct __sk_buff, FIELD)));	\
+	return ptr;								\
+}
+/* This defines ctx_data(). */
+DEFINE_FUNC_CTX_POINTER(data)
+#undef DEFINE_FUNC_CTX_POINTER
+
+
+static __always_inline int ipv6_optlen(const struct ipv6_opt_hdr *opthdr)
+{
+	return (opthdr->hdrlen + 1) << 3;
+}
+
+static __always_inline int ipv6_authlen(const struct ipv6_opt_hdr *opthdr)
+{
+	return (opthdr->hdrlen + 2) << 2;
+}
+
+static __always_inline int ipv6_hdrlen_offset(struct __sk_buff *ctx,
+					      __u8 *nexthdr, int l3_off)
+{
+	int i, len = sizeof(struct ipv6hdr);
+	struct ipv6_opt_hdr opthdr __attribute__((aligned(8)));
+	__u8 nh = *nexthdr;
+
+#pragma unroll
+	for (i = 0; i < IPV6_MAX_HEADERS; i++) {
+		switch (nh) {
+		case NEXTHDR_NONE:
+			return DROP_INVALID_EXTHDR;
+
+		case NEXTHDR_FRAGMENT:
+			return DROP_FRAG_NOSUPPORT;
+
+		case NEXTHDR_HOP:
+		case NEXTHDR_ROUTING:
+		case NEXTHDR_AUTH:
+		case NEXTHDR_DEST:
+			if (bpf_skb_load_bytes(ctx, l3_off + len, &opthdr,
+					   sizeof(opthdr)) < 0)
+				return DROP_INVALID;
+
+			if (nh == NEXTHDR_AUTH)
+				len += ipv6_authlen(&opthdr);
+			else
+				len += ipv6_optlen(&opthdr);
+
+			nh = opthdr.nexthdr;
+			break;
+
+		default:
+			bpf_printk("OKOK %d, len: %d", *nexthdr, len);
+			*nexthdr = nh;
+			return len;
+		}
+	}
+
+	bpf_printk("KO INVALID EXTHDR");
+
+	/* Reached limit of supported extension headers */
+	return DROP_INVALID_EXTHDR;
+}
+static __always_inline
+bool icmp6_ndisc_validate(struct __sk_buff *ctx, struct ipv6hdr *ip6,
+			  union macaddr *mac, union macaddr *smac,
+			  union v6addr *sip, union v6addr *tip)
+{
+	__u8 nexthdr;
+	struct icmp6hdr *icmp;
+	int l3_off, l4_off;
+
+	l3_off = (__u8 *)ip6 - (__u8 *)ctx_data(ctx);
+	bpf_printk("pre ipv6_hdrlen_offset");
+	l4_off = ipv6_hdrlen_offset(ctx, &nexthdr, l3_off);
+	bpf_printk("post ipv6_hdrlen_offset");
+
+	if (l4_off < 0 || nexthdr != NEXTHDR_ICMP) {
+		bpf_printk("KO 1");
+		return false;
+	}
+
+	icmp = (struct icmp6hdr *)((__u8 *)ctx_data(ctx) + l4_off);
+	if (icmp->icmp6_type != ICMP6_NS_MSG_TYPE) {
+		bpf_printk("KO 2");
+		return false;
+	}
+
+	/* Extract fields */
+#if 0
+	eth_load_saddr(ctx, &smac->addr[0], 0);
+	eth_load_daddr(ctx, &mac->addr[0], 0);
+	ipv6_load_saddr(ctx, l3_off, sip);
+	ipv6_load_daddr(ctx, l3_off, tip);
+#endif
+	bpf_printk("ACK ");
+
+	return true;
+}
+
+SEC("classifier")
+__description("agressive optimization due to uninitialized variable")
+#if __clang_major__ >= 21
+__failure __msg("last insn is bpf_unreachable, due to uninitialized var")
+#else
+__failure __msg("last insn is not an exit or jmp")
+#endif
+int classifier_uninit_var(struct __sk_buff *skb)
+{
+	struct ipv6hdr* ip6 = NULL;
+	union macaddr mac, smac;
+	union v6addr sip, tip;
+
+	bpf_printk("Start");
+	icmp6_ndisc_validate(skb, ip6, &mac, &smac, &sip, &tip);
+	bpf_printk("End");
+
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
-- 
2.47.1


  reply	other threads:[~2025-05-11 18:28 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-05-11 18:27 [PATCH bpf-next 1/2] bpf: Warn with new bpf_unreachable() kfunc maybe due to uninitialized var Yonghong Song
2025-05-11 18:27 ` Yonghong Song [this message]
2025-05-12  0:30 ` Alexei Starovoitov
2025-05-13  5:49   ` Yonghong Song
2025-05-13 14:54     ` Alexei Starovoitov
2025-05-13 18:05       ` Yonghong Song

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=20250511182749.1807380-1-yonghong.song@linux.dev \
    --to=yonghong.song@linux.dev \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=kernel-team@fb.com \
    --cc=martin.lau@kernel.org \
    /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.