btf_type_size_unknown() returns an int, but calculates the type record
size using unsigned operands.  With a 24-bit vlen, an unknown kind whose
vlen is 0xffffff and whose info and element sizes are both 252 has a
record size of 0xfc00000c.  Converting this value to int produces
-67108852.

btf_parse_type_sec() returns the negative value, and btf_new() encodes it
with ERR_PTR().  Since the value is outside the error-pointer range,
libbpf_ptr() does not recognize it as an error.  On a 64-bit system, a
129-byte raw BTF input makes btf__new() return 0xfffffffffc00000c while
libbpf_get_error() returns zero and errno remains zero.  Calling
btf__type_cnt() or btf__free() on the result crashes.  The same input
makes bpftool's "btf dump file" command terminate with SIGSEGV.

Calculate the record size as size_t and reject values above INT_MAX
before converting it to int.  Add a regression test for the maximum-vlen
unknown kind.

Fixes: cacd6729c092 ("libbpf: Adjust btf_vlen() to return a __u32")
Assisted-by: Symbolic
Signed-off-by: Mark Amirkan <[email protected]>
---
Testing:
- normal builds of libbpf and bpftool, plus an ASan/UBSan libbpf build;
- the reproducer through btf__new() and bpftool;
- the added btf_kind test and the existing encoding/decoding subtests;
- identical bpftool output before and after the fix for all 60 BTF blobs
  under /sys/kernel/btf on the test system.

The complete BPF selftest suite was not run locally because clang was not
available.

This fixes code present in Linux v7.2; please consider it for stable.

 tools/lib/bpf/btf.c                           | 11 +++++-
 .../selftests/bpf/prog_tests/btf_kind.c       | 39 +++++++++++++++++++
 2 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/tools/lib/bpf/btf.c b/tools/lib/bpf/btf.c
index 
c783359977b46e521965c1af223515cfd8701b12..83a7f199f37dfdc2384107a45a8be5b98c4cd8f1
 100644
--- a/tools/lib/bpf/btf.c
+++ b/tools/lib/bpf/btf.c
@@ -421,6 +421,7 @@ static int btf_type_size_unknown(const struct btf *btf, 
const struct btf_type *t
 {
        __u32 l_cnt = btf->hdr.layout_len / sizeof(struct btf_layout);
        struct btf_layout *l = btf->layout;
+       size_t type_size;
        __u32 vlen = btf_vlen(t);
        __u32 kind = btf_kind(t);
 
@@ -448,7 +449,15 @@ static int btf_type_size_unknown(const struct btf *btf, 
const struct btf_type *t
                return -EINVAL;
        }
 
-       return sizeof(struct btf_type) + l[kind].info_sz + vlen * 
l[kind].elem_sz;
+       type_size = sizeof(struct btf_type) + l[kind].info_sz +
+                   (size_t)vlen * l[kind].elem_sz;
+       if (type_size > INT_MAX) {
+               pr_debug("BTF type size %zu for kind %u is too large\n",
+                        type_size, kind);
+               return -E2BIG;
+       }
+
+       return type_size;
 }
 
 static int btf_type_size(const struct btf *btf, const struct btf_type *t)
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_kind.c 
b/tools/testing/selftests/bpf/prog_tests/btf_kind.c
index 
f61afe6a79a51f86f22f62bd86c685b7db8b39d1..fc6a4db9993764536885a5c5f13980a4c3a6bd26
 100644
--- a/tools/testing/selftests/bpf/prog_tests/btf_kind.c
+++ b/tools/testing/selftests/bpf/prog_tests/btf_kind.c
@@ -217,10 +217,49 @@ void test_btf_kind_decoding(void)
        btf__free(btf);
 }
 
+static void test_btf_kind_size_overflow(void)
+{
+       /* Max vlen and aligned u8 layout sizes produce type size 0xfc00000c. */
+       struct {
+               struct btf_header hdr;
+               struct btf_type type;
+               struct btf_layout layouts[NR_BTF_KINDS + 1];
+               char strs[1];
+       } __packed raw_btf = {
+               .hdr = {
+                       .magic = BTF_MAGIC,
+                       .version = BTF_VERSION,
+                       .hdr_len = sizeof(struct btf_header),
+                       .type_len = sizeof(struct btf_type),
+                       .layout_off = sizeof(struct btf_type),
+                       .layout_len = sizeof(struct btf_layout) * (NR_BTF_KINDS 
+ 1),
+                       .str_off = sizeof(struct btf_type) +
+                                  sizeof(struct btf_layout) * (NR_BTF_KINDS + 
1),
+                       .str_len = 1,
+               },
+               .type.info = (NR_BTF_KINDS << 24) | BTF_MAX_VLEN,
+               .layouts[NR_BTF_KINDS] = {
+                       .info_sz = 252,
+                       .elem_sz = 252,
+               },
+       };
+       struct btf *btf;
+       int err;
+
+       errno = 0;
+       btf = btf__new(&raw_btf, sizeof(raw_btf));
+       err = libbpf_get_error(btf);
+       if (!ASSERT_EQ(err, -E2BIG, "size_overflow_err"))
+               return;
+       ASSERT_NULL(btf, "size_overflow_btf");
+}
+
 void test_btf_kind(void)
 {
        if (test__start_subtest("btf_kind_encoding"))
                test_btf_kind_encoding();
        if (test__start_subtest("btf_kind_decoding"))
                test_btf_kind_decoding();
+       if (test__start_subtest("btf_kind_size_overflow"))
+               test_btf_kind_size_overflow();
 }

base-commit: d761934c9483ecde93fe99d8705282f716dfee50
-- 
2.39.3 (Apple Git-146)

Reply via email to