BPF List
 help / color / mirror / Atom feed
* [PATCH bpf-next] libbpf: Fix sign extension of unaligned enums in btf_dump_get_enum_value()
@ 2026-11-13 22:34 Hungyu Lin
  2026-11-13 22:55 ` sashiko-bot
  2026-11-14  5:58 ` Alexei Starovoitov
  0 siblings, 2 replies; 3+ messages in thread
From: Hungyu Lin @ 2026-11-13 22:34 UTC (permalink / raw)
  To: andrii, bpf
  Cc: eddyz87, ihor.solodrai, ast, daniel, memxor, martin.lau, song,
	yonghong.song, jolsa, emil, alan.maguire, linux-kernel,
	dennylin0707

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
for aligned data, so a negative value of a signed enum loses its
enumerator name when the data is unaligned.

Fix this by copying unaligned enum data into an aligned buffer, after
checking that the whole value is within the data, and reading it with
the same loads as aligned data.

Fixes: 920d16af9b42 ("libbpf: BTF dumper support for typed data")
Signed-off-by: Hungyu Lin <dennylin0707@gmail.com>
---
 tools/lib/bpf/btf_dump.c | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/tools/lib/bpf/btf_dump.c b/tools/lib/bpf/btf_dump.c
index bbe86928b3ec..30fe3bb48df6 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;
 	}
 
 	switch (t->size) {

base-commit: 0021f17128de5dc0b19f1a523f448df2440d8088
-- 
2.43.0


^ permalink raw reply related	[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: 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

end of thread, other threads:[~2026-11-14  5:58 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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

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