* Re: [PATCH bpf-next] libbpf: Fix sign extension of unaligned enums in btf_dump_get_enum_value()
2026-11-13 22:34 [PATCH bpf-next] libbpf: Fix sign extension of unaligned enums in btf_dump_get_enum_value() Hungyu Lin
@ 2026-11-13 22:55 ` sashiko-bot
2026-11-14 5:58 ` Alexei Starovoitov
1 sibling, 0 replies; 3+ messages in thread
From: sashiko-bot @ 2026-11-13 22:55 UTC (permalink / raw)
To: Hungyu Lin, bpf
Thank you for your contribution! Sashiko AI review found 1 potential
issue to consider:
- [Medium] libbpf: false -E2BIG on unaligned enum bitfields in
btf_dump_get_enum_value()
It also noticed 1 related pre-existing issue this patch does not
introduce:
- [Medium] libbpf: out-of-bounds read in btf_dump_type_data_check_zero()
[1]
--
commit 7eb03a62b6fbbb96cde677bedc0b0fa89d38e668
Author: Hungyu Lin <dennylin0707@gmail.com>
libbpf: Fix sign extension of unaligned enums in btf_dump_get_enum_value()
This commit fixes sign extension for unaligned enum values in
btf_dump_get_enum_value() by copying the unaligned value into a local
aligned buffer before reading it with the same signed loads used for
aligned data.
> diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c
> index bbe86928b3eca..30fe3bb48df6c 100644
> --- a/tools/lib/bpf/btf_dump.c
> +++ b/tools/lib/bpf/btf_dump.c
> @@ -2329,16 +2329,13 @@ static int btf_dump_get_enum_value(struct btf_dump *d,
> __s64 *value)
> {
> bool is_signed = btf_kflag(t);
> + char buf[8] __attribute__((aligned(8)));
>
> - if (!ptr_is_aligned(d->btf, id, data)) {
> - __u64 val;
> - int err;
> -
> - err = btf_dump_get_bitfield_value(d, t, data, 0, 0, &val);
> - if (err)
> - return err;
> - *value = (__s64)val;
> - return 0;
> + if (t->size <= sizeof(buf) && !ptr_is_aligned(d->btf, id, data)) {
> + if (data + t->size > d->typed_dump->data_end)
> + return -E2BIG;
> + memcpy(buf, data, t->size);
> + data = buf;
> }
[Severity: Medium]
Can this return a false -E2BIG regression when dumping a struct that has
an unaligned enum bitfield within the last t->size - 1 bytes of the
buffer (for example, enum fs_context_phase phase:8 in struct fs_context)?
When emit_zeroes is false, btf_dump_type_data_check_zero() ignores bit_sz
on BTF_KIND_ENUM and BTF_KIND_ENUM64 and calls btf_dump_get_enum_value()
directly instead of btf_dump_bitfield_check_zero():
tools/lib/bpf/btf_dump.c:btf_dump_type_data_check_zero() {
...
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
err = btf_dump_get_enum_value(d, t, data, id, &value);
if (err)
return err;
...
}
Previously, the unaligned path called btf_dump_get_bitfield_value(d, t,
data, 0, 0, &val), which computed nr_bytes = 0 and did not fail the bounds
check. Requiring data + t->size <= d->typed_dump->data_end rejects any
unaligned enum bitfield whose underlying enum size extends past the end of
the struct, causing btf_dump__dump_type_data() to fail with -E2BIG on a
full-sized buffer.
[Severity: Medium]
This problem wasn't introduced by this patch, but can
btf_dump_type_data_check_zero() read past d->typed_dump->data_end
[1] when dumping a truncated composite type?
btf_dump_type_data_check_overflow() intentionally skips bounds checks on
BTF_KIND_STRUCT and BTF_KIND_ARRAY so truncated structures and arrays can
be partially dumped. Before dumping each member, however,
btf_dump_type_data_check_zero() recurses into the composite type and calls
helpers such as btf_dump_base_type_check_zero() and the aligned path of
btf_dump_get_enum_value(), which dereference data without checking that
data + nr_bytes or data + t->size fits within d->typed_dump->data_end.
[1] https://sashiko.dev/#/bug/linux-c12e98ee-e737-47d5-ad42-ceec0d189844
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261113223449.97604-1-dennylin0707@gmail.com?part=1
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH bpf-next] libbpf: Fix sign extension of unaligned enums in btf_dump_get_enum_value()
2026-11-13 22:34 [PATCH bpf-next] libbpf: Fix sign extension of unaligned enums in btf_dump_get_enum_value() Hungyu Lin
2026-11-13 22:55 ` sashiko-bot
@ 2026-11-14 5:58 ` Alexei Starovoitov
1 sibling, 0 replies; 3+ messages in thread
From: Alexei Starovoitov @ 2026-11-14 5:58 UTC (permalink / raw)
To: Hungyu Lin, andrii, bpf
Cc: eddyz87, ihor.solodrai, daniel, memxor, martin.lau, song,
yonghong.song, jolsa, emil, alan.maguire, linux-kernel
On Fri, Nov 13, 2026 at 02:34 PM Hungyu Lin <dennylin0707@gmail.com> wrote:
> btf_dump_get_enum_value() reads unaligned enum values through
> btf_dump_get_bitfield_value() with a zero bit size. That path
> zero-extends the value instead of using the signed loads that are used
Not quite. bit_sz == 0 makes right_shift_bits == 64 in
*value = (num << left_shift_bits) >> right_shift_bits;
That's undefined behavior for __u64 on both endians.
Pls say it in the commit log.
> + if (t->size <= sizeof(buf) && !ptr_is_aligned(d->btf, id, data)) {
> + if (data + t->size > d->typed_dump->data_end)
> + return -E2BIG;
This breaks the dump of struct fs_context when emit_zeroes is not set.
'phase' is an enum bitfield of 8 bits, 3 bytes before the end of the
struct. btf_dump_type_data_check_zero() ignores bit_sz of an enum:
btf_dump_struct_data()
btf_dump_dump_type_data(data = end - 3, bit_sz = 8)
btf_dump_type_data_check_overflow() 1 byte, ok
btf_dump_type_data_check_zero()
btf_dump_get_enum_value() t->size == 4, unaligned
end - 3 + 4 > data_end -E2BIG
Today it reads 1 byte past data_end and the dump succeeds.
The over-read is an old bug of enum bitfields. It needs its own fix.
The fs_context test in test_btf_dump_struct_data() sets emit_zeroes,
so it doesn't notice.
Drop the check. btf_dump_int_data() copies without it.
The check against data_end belongs in one place for all types.
The first case below fails on bpf-next today.
The second case fails with this patch.
I didn't build it and didn't run it:
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c
index b5c90f7..d6fd993 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c
@@ -945,7 +945,7 @@ static void test_btf_dump_enum_data(struct btf *btf, struct btf_dump *d,
char *str)
{
__u32 high = 0x80000000;
- __s32 neg = -1;
+ __s32 neg[2] = { -1, -1 };
/* enum where enum value does (and does not) exist */
TEST_BTF_DUMP_DATA_C(btf, d, "enum", str, enum bpf_cmd, BTF_F_COMPACT,
@@ -994,6 +994,8 @@ static void test_btf_dump_enum_data(struct btf *btf, struct btf_dump *d,
"(enum test_enum_u32)TEST_ENUM_HIGH"), "dump unsigned enum");
ASSERT_OK(btf_dump_data(btf, d, "test_enum_s32", NULL, 0, &neg, 4, str,
"(enum test_enum_s32)TEST_ENUM_NEG"), "dump signed enum");
+ ASSERT_OK(btf_dump_data(btf, d, "test_enum_s32", NULL, 0, (void *)neg + 1, 4, str,
+ "(enum test_enum_s32)TEST_ENUM_NEG"), "dump unaligned enum");
btf_dump__free(d);
}
@@ -1182,6 +1184,12 @@ static void test_btf_dump_struct_data(struct btf *btf, struct btf_dump *d,
type_sz = btf__resolve_size(btf, type_id);
str[0] = '\0';
+ /* 'phase' is an enum bitfield 3 bytes before the end */
+ ret = btf_dump__dump_type_data(d, type_id, zero_data, type_sz, &opts);
+ ASSERT_EQ(ret, type_sz,
+ "unexpected return value dumping zeroed fs_context");
+
+ str[0] = '\0';
opts.emit_zeroes = true;
ret = btf_dump__dump_type_data(d, type_id, zero_data, type_sz, &opts);
ASSERT_EQ(ret, type_sz,
pw-bot: cr
^ permalink raw reply related [flat|nested] 3+ messages in thread