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)