* [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