diff --git a/libbpf-Fix-memory-leak-in-parse_usdt_arg.patch b/libbpf-Fix-memory-leak-in-parse_usdt_arg.patch new file mode 100644 index 0000000..a8fad0e --- /dev/null +++ b/libbpf-Fix-memory-leak-in-parse_usdt_arg.patch @@ -0,0 +1,67 @@ +From 881a10980b7ded995da5d9cc1919992c36c9d2be Mon Sep 17 00:00:00 2001 +From: Xu Kuohai +Date: Tue, 11 Oct 2022 08:01:04 -0400 +Subject: [PATCH 2/2] libbpf: Fix memory leak in parse_usdt_arg() + +In the arm64 version of parse_usdt_arg(), when sscanf returns 2, reg_name +is allocated but not freed. Fix it. + +Fixes: 0f8619929c57 ("libbpf: Usdt aarch64 arg parsing support") +Signed-off-by: Xu Kuohai +Signed-off-by: Andrii Nakryiko +Acked-by: Martin KaFai Lau +Link: https://lore.kernel.org/bpf/20221011120108.782373-3-xukuohai@huaweicloud.com +--- + src/usdt.c | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +diff --git a/src/usdt.c b/src/usdt.c +index e83b497..49f3c3b 100644 +--- a/src/usdt.c ++++ b/src/usdt.c +@@ -1348,25 +1348,23 @@ static int calc_pt_regs_off(const char *reg_name) + + static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec *arg) + { +- char *reg_name = NULL; ++ char reg_name[16]; + int arg_sz, len, reg_off; + long off; + +- if (sscanf(arg_str, " %d @ \[ %m[a-z0-9], %ld ] %n", &arg_sz, ®_name, &off, &len) == 3) { ++ if (sscanf(arg_str, " %d @ \[ %15[a-z0-9], %ld ] %n", &arg_sz, reg_name, &off, &len) == 3) { + /* Memory dereference case, e.g., -4@[sp, 96] */ + arg->arg_type = USDT_ARG_REG_DEREF; + arg->val_off = off; + reg_off = calc_pt_regs_off(reg_name); +- free(reg_name); + if (reg_off < 0) + return reg_off; + arg->reg_off = reg_off; +- } else if (sscanf(arg_str, " %d @ \[ %m[a-z0-9] ] %n", &arg_sz, ®_name, &len) == 2) { ++ } else if (sscanf(arg_str, " %d @ \[ %15[a-z0-9] ] %n", &arg_sz, reg_name, &len) == 2) { + /* Memory dereference case, e.g., -4@[sp] */ + arg->arg_type = USDT_ARG_REG_DEREF; + arg->val_off = 0; + reg_off = calc_pt_regs_off(reg_name); +- free(reg_name); + if (reg_off < 0) + return reg_off; + arg->reg_off = reg_off; +@@ -1375,12 +1373,11 @@ static int parse_usdt_arg(const char *arg_str, int arg_num, struct usdt_arg_spec + arg->arg_type = USDT_ARG_CONST; + arg->val_off = off; + arg->reg_off = 0; +- } else if (sscanf(arg_str, " %d @ %m[a-z0-9] %n", &arg_sz, ®_name, &len) == 2) { ++ } else if (sscanf(arg_str, " %d @ %15[a-z0-9] %n", &arg_sz, reg_name, &len) == 2) { + /* Register read case, e.g., -8@x4 */ + arg->arg_type = USDT_ARG_REG; + arg->val_off = 0; + reg_off = calc_pt_regs_off(reg_name); +- free(reg_name); + if (reg_off < 0) + return reg_off; + arg->reg_off = reg_off; +-- +2.38.0 + diff --git a/libbpf-Fix-null-pointer-dereference-in-find_prog_by_.patch b/libbpf-Fix-null-pointer-dereference-in-find_prog_by_.patch new file mode 100644 index 0000000..a657a97 --- /dev/null +++ b/libbpf-Fix-null-pointer-dereference-in-find_prog_by_.patch @@ -0,0 +1,39 @@ +From 3a3ef0c1d09e1894740db71cdcb7be0bfd713671 Mon Sep 17 00:00:00 2001 +From: Shung-Hsi Yu +Date: Wed, 12 Oct 2022 10:23:53 +0800 +Subject: [PATCH 4/4] libbpf: Fix null-pointer dereference in + find_prog_by_sec_insn() + +When there are no program sections, obj->programs is left unallocated, +and find_prog_by_sec_insn()'s search lands on &obj->programs[0] == NULL, +and will cause null-pointer dereference in the following access to +prog->sec_idx. + +Guard the search with obj->nr_programs similar to what's being done in +__bpf_program__iter() to prevent null-pointer access from happening. + +Fixes: db2b8b06423c ("libbpf: Support CO-RE relocations for multi-prog sections") +Signed-off-by: Shung-Hsi Yu +Signed-off-by: Andrii Nakryiko +Link: https://lore.kernel.org/bpf/20221012022353.7350-4-shung-hsi.yu@suse.com +--- + src/libbpf.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/libbpf.c b/src/libbpf.c +index 29e9df0..8c3f236 100644 +--- a/src/libbpf.c ++++ b/src/libbpf.c +@@ -4115,6 +4115,9 @@ static struct bpf_program *find_prog_by_sec_insn(const struct bpf_object *obj, + int l = 0, r = obj->nr_programs - 1, m; + struct bpf_program *prog; + ++ if (!obj->nr_programs) ++ return NULL; ++ + while (l < r) { + m = l + (r - l + 1) / 2; + prog = &obj->programs[m]; +-- +2.38.0 + diff --git a/libbpf-Fix-use-after-free-in-btf_dump_name_dups.patch b/libbpf-Fix-use-after-free-in-btf_dump_name_dups.patch new file mode 100644 index 0000000..5c92149 --- /dev/null +++ b/libbpf-Fix-use-after-free-in-btf_dump_name_dups.patch @@ -0,0 +1,136 @@ +From 54caf920db0e489de90f3aaaa41e2a51ddbcd084 Mon Sep 17 00:00:00 2001 +From: Xu Kuohai +Date: Tue, 11 Oct 2022 08:01:03 -0400 +Subject: [PATCH 1/1] libbpf: Fix use-after-free in btf_dump_name_dups + +ASAN reports an use-after-free in btf_dump_name_dups: + +ERROR: AddressSanitizer: heap-use-after-free on address 0xffff927006db at pc 0xaaaab5dfb618 bp 0xffffdd89b890 sp 0xffffdd89b928 +READ of size 2 at 0xffff927006db thread T0 + #0 0xaaaab5dfb614 in __interceptor_strcmp.part.0 (test_progs+0x21b614) + #1 0xaaaab635f144 in str_equal_fn tools/lib/bpf/btf_dump.c:127 + #2 0xaaaab635e3e0 in hashmap_find_entry tools/lib/bpf/hashmap.c:143 + #3 0xaaaab635e72c in hashmap__find tools/lib/bpf/hashmap.c:212 + #4 0xaaaab6362258 in btf_dump_name_dups tools/lib/bpf/btf_dump.c:1525 + #5 0xaaaab636240c in btf_dump_resolve_name tools/lib/bpf/btf_dump.c:1552 + #6 0xaaaab6362598 in btf_dump_type_name tools/lib/bpf/btf_dump.c:1567 + #7 0xaaaab6360b48 in btf_dump_emit_struct_def tools/lib/bpf/btf_dump.c:912 + #8 0xaaaab6360630 in btf_dump_emit_type tools/lib/bpf/btf_dump.c:798 + #9 0xaaaab635f720 in btf_dump__dump_type tools/lib/bpf/btf_dump.c:282 + #10 0xaaaab608523c in test_btf_dump_incremental tools/testing/selftests/bpf/prog_tests/btf_dump.c:236 + #11 0xaaaab6097530 in test_btf_dump tools/testing/selftests/bpf/prog_tests/btf_dump.c:875 + #12 0xaaaab6314ed0 in run_one_test tools/testing/selftests/bpf/test_progs.c:1062 + #13 0xaaaab631a0a8 in main tools/testing/selftests/bpf/test_progs.c:1697 + #14 0xffff9676d214 in __libc_start_main ../csu/libc-start.c:308 + #15 0xaaaab5d65990 (test_progs+0x185990) + +0xffff927006db is located 11 bytes inside of 16-byte region [0xffff927006d0,0xffff927006e0) +freed by thread T0 here: + #0 0xaaaab5e2c7c4 in realloc (test_progs+0x24c7c4) + #1 0xaaaab634f4a0 in libbpf_reallocarray tools/lib/bpf/libbpf_internal.h:191 + #2 0xaaaab634f840 in libbpf_add_mem tools/lib/bpf/btf.c:163 + #3 0xaaaab636643c in strset_add_str_mem tools/lib/bpf/strset.c:106 + #4 0xaaaab6366560 in strset__add_str tools/lib/bpf/strset.c:157 + #5 0xaaaab6352d70 in btf__add_str tools/lib/bpf/btf.c:1519 + #6 0xaaaab6353e10 in btf__add_field tools/lib/bpf/btf.c:2032 + #7 0xaaaab6084fcc in test_btf_dump_incremental tools/testing/selftests/bpf/prog_tests/btf_dump.c:232 + #8 0xaaaab6097530 in test_btf_dump tools/testing/selftests/bpf/prog_tests/btf_dump.c:875 + #9 0xaaaab6314ed0 in run_one_test tools/testing/selftests/bpf/test_progs.c:1062 + #10 0xaaaab631a0a8 in main tools/testing/selftests/bpf/test_progs.c:1697 + #11 0xffff9676d214 in __libc_start_main ../csu/libc-start.c:308 + #12 0xaaaab5d65990 (test_progs+0x185990) + +previously allocated by thread T0 here: + #0 0xaaaab5e2c7c4 in realloc (test_progs+0x24c7c4) + #1 0xaaaab634f4a0 in libbpf_reallocarray tools/lib/bpf/libbpf_internal.h:191 + #2 0xaaaab634f840 in libbpf_add_mem tools/lib/bpf/btf.c:163 + #3 0xaaaab636643c in strset_add_str_mem tools/lib/bpf/strset.c:106 + #4 0xaaaab6366560 in strset__add_str tools/lib/bpf/strset.c:157 + #5 0xaaaab6352d70 in btf__add_str tools/lib/bpf/btf.c:1519 + #6 0xaaaab6353ff0 in btf_add_enum_common tools/lib/bpf/btf.c:2070 + #7 0xaaaab6354080 in btf__add_enum tools/lib/bpf/btf.c:2102 + #8 0xaaaab6082f50 in test_btf_dump_incremental tools/testing/selftests/bpf/prog_tests/btf_dump.c:162 + #9 0xaaaab6097530 in test_btf_dump tools/testing/selftests/bpf/prog_tests/btf_dump.c:875 + #10 0xaaaab6314ed0 in run_one_test tools/testing/selftests/bpf/test_progs.c:1062 + #11 0xaaaab631a0a8 in main tools/testing/selftests/bpf/test_progs.c:1697 + #12 0xffff9676d214 in __libc_start_main ../csu/libc-start.c:308 + #13 0xaaaab5d65990 (test_progs+0x185990) + +The reason is that the key stored in hash table name_map is a string +address, and the string memory is allocated by realloc() function, when +the memory is resized by realloc() later, the old memory may be freed, +so the address stored in name_map references to a freed memory, causing +use-after-free. + +Fix it by storing duplicated string address in name_map. + +Fixes: 919d2b1dbb07 ("libbpf: Allow modification of BTF and add btf__add_str API") +Signed-off-by: Xu Kuohai +Signed-off-by: Andrii Nakryiko +Acked-by: Martin KaFai Lau +Link: https://lore.kernel.org/bpf/20221011120108.782373-2-xukuohai@huaweicloud.com +--- + src/btf_dump.c | 29 ++++++++++++++++++++++++++--- + 1 file changed, 26 insertions(+), 3 deletions(-) + +diff --git a/src/btf_dump.c b/src/btf_dump.c +index e4da6de..bf0cc0e 100644 +--- a/src/btf_dump.c ++++ b/src/btf_dump.c +@@ -219,6 +219,17 @@ static int btf_dump_resize(struct btf_dump *d) + return 0; + } + ++static void btf_dump_free_names(struct hashmap *map) ++{ ++ size_t bkt; ++ struct hashmap_entry *cur; ++ ++ hashmap__for_each_entry(map, cur, bkt) ++ free((void *)cur->key); ++ ++ hashmap__free(map); ++} ++ + void btf_dump__free(struct btf_dump *d) + { + int i; +@@ -237,8 +248,8 @@ void btf_dump__free(struct btf_dump *d) + free(d->cached_names); + free(d->emit_queue); + free(d->decl_stack); +- hashmap__free(d->type_names); +- hashmap__free(d->ident_names); ++ btf_dump_free_names(d->type_names); ++ btf_dump_free_names(d->ident_names); + + free(d); + } +@@ -1524,11 +1535,23 @@ static void btf_dump_emit_type_cast(struct btf_dump *d, __u32 id, + static size_t btf_dump_name_dups(struct btf_dump *d, struct hashmap *name_map, + const char *orig_name) + { ++ char *old_name, *new_name; + size_t dup_cnt = 0; ++ int err; ++ ++ new_name = strdup(orig_name); ++ if (!new_name) ++ return 1; + + hashmap__find(name_map, orig_name, (void **)&dup_cnt); + dup_cnt++; +- hashmap__set(name_map, orig_name, (void *)dup_cnt, NULL, NULL); ++ ++ err = hashmap__set(name_map, new_name, (void *)dup_cnt, ++ (const void **)&old_name, NULL); ++ if (err) ++ free(new_name); ++ ++ free(old_name); + + return dup_cnt; + } +-- +2.38.0 + diff --git a/libbpf-Use-elf_getshdrnum-instead-of-e_shnum.patch b/libbpf-Use-elf_getshdrnum-instead-of-e_shnum.patch new file mode 100644 index 0000000..ee1b02d --- /dev/null +++ b/libbpf-Use-elf_getshdrnum-instead-of-e_shnum.patch @@ -0,0 +1,82 @@ +From 741277511035893c72a34df05da3b943afa747a4 Mon Sep 17 00:00:00 2001 +From: Shung-Hsi Yu +Date: Wed, 12 Oct 2022 10:23:51 +0800 +Subject: [PATCH 3/3] libbpf: Use elf_getshdrnum() instead of e_shnum + +This commit replace e_shnum with the elf_getshdrnum() helper to fix two +oss-fuzz-reported heap-buffer overflow in __bpf_object__open. Both +reports are incorrectly marked as fixed and while still being +reproducible in the latest libbpf. + + # clusterfuzz-testcase-minimized-bpf-object-fuzzer-5747922482888704 + libbpf: loading object 'fuzz-object' from buffer + libbpf: sec_cnt is 0 + libbpf: elf: section(1) .data, size 0, link 538976288, flags 2020202020202020, type=2 + libbpf: elf: section(2) .data, size 32, link 538976288, flags 202020202020ff20, type=1 + ================================================================= + ==13==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000000c0 at pc 0x0000005a7b46 bp 0x7ffd12214af0 sp 0x7ffd12214ae8 + WRITE of size 4 at 0x6020000000c0 thread T0 + SCARINESS: 46 (4-byte-write-heap-buffer-overflow-far-from-bounds) + #0 0x5a7b45 in bpf_object__elf_collect /src/libbpf/src/libbpf.c:3414:24 + #1 0x5733c0 in bpf_object_open /src/libbpf/src/libbpf.c:7223:16 + #2 0x5739fd in bpf_object__open_mem /src/libbpf/src/libbpf.c:7263:20 + ... + +The issue lie in libbpf's direct use of e_shnum field in ELF header as +the section header count. Where as libelf implemented an extra logic +that, when e_shnum == 0 && e_shoff != 0, will use sh_size member of the +initial section header as the real section header count (part of ELF +spec to accommodate situation where section header counter is larger +than SHN_LORESERVE). + +The above inconsistency lead to libbpf writing into a zero-entry calloc +area. So intead of using e_shnum directly, use the elf_getshdrnum() +helper provided by libelf to retrieve the section header counter into +sec_cnt. + +Fixes: 0d6988e16a12 ("libbpf: Fix section counting logic") +Fixes: 25bbbd7a444b ("libbpf: Remove assumptions about uniqueness of .rodata/.data/.bss maps") +Signed-off-by: Shung-Hsi Yu +Signed-off-by: Andrii Nakryiko +Link: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=40868 +Link: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=40957 +Link: https://lore.kernel.org/bpf/20221012022353.7350-2-shung-hsi.yu@suse.com +--- + src/libbpf.c | 13 +++++++++---- + 1 file changed, 9 insertions(+), 4 deletions(-) + +diff --git a/src/libbpf.c b/src/libbpf.c +index 184ce16..2e8ac13 100644 +--- a/src/libbpf.c ++++ b/src/libbpf.c +@@ -597,7 +597,7 @@ struct elf_state { + size_t shstrndx; /* section index for section name strings */ + size_t strtabidx; + struct elf_sec_desc *secs; +- int sec_cnt; ++ size_t sec_cnt; + int btf_maps_shndx; + __u32 btf_maps_sec_btf_id; + int text_shndx; +@@ -3312,10 +3312,15 @@ static int bpf_object__elf_collect(struct bpf_object *obj) + Elf64_Shdr *sh; + + /* ELF section indices are 0-based, but sec #0 is special "invalid" +- * section. e_shnum does include sec #0, so e_shnum is the necessary +- * size of an array to keep all the sections. ++ * section. Since section count retrieved by elf_getshdrnum() does ++ * include sec #0, it is already the necessary size of an array to keep ++ * all the sections. + */ +- obj->efile.sec_cnt = obj->efile.ehdr->e_shnum; ++ if (elf_getshdrnum(obj->efile.elf, &obj->efile.sec_cnt)) { ++ pr_warn("elf: failed to get the number of sections for %s: %s\n", ++ obj->path, elf_errmsg(-1)); ++ return -LIBBPF_ERRNO__FORMAT; ++ } + obj->efile.secs = calloc(obj->efile.sec_cnt, sizeof(*obj->efile.secs)); + if (!obj->efile.secs) + return -ENOMEM; +-- +2.38.0 + diff --git a/libbpf.changes b/libbpf.changes index 2e82a78..9765713 100644 --- a/libbpf.changes +++ b/libbpf.changes @@ -1,3 +1,15 @@ +------------------------------------------------------------------- +Tue Nov 8 06:03:55 UTC 2022 - Shung-Hsi Yu + +- Fix out-of-bound heap write (boo#1194248 boo#1194249 CVE-2021-45940 CVE-2021-45941) + + libbpf-Use-elf_getshdrnum-instead-of-e_shnum.patch +- Fix use-after-free in btf_dump_name_dups (boo#1204391 CVE-2022-3534) + + libbpf-Fix-use-after-free-in-btf_dump_name_dups.patch +- Fix memory leak in parse_usdt_arg() (boo#1204393 CVE-2022-3533) + + libbpf-Fix-memory-leak-in-parse_usdt_arg.patch +- Fix null pointer dereference in find_prog_by_sec_insn() (boo#1204502 CVE-2022-3606) + + libbpf-Fix-null-pointer-dereference-in-find_prog_by_.patch + ------------------------------------------------------------------- Tue Oct 4 17:22:08 UTC 2022 - Dirk Müller diff --git a/libbpf.spec b/libbpf.spec index 20685f0..9b568a9 100644 --- a/libbpf.spec +++ b/libbpf.spec @@ -26,6 +26,10 @@ License: LGPL-2.1-only URL: https://github.com/libbpf/libbpf Source: https://github.com/libbpf/libbpf/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.gz Source99: baselibs.conf +Patch1: libbpf-Fix-use-after-free-in-btf_dump_name_dups.patch +Patch2: libbpf-Fix-memory-leak-in-parse_usdt_arg.patch +Patch3: libbpf-Use-elf_getshdrnum-instead-of-e_shnum.patch +Patch4: libbpf-Fix-null-pointer-dereference-in-find_prog_by_.patch BuildRequires: libelf-devel BuildRequires: linux-glibc-devel >= 4.5 BuildRequires: zlib-devel @@ -57,7 +61,7 @@ Requires: linux-glibc-devel >= 5.16 libbpf is a C library which provides API for managing eBPF programs and maps. %prep -%autosetup +%autosetup -p1 %build %global _lto_cflags %{_lto_cflags} -ffat-lto-objects