public inbox for bpf@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf v3 0/2] bpf: reject negative CO-RE accessor indices
@ 2026-04-04 16:12 Weiming Shi
  2026-04-04 16:12 ` [PATCH bpf v3 1/2] bpf: reject negative CO-RE accessor indices in bpf_core_parse_spec() Weiming Shi
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: Weiming Shi @ 2026-04-04 16:12 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, bpf,
	Xiang Mei, Weiming Shi

bpf_core_parse_spec() parses CO-RE accessor strings with sscanf("%d"),
which accepts negative values.  The downstream bounds checks only test
the upper bound, so a negative index like -1 slips through, gets cast
to u32 0xffffffff in btf_member_bit_offset(), and crashes the kernel.

To clarify the kernel-side concern from v2 review:
tools/lib/bpf/relo_core.c is shared code -- the kernel compiles it
directly via kernel/bpf/relo_core.c (#include).  So the fix does
apply in the kernel's BPF_PROG_LOAD -> check_core_relo() ->
bpf_core_apply() -> bpf_core_parse_spec() path.

v3: added selftest (patch 2/2)
v2: fix typo Signed-off-by tag (missing leading 'S')

Weiming Shi (2):
  bpf: reject negative CO-RE accessor indices in bpf_core_parse_spec()
  selftests/bpf: add test for negative CO-RE accessor index rejection

 tools/lib/bpf/relo_core.c                     |  2 +
 .../selftests/bpf/prog_tests/core_reloc_raw.c | 94 +++++++++++++++++++
 2 files changed, 96 insertions(+)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH bpf v3 1/2] bpf: reject negative CO-RE accessor indices in bpf_core_parse_spec()
  2026-04-04 16:12 [PATCH bpf v3 0/2] bpf: reject negative CO-RE accessor indices Weiming Shi
@ 2026-04-04 16:12 ` Weiming Shi
  2026-04-04 18:07   ` Emil Tsalapatis
  2026-04-06 15:01   ` Paul Chaignon
  2026-04-04 16:12 ` [PATCH bpf v3 2/2] selftests/bpf: add test for negative CO-RE accessor index rejection Weiming Shi
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 9+ messages in thread
From: Weiming Shi @ 2026-04-04 16:12 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, bpf,
	Xiang Mei, Weiming Shi

CO-RE accessor strings are colon-separated indices that describe a path
from a root BTF type to a target field, e.g. "0:1:2" walks through
nested struct members. bpf_core_parse_spec() parses each component with
sscanf("%d"), so negative values like -1 are silently accepted.  The
subsequent bounds checks (access_idx >= btf_vlen(t)) only guard the
upper bound and always pass for negative values because C integer
promotion converts the __u16 btf_vlen result to int, making the
comparison (int)(-1) >= (int)(N) false for any positive N.

When -1 reaches btf_member_bit_offset() it gets cast to u32 0xffffffff,
producing an out-of-bounds read far past the members array.  A crafted
BPF program with a negative CO-RE accessor on any struct that exists in
vmlinux BTF (e.g. task_struct) crashes the kernel deterministically
during BPF_PROG_LOAD on any system with CONFIG_DEBUG_INFO_BTF=y
(default on major distributions).  The bug is reachable with CAP_BPF:

 BUG: unable to handle page fault for address: ffffed11818b6626
 #PF: supervisor read access in kernel mode
 #PF: error_code(0x0000) - not-present page
 Oops: Oops: 0000 [#1] SMP KASAN NOPTI
 CPU: 0 UID: 0 PID: 85 Comm: poc Not tainted 7.0.0-rc6 #18 PREEMPT(full)
 RIP: 0010:bpf_core_parse_spec (tools/lib/bpf/relo_core.c:354)
 RAX: 00000000ffffffff
 Call Trace:
  <TASK>
  bpf_core_calc_relo_insn (tools/lib/bpf/relo_core.c:1321)
  bpf_core_apply (kernel/bpf/btf.c:9507)
  check_core_relo (kernel/bpf/verifier.c:19475)
  bpf_check (kernel/bpf/verifier.c:26031)
  bpf_prog_load (kernel/bpf/syscall.c:3089)
  __sys_bpf (kernel/bpf/syscall.c:6228)
  </TASK>

CO-RE accessor indices are inherently non-negative (struct member index,
array element index, or enumerator index), so reject them immediately
after parsing.

Fixes: ddc7c3042614 ("libbpf: implement BPF CO-RE offset relocation algorithm")
Reported-by: Xiang Mei <xmei5@asu.edu>
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
---
v3: added selftest 
v2: fix typo Signed-off-by tag (missing leading 'S')

 tools/lib/bpf/relo_core.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tools/lib/bpf/relo_core.c b/tools/lib/bpf/relo_core.c
index 6eea5edba58a..0ccc8f548cba 100644
--- a/tools/lib/bpf/relo_core.c
+++ b/tools/lib/bpf/relo_core.c
@@ -292,6 +292,8 @@ int bpf_core_parse_spec(const char *prog_name, const struct btf *btf,
 			++spec_str;
 		if (sscanf(spec_str, "%d%n", &access_idx, &parsed_len) != 1)
 			return -EINVAL;
+		if (access_idx < 0)
+			return -EINVAL;
 		if (spec->raw_len == BPF_CORE_SPEC_MAX_LEN)
 			return -E2BIG;
 		spec_str += parsed_len;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH bpf v3 2/2] selftests/bpf: add test for negative CO-RE accessor index rejection
  2026-04-04 16:12 [PATCH bpf v3 0/2] bpf: reject negative CO-RE accessor indices Weiming Shi
  2026-04-04 16:12 ` [PATCH bpf v3 1/2] bpf: reject negative CO-RE accessor indices in bpf_core_parse_spec() Weiming Shi
@ 2026-04-04 16:12 ` Weiming Shi
  2026-04-04 18:12   ` Emil Tsalapatis
  2026-04-06 15:03   ` Paul Chaignon
  2026-04-04 18:06 ` [PATCH bpf v3 0/2] bpf: reject negative CO-RE accessor indices Emil Tsalapatis
  2026-04-07 15:30 ` patchwork-bot+netdevbpf
  3 siblings, 2 replies; 9+ messages in thread
From: Weiming Shi @ 2026-04-04 16:12 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, bpf,
	Xiang Mei, Weiming Shi

Add a selftest to verify that the kernel rejects BPF programs containing
CO-RE relocations with negative accessor indices.  The test constructs a
minimal BTF blob with a struct type and a CO-RE relocation whose access
string is "0:-1".  Without the fix in the previous patch, this triggers
an out-of-bounds read in bpf_core_parse_spec(); with the fix, the
program load fails cleanly with -EINVAL.

Signed-off-by: Weiming Shi <bestswngs@gmail.com>
---
 .../selftests/bpf/prog_tests/core_reloc_raw.c | 94 +++++++++++++++++++
 1 file changed, 94 insertions(+)

diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c b/tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c
index a18d3680fb16..f7bf6c237d59 100644
--- a/tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c
+++ b/tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c
@@ -118,8 +118,102 @@ static void test_bad_local_id(void)
 	close(btf_fd);
 }
 
+/* Check that verifier rejects BPF program containing CO-RE relocation
+ * with a negative accessor index (e.g. "0:-1").
+ */
+static void test_negative_accessor(void)
+{
+	struct test_btf {
+		struct btf_header hdr;
+		__u32 types[18];
+		char strings[20];
+	} raw_btf = {
+		.hdr = {
+			.magic = BTF_MAGIC,
+			.version = BTF_VERSION,
+			.hdr_len = sizeof(struct btf_header),
+			.type_off = 0,
+			.type_len = sizeof(raw_btf.types),
+			.str_off = offsetof(struct test_btf, strings) -
+				   offsetof(struct test_btf, types),
+			.str_len = sizeof(raw_btf.strings),
+		},
+		.types = {
+			/* [1] int */
+			BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4),
+			/* [2] struct s { int x; } */
+			BTF_STRUCT_ENC(5, 1, 4),
+			BTF_MEMBER_ENC(7, 1, 0),
+			/* [3] int (*)(int a) */
+			BTF_FUNC_PROTO_ENC(1, 1),
+			BTF_FUNC_PROTO_ARG_ENC(13, 1),
+			/* [4] FUNC 'foo' */
+			BTF_FUNC_ENC(9, 3),
+		},
+		/* offsets: 0:NUL 1:"int" 5:"s" 7:"x" 9:"foo" 13:"a" 15:"0:-1" */
+		.strings = "\0int\0s\0x\0foo\0a\0" "0:-1",
+	};
+	__u32 log_level = 1 | 2 | 4;
+	LIBBPF_OPTS(bpf_btf_load_opts, opts,
+		    .log_buf = log,
+		    .log_size = sizeof(log),
+		    .log_level = log_level,
+	);
+	struct bpf_insn insns[] = {
+		BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 0),
+		BPF_EXIT_INSN(),
+	};
+	struct bpf_func_info funcs[] = {
+		{ .insn_off = 0, .type_id = 4 }
+	};
+	struct bpf_core_relo relos[] = {
+		{
+			.insn_off = 0,
+			.type_id = 2,		/* struct s */
+			.access_str_off = 15,	/* "0:-1" */
+			.kind = BPF_CORE_FIELD_BYTE_OFFSET,
+		}
+	};
+	union bpf_attr attr;
+	int prog_fd = -1;
+	int btf_fd = -1;
+
+	btf_fd = bpf_btf_load(&raw_btf, sizeof(raw_btf), &opts);
+	if (!ASSERT_GE(btf_fd, 0, "btf_load"))
+		return;
+
+	log[0] = 0;
+	memset(&attr, 0, sizeof(attr));
+	attr.prog_btf_fd = btf_fd;
+	attr.prog_type = BPF_TRACE_RAW_TP;
+	attr.license = (__u64)"GPL";
+	attr.insns = (__u64)&insns;
+	attr.insn_cnt = sizeof(insns) / sizeof(*insns);
+	attr.log_buf = (__u64)log;
+	attr.log_size = sizeof(log);
+	attr.log_level = log_level;
+	attr.func_info = (__u64)funcs;
+	attr.func_info_cnt = sizeof(funcs) / sizeof(*funcs);
+	attr.func_info_rec_size = sizeof(*funcs);
+	attr.core_relos = (__u64)relos;
+	attr.core_relo_cnt = sizeof(relos) / sizeof(*relos);
+	attr.core_relo_rec_size = sizeof(*relos);
+	prog_fd = sys_bpf_prog_load(&attr, sizeof(attr), 1);
+	if (prog_fd >= 0) {
+		PRINT_FAIL("sys_bpf_prog_load() expected to fail\n");
+		goto out;
+	}
+	ASSERT_HAS_SUBSTR(log, "failed: -22", "prog_load_log");
+
+out:
+	close(prog_fd);
+	close(btf_fd);
+}
+
 void test_core_reloc_raw(void)
 {
 	if (test__start_subtest("bad_local_id"))
 		test_bad_local_id();
+	if (test__start_subtest("negative_accessor"))
+		test_negative_accessor();
 }
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH bpf v3 0/2] bpf: reject negative CO-RE accessor indices
  2026-04-04 16:12 [PATCH bpf v3 0/2] bpf: reject negative CO-RE accessor indices Weiming Shi
  2026-04-04 16:12 ` [PATCH bpf v3 1/2] bpf: reject negative CO-RE accessor indices in bpf_core_parse_spec() Weiming Shi
  2026-04-04 16:12 ` [PATCH bpf v3 2/2] selftests/bpf: add test for negative CO-RE accessor index rejection Weiming Shi
@ 2026-04-04 18:06 ` Emil Tsalapatis
  2026-04-07 15:30 ` patchwork-bot+netdevbpf
  3 siblings, 0 replies; 9+ messages in thread
From: Emil Tsalapatis @ 2026-04-04 18:06 UTC (permalink / raw)
  To: Weiming Shi, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, bpf,
	Xiang Mei

On Sat Apr 4, 2026 at 12:12 PM EDT, Weiming Shi wrote:
> bpf_core_parse_spec() parses CO-RE accessor strings with sscanf("%d"),
> which accepts negative values.  The downstream bounds checks only test
> the upper bound, so a negative index like -1 slips through, gets cast
> to u32 0xffffffff in btf_member_bit_offset(), and crashes the kernel.
>
> To clarify the kernel-side concern from v2 review:
> tools/lib/bpf/relo_core.c is shared code -- the kernel compiles it
> directly via kernel/bpf/relo_core.c (#include).  So the fix does
> apply in the kernel's BPF_PROG_LOAD -> check_core_relo() ->
> bpf_core_apply() -> bpf_core_parse_spec() path.
>

Since this text ends up in the git log, I think you can remove this
paragraph - especially since there is no permalink in the version
history below.

> v3: added selftest (patch 2/2)
> v2: fix typo Signed-off-by tag (missing leading 'S')
>
> Weiming Shi (2):
>   bpf: reject negative CO-RE accessor indices in bpf_core_parse_spec()
>   selftests/bpf: add test for negative CO-RE accessor index rejection
>
>  tools/lib/bpf/relo_core.c                     |  2 +
>  .../selftests/bpf/prog_tests/core_reloc_raw.c | 94 +++++++++++++++++++
>  2 files changed, 96 insertions(+)


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH bpf v3 1/2] bpf: reject negative CO-RE accessor indices in bpf_core_parse_spec()
  2026-04-04 16:12 ` [PATCH bpf v3 1/2] bpf: reject negative CO-RE accessor indices in bpf_core_parse_spec() Weiming Shi
@ 2026-04-04 18:07   ` Emil Tsalapatis
  2026-04-06 15:01   ` Paul Chaignon
  1 sibling, 0 replies; 9+ messages in thread
From: Emil Tsalapatis @ 2026-04-04 18:07 UTC (permalink / raw)
  To: Weiming Shi, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, bpf,
	Xiang Mei

On Sat Apr 4, 2026 at 12:12 PM EDT, Weiming Shi wrote:
> CO-RE accessor strings are colon-separated indices that describe a path
> from a root BTF type to a target field, e.g. "0:1:2" walks through
> nested struct members. bpf_core_parse_spec() parses each component with
> sscanf("%d"), so negative values like -1 are silently accepted.  The
> subsequent bounds checks (access_idx >= btf_vlen(t)) only guard the
> upper bound and always pass for negative values because C integer
> promotion converts the __u16 btf_vlen result to int, making the
> comparison (int)(-1) >= (int)(N) false for any positive N.
>
> When -1 reaches btf_member_bit_offset() it gets cast to u32 0xffffffff,
> producing an out-of-bounds read far past the members array.  A crafted
> BPF program with a negative CO-RE accessor on any struct that exists in
> vmlinux BTF (e.g. task_struct) crashes the kernel deterministically
> during BPF_PROG_LOAD on any system with CONFIG_DEBUG_INFO_BTF=y
> (default on major distributions).  The bug is reachable with CAP_BPF:
>
>  BUG: unable to handle page fault for address: ffffed11818b6626
>  #PF: supervisor read access in kernel mode
>  #PF: error_code(0x0000) - not-present page
>  Oops: Oops: 0000 [#1] SMP KASAN NOPTI
>  CPU: 0 UID: 0 PID: 85 Comm: poc Not tainted 7.0.0-rc6 #18 PREEMPT(full)
>  RIP: 0010:bpf_core_parse_spec (tools/lib/bpf/relo_core.c:354)
>  RAX: 00000000ffffffff
>  Call Trace:
>   <TASK>
>   bpf_core_calc_relo_insn (tools/lib/bpf/relo_core.c:1321)
>   bpf_core_apply (kernel/bpf/btf.c:9507)
>   check_core_relo (kernel/bpf/verifier.c:19475)
>   bpf_check (kernel/bpf/verifier.c:26031)
>   bpf_prog_load (kernel/bpf/syscall.c:3089)
>   __sys_bpf (kernel/bpf/syscall.c:6228)
>   </TASK>
>
> CO-RE accessor indices are inherently non-negative (struct member index,
> array element index, or enumerator index), so reject them immediately
> after parsing.
>
> Fixes: ddc7c3042614 ("libbpf: implement BPF CO-RE offset relocation algorithm")
> Reported-by: Xiang Mei <xmei5@asu.edu>
> Signed-off-by: Weiming Shi <bestswngs@gmail.com>

Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>

> ---
> v3: added selftest 
> v2: fix typo Signed-off-by tag (missing leading 'S')
>
>  tools/lib/bpf/relo_core.c | 2 ++
>  1 file changed, 2 insertions(+)
>
> diff --git a/tools/lib/bpf/relo_core.c b/tools/lib/bpf/relo_core.c
> index 6eea5edba58a..0ccc8f548cba 100644
> --- a/tools/lib/bpf/relo_core.c
> +++ b/tools/lib/bpf/relo_core.c
> @@ -292,6 +292,8 @@ int bpf_core_parse_spec(const char *prog_name, const struct btf *btf,
>  			++spec_str;
>  		if (sscanf(spec_str, "%d%n", &access_idx, &parsed_len) != 1)
>  			return -EINVAL;
> +		if (access_idx < 0)
> +			return -EINVAL;
>  		if (spec->raw_len == BPF_CORE_SPEC_MAX_LEN)
>  			return -E2BIG;
>  		spec_str += parsed_len;


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH bpf v3 2/2] selftests/bpf: add test for negative CO-RE accessor index rejection
  2026-04-04 16:12 ` [PATCH bpf v3 2/2] selftests/bpf: add test for negative CO-RE accessor index rejection Weiming Shi
@ 2026-04-04 18:12   ` Emil Tsalapatis
  2026-04-06 15:03   ` Paul Chaignon
  1 sibling, 0 replies; 9+ messages in thread
From: Emil Tsalapatis @ 2026-04-04 18:12 UTC (permalink / raw)
  To: Weiming Shi, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi
  Cc: Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, bpf,
	Xiang Mei

On Sat Apr 4, 2026 at 12:12 PM EDT, Weiming Shi wrote:
> Add a selftest to verify that the kernel rejects BPF programs containing
> CO-RE relocations with negative accessor indices.  The test constructs a
> minimal BTF blob with a struct type and a CO-RE relocation whose access
> string is "0:-1".  Without the fix in the previous patch, this triggers
> an out-of-bounds read in bpf_core_parse_spec(); with the fix, the
> program load fails cleanly with -EINVAL.
>
> Signed-off-by: Weiming Shi <bestswngs@gmail.com>

Thank you for adding the selftest. Some nits below, but otherwise:

Reviewed-by: Emil Tsalapatis <emil@etsalapatis.com>

> ---
>  .../selftests/bpf/prog_tests/core_reloc_raw.c | 94 +++++++++++++++++++
>  1 file changed, 94 insertions(+)
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c b/tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c
> index a18d3680fb16..f7bf6c237d59 100644
> --- a/tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c
> +++ b/tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c
> @@ -118,8 +118,102 @@ static void test_bad_local_id(void)
>  	close(btf_fd);
>  }
>  
> +/* Check that verifier rejects BPF program containing CO-RE relocation
> + * with a negative accessor index (e.g. "0:-1").
> + */
> +static void test_negative_accessor(void)
> +{
> +	struct test_btf {
> +		struct btf_header hdr;
> +		__u32 types[18];
> +		char strings[20];
> +	} raw_btf = {
> +		.hdr = {
> +			.magic = BTF_MAGIC,
> +			.version = BTF_VERSION,
> +			.hdr_len = sizeof(struct btf_header),
> +			.type_off = 0,
> +			.type_len = sizeof(raw_btf.types),
> +			.str_off = offsetof(struct test_btf, strings) -
> +				   offsetof(struct test_btf, types),
> +			.str_len = sizeof(raw_btf.strings),
> +		},
> +		.types = {
> +			/* [1] int */
> +			BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4),
> +			/* [2] struct s { int x; } */
> +			BTF_STRUCT_ENC(5, 1, 4),
> +			BTF_MEMBER_ENC(7, 1, 0),
> +			/* [3] int (*)(int a) */
> +			BTF_FUNC_PROTO_ENC(1, 1),
> +			BTF_FUNC_PROTO_ARG_ENC(13, 1),

Nit: If you make this BPF_PROG_TYPE_TRACING (see below) you can change this
to be void(void) and skip the extra member.

> +			/* [4] FUNC 'foo' */
> +			BTF_FUNC_ENC(9, 3),
> +		},
> +		/* offsets: 0:NUL 1:"int" 5:"s" 7:"x" 9:"foo" 13:"a" 15:"0:-1" */
> +		.strings = "\0int\0s\0x\0foo\0a\0" "0:-1",
> +	};
> +	__u32 log_level = 1 | 2 | 4;
> +	LIBBPF_OPTS(bpf_btf_load_opts, opts,
> +		    .log_buf = log,
> +		    .log_size = sizeof(log),
> +		    .log_level = log_level,
> +	);
> +	struct bpf_insn insns[] = {
> +		BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 0),
> +		BPF_EXIT_INSN(),
> +	};
> +	struct bpf_func_info funcs[] = {
> +		{ .insn_off = 0, .type_id = 4 }
> +	};
> +	struct bpf_core_relo relos[] = {
> +		{
> +			.insn_off = 0,
> +			.type_id = 2,		/* struct s */
> +			.access_str_off = 15,	/* "0:-1" */
> +			.kind = BPF_CORE_FIELD_BYTE_OFFSET,
> +		}
> +	};
> +	union bpf_attr attr;
> +	int prog_fd = -1;
> +	int btf_fd = -1;
> +
> +	btf_fd = bpf_btf_load(&raw_btf, sizeof(raw_btf), &opts);
> +	if (!ASSERT_GE(btf_fd, 0, "btf_load"))
> +		return;
> +
> +	log[0] = 0;

No need for setting 0, the log is guaranteed to be NULL-terminated
after load() is called provided the user running the selftests has
the appropriate privilege.

> +	memset(&attr, 0, sizeof(attr));
> +	attr.prog_btf_fd = btf_fd;
> +	attr.prog_type = BPF_TRACE_RAW_TP;

As the bot in sashiko.dev points out, this is an attach type and not a
load type. Can you change it so BPF_PROG_TYPE_TRACING? The same holds
for the pre-existing test that has the same issue - I think a drive-by
fix is fine in this case as long as you mention it in the commit
message.

> +	attr.license = (__u64)"GPL";
> +	attr.insns = (__u64)&insns;
> +	attr.insn_cnt = sizeof(insns) / sizeof(*insns);
> +	attr.log_buf = (__u64)log;
> +	attr.log_size = sizeof(log);
> +	attr.log_level = log_level;
> +	attr.func_info = (__u64)funcs;
> +	attr.func_info_cnt = sizeof(funcs) / sizeof(*funcs);
> +	attr.func_info_rec_size = sizeof(*funcs);
> +	attr.core_relos = (__u64)relos;
> +	attr.core_relo_cnt = sizeof(relos) / sizeof(*relos);
> +	attr.core_relo_rec_size = sizeof(*relos);
> +	prog_fd = sys_bpf_prog_load(&attr, sizeof(attr), 1);
> +	if (prog_fd >= 0) {
> +		PRINT_FAIL("sys_bpf_prog_load() expected to fail\n");
> +		goto out;
> +	}
> +	ASSERT_HAS_SUBSTR(log, "failed: -22", "prog_load_log");
> +
> +out:
> +	close(prog_fd);
> +	close(btf_fd);
> +}
> +
>  void test_core_reloc_raw(void)
>  {
>  	if (test__start_subtest("bad_local_id"))
>  		test_bad_local_id();
> +	if (test__start_subtest("negative_accessor"))
> +		test_negative_accessor();
>  }


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH bpf v3 1/2] bpf: reject negative CO-RE accessor indices in bpf_core_parse_spec()
  2026-04-04 16:12 ` [PATCH bpf v3 1/2] bpf: reject negative CO-RE accessor indices in bpf_core_parse_spec() Weiming Shi
  2026-04-04 18:07   ` Emil Tsalapatis
@ 2026-04-06 15:01   ` Paul Chaignon
  1 sibling, 0 replies; 9+ messages in thread
From: Paul Chaignon @ 2026-04-06 15:01 UTC (permalink / raw)
  To: Weiming Shi
  Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
	Song Liu, Yonghong Song, Jiri Olsa, bpf, Xiang Mei

On Sun, Apr 05, 2026 at 12:12:20AM +0800, Weiming Shi wrote:
> CO-RE accessor strings are colon-separated indices that describe a path
> from a root BTF type to a target field, e.g. "0:1:2" walks through
> nested struct members. bpf_core_parse_spec() parses each component with
> sscanf("%d"), so negative values like -1 are silently accepted.  The
> subsequent bounds checks (access_idx >= btf_vlen(t)) only guard the
> upper bound and always pass for negative values because C integer
> promotion converts the __u16 btf_vlen result to int, making the
> comparison (int)(-1) >= (int)(N) false for any positive N.
> 
> When -1 reaches btf_member_bit_offset() it gets cast to u32 0xffffffff,
> producing an out-of-bounds read far past the members array.  A crafted
> BPF program with a negative CO-RE accessor on any struct that exists in
> vmlinux BTF (e.g. task_struct) crashes the kernel deterministically
> during BPF_PROG_LOAD on any system with CONFIG_DEBUG_INFO_BTF=y
> (default on major distributions).  The bug is reachable with CAP_BPF:
> 
>  BUG: unable to handle page fault for address: ffffed11818b6626
>  #PF: supervisor read access in kernel mode
>  #PF: error_code(0x0000) - not-present page
>  Oops: Oops: 0000 [#1] SMP KASAN NOPTI
>  CPU: 0 UID: 0 PID: 85 Comm: poc Not tainted 7.0.0-rc6 #18 PREEMPT(full)
>  RIP: 0010:bpf_core_parse_spec (tools/lib/bpf/relo_core.c:354)
>  RAX: 00000000ffffffff
>  Call Trace:
>   <TASK>
>   bpf_core_calc_relo_insn (tools/lib/bpf/relo_core.c:1321)
>   bpf_core_apply (kernel/bpf/btf.c:9507)
>   check_core_relo (kernel/bpf/verifier.c:19475)
>   bpf_check (kernel/bpf/verifier.c:26031)
>   bpf_prog_load (kernel/bpf/syscall.c:3089)
>   __sys_bpf (kernel/bpf/syscall.c:6228)
>   </TASK>
> 
> CO-RE accessor indices are inherently non-negative (struct member index,
> array element index, or enumerator index), so reject them immediately
> after parsing.
> 
> Fixes: ddc7c3042614 ("libbpf: implement BPF CO-RE offset relocation algorithm")
> Reported-by: Xiang Mei <xmei5@asu.edu>
> Signed-off-by: Weiming Shi <bestswngs@gmail.com>

Acked-by: Paul Chaignon <paul.chaignon@gmail.com>

> ---
> v3: added selftest 
> v2: fix typo Signed-off-by tag (missing leading 'S')
> 
>  tools/lib/bpf/relo_core.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/tools/lib/bpf/relo_core.c b/tools/lib/bpf/relo_core.c
> index 6eea5edba58a..0ccc8f548cba 100644
> --- a/tools/lib/bpf/relo_core.c
> +++ b/tools/lib/bpf/relo_core.c
> @@ -292,6 +292,8 @@ int bpf_core_parse_spec(const char *prog_name, const struct btf *btf,
>  			++spec_str;
>  		if (sscanf(spec_str, "%d%n", &access_idx, &parsed_len) != 1)
>  			return -EINVAL;
> +		if (access_idx < 0)
> +			return -EINVAL;
>  		if (spec->raw_len == BPF_CORE_SPEC_MAX_LEN)
>  			return -E2BIG;
>  		spec_str += parsed_len;
> -- 
> 2.43.0
> 
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH bpf v3 2/2] selftests/bpf: add test for negative CO-RE accessor index rejection
  2026-04-04 16:12 ` [PATCH bpf v3 2/2] selftests/bpf: add test for negative CO-RE accessor index rejection Weiming Shi
  2026-04-04 18:12   ` Emil Tsalapatis
@ 2026-04-06 15:03   ` Paul Chaignon
  1 sibling, 0 replies; 9+ messages in thread
From: Paul Chaignon @ 2026-04-06 15:03 UTC (permalink / raw)
  To: Weiming Shi
  Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Martin KaFai Lau,
	Song Liu, Yonghong Song, Jiri Olsa, bpf, Xiang Mei

On Sun, Apr 05, 2026 at 12:12:21AM +0800, Weiming Shi wrote:
> Add a selftest to verify that the kernel rejects BPF programs containing
> CO-RE relocations with negative accessor indices.  The test constructs a
> minimal BTF blob with a struct type and a CO-RE relocation whose access
> string is "0:-1".  Without the fix in the previous patch, this triggers
> an out-of-bounds read in bpf_core_parse_spec(); with the fix, the
> program load fails cleanly with -EINVAL.
> 
> Signed-off-by: Weiming Shi <bestswngs@gmail.com>

I checked that the new selftest does fail, in the expected manner, when
applied without the corresponding fix.

Tested-by: Paul Chaignon <paul.chaignon@gmail.com>

> ---
>  .../selftests/bpf/prog_tests/core_reloc_raw.c | 94 +++++++++++++++++++
>  1 file changed, 94 insertions(+)
> 
> diff --git a/tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c b/tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c
> index a18d3680fb16..f7bf6c237d59 100644
> --- a/tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c
> +++ b/tools/testing/selftests/bpf/prog_tests/core_reloc_raw.c
> @@ -118,8 +118,102 @@ static void test_bad_local_id(void)
>  	close(btf_fd);
>  }
>  
> +/* Check that verifier rejects BPF program containing CO-RE relocation
> + * with a negative accessor index (e.g. "0:-1").
> + */
> +static void test_negative_accessor(void)
> +{
> +	struct test_btf {
> +		struct btf_header hdr;
> +		__u32 types[18];
> +		char strings[20];
> +	} raw_btf = {
> +		.hdr = {
> +			.magic = BTF_MAGIC,
> +			.version = BTF_VERSION,
> +			.hdr_len = sizeof(struct btf_header),
> +			.type_off = 0,
> +			.type_len = sizeof(raw_btf.types),
> +			.str_off = offsetof(struct test_btf, strings) -
> +				   offsetof(struct test_btf, types),
> +			.str_len = sizeof(raw_btf.strings),
> +		},
> +		.types = {
> +			/* [1] int */
> +			BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4),
> +			/* [2] struct s { int x; } */
> +			BTF_STRUCT_ENC(5, 1, 4),
> +			BTF_MEMBER_ENC(7, 1, 0),
> +			/* [3] int (*)(int a) */
> +			BTF_FUNC_PROTO_ENC(1, 1),
> +			BTF_FUNC_PROTO_ARG_ENC(13, 1),
> +			/* [4] FUNC 'foo' */
> +			BTF_FUNC_ENC(9, 3),
> +		},
> +		/* offsets: 0:NUL 1:"int" 5:"s" 7:"x" 9:"foo" 13:"a" 15:"0:-1" */
> +		.strings = "\0int\0s\0x\0foo\0a\0" "0:-1",
> +	};
> +	__u32 log_level = 1 | 2 | 4;
> +	LIBBPF_OPTS(bpf_btf_load_opts, opts,
> +		    .log_buf = log,
> +		    .log_size = sizeof(log),
> +		    .log_level = log_level,
> +	);
> +	struct bpf_insn insns[] = {
> +		BPF_ALU64_IMM(BPF_MOV, BPF_REG_0, 0),
> +		BPF_EXIT_INSN(),
> +	};
> +	struct bpf_func_info funcs[] = {
> +		{ .insn_off = 0, .type_id = 4 }
> +	};
> +	struct bpf_core_relo relos[] = {
> +		{
> +			.insn_off = 0,
> +			.type_id = 2,		/* struct s */
> +			.access_str_off = 15,	/* "0:-1" */
> +			.kind = BPF_CORE_FIELD_BYTE_OFFSET,
> +		}
> +	};
> +	union bpf_attr attr;
> +	int prog_fd = -1;
> +	int btf_fd = -1;
> +
> +	btf_fd = bpf_btf_load(&raw_btf, sizeof(raw_btf), &opts);
> +	if (!ASSERT_GE(btf_fd, 0, "btf_load"))
> +		return;
> +
> +	log[0] = 0;
> +	memset(&attr, 0, sizeof(attr));
> +	attr.prog_btf_fd = btf_fd;
> +	attr.prog_type = BPF_TRACE_RAW_TP;
> +	attr.license = (__u64)"GPL";
> +	attr.insns = (__u64)&insns;
> +	attr.insn_cnt = sizeof(insns) / sizeof(*insns);
> +	attr.log_buf = (__u64)log;
> +	attr.log_size = sizeof(log);
> +	attr.log_level = log_level;
> +	attr.func_info = (__u64)funcs;
> +	attr.func_info_cnt = sizeof(funcs) / sizeof(*funcs);
> +	attr.func_info_rec_size = sizeof(*funcs);
> +	attr.core_relos = (__u64)relos;
> +	attr.core_relo_cnt = sizeof(relos) / sizeof(*relos);
> +	attr.core_relo_rec_size = sizeof(*relos);
> +	prog_fd = sys_bpf_prog_load(&attr, sizeof(attr), 1);
> +	if (prog_fd >= 0) {
> +		PRINT_FAIL("sys_bpf_prog_load() expected to fail\n");
> +		goto out;
> +	}
> +	ASSERT_HAS_SUBSTR(log, "failed: -22", "prog_load_log");
> +
> +out:
> +	close(prog_fd);
> +	close(btf_fd);
> +}
> +
>  void test_core_reloc_raw(void)
>  {
>  	if (test__start_subtest("bad_local_id"))
>  		test_bad_local_id();
> +	if (test__start_subtest("negative_accessor"))
> +		test_negative_accessor();
>  }
> -- 
> 2.43.0
> 
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH bpf v3 0/2] bpf: reject negative CO-RE accessor indices
  2026-04-04 16:12 [PATCH bpf v3 0/2] bpf: reject negative CO-RE accessor indices Weiming Shi
                   ` (2 preceding siblings ...)
  2026-04-04 18:06 ` [PATCH bpf v3 0/2] bpf: reject negative CO-RE accessor indices Emil Tsalapatis
@ 2026-04-07 15:30 ` patchwork-bot+netdevbpf
  3 siblings, 0 replies; 9+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-04-07 15:30 UTC (permalink / raw)
  To: Weiming Shi
  Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
	yonghong.song, jolsa, bpf, xmei5

Hello:

This series was applied to bpf/bpf-next.git (master)
by Alexei Starovoitov <ast@kernel.org>:

On Sun,  5 Apr 2026 00:12:19 +0800 you wrote:
> bpf_core_parse_spec() parses CO-RE accessor strings with sscanf("%d"),
> which accepts negative values.  The downstream bounds checks only test
> the upper bound, so a negative index like -1 slips through, gets cast
> to u32 0xffffffff in btf_member_bit_offset(), and crashes the kernel.
> 
> To clarify the kernel-side concern from v2 review:
> tools/lib/bpf/relo_core.c is shared code -- the kernel compiles it
> directly via kernel/bpf/relo_core.c (#include).  So the fix does
> apply in the kernel's BPF_PROG_LOAD -> check_core_relo() ->
> bpf_core_apply() -> bpf_core_parse_spec() path.
> 
> [...]

Here is the summary with links:
  - [bpf,v3,1/2] bpf: reject negative CO-RE accessor indices in bpf_core_parse_spec()
    https://git.kernel.org/bpf/bpf-next/c/1c22483a2c4b
  - [bpf,v3,2/2] selftests/bpf: add test for negative CO-RE accessor index rejection
    (no matching commit)

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2026-04-07 15:30 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-04 16:12 [PATCH bpf v3 0/2] bpf: reject negative CO-RE accessor indices Weiming Shi
2026-04-04 16:12 ` [PATCH bpf v3 1/2] bpf: reject negative CO-RE accessor indices in bpf_core_parse_spec() Weiming Shi
2026-04-04 18:07   ` Emil Tsalapatis
2026-04-06 15:01   ` Paul Chaignon
2026-04-04 16:12 ` [PATCH bpf v3 2/2] selftests/bpf: add test for negative CO-RE accessor index rejection Weiming Shi
2026-04-04 18:12   ` Emil Tsalapatis
2026-04-06 15:03   ` Paul Chaignon
2026-04-04 18:06 ` [PATCH bpf v3 0/2] bpf: reject negative CO-RE accessor indices Emil Tsalapatis
2026-04-07 15:30 ` patchwork-bot+netdevbpf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox