Skip to content

Commit fa6f092

Browse files
amscanneanakryiko
authored andcommitted
libbpf: Fix possible use-after-free for externs
The `name` field in `obj->externs` points into the BTF data at initial open time. However, some functions may invalidate this after opening and before loading (e.g. `bpf_map__set_value_size`), which results in pointers into freed memory and undefined behavior. The simplest solution is to simply `strdup` these strings, similar to the `essent_name`, and free them at the same time. In order to test this path, the `global_map_resize` BPF selftest is modified slightly to ensure the presence of an extern, which causes this test to fail prior to the fix. Given there isn't an obvious API or error to test against, I opted to add this to the existing test as an aspect of the resizing feature rather than duplicate the test. Fixes: 9d0a233 ("libbpf: Add capability for resizing datasec maps") Signed-off-by: Adin Scannell <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 1901139 commit fa6f092

File tree

2 files changed

+23
-3
lines changed

2 files changed

+23
-3
lines changed

tools/lib/bpf/libbpf.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ struct extern_desc {
597597
int sym_idx;
598598
int btf_id;
599599
int sec_btf_id;
600-
const char *name;
600+
char *name;
601601
char *essent_name;
602602
bool is_set;
603603
bool is_weak;
@@ -4259,7 +4259,9 @@ static int bpf_object__collect_externs(struct bpf_object *obj)
42594259
return ext->btf_id;
42604260
}
42614261
t = btf__type_by_id(obj->btf, ext->btf_id);
4262-
ext->name = btf__name_by_offset(obj->btf, t->name_off);
4262+
ext->name = strdup(btf__name_by_offset(obj->btf, t->name_off));
4263+
if (!ext->name)
4264+
return -ENOMEM;
42634265
ext->sym_idx = i;
42644266
ext->is_weak = ELF64_ST_BIND(sym->st_info) == STB_WEAK;
42654267

@@ -9138,8 +9140,10 @@ void bpf_object__close(struct bpf_object *obj)
91389140
zfree(&obj->btf_custom_path);
91399141
zfree(&obj->kconfig);
91409142

9141-
for (i = 0; i < obj->nr_extern; i++)
9143+
for (i = 0; i < obj->nr_extern; i++) {
9144+
zfree(&obj->externs[i].name);
91429145
zfree(&obj->externs[i].essent_name);
9146+
}
91439147

91449148
zfree(&obj->externs);
91459149
obj->nr_extern = 0;

tools/testing/selftests/bpf/progs/test_global_map_resize.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ int my_int_last SEC(".data.array_not_last");
3232

3333
int percpu_arr[1] SEC(".data.percpu_arr");
3434

35+
/* at least one extern is included, to ensure that a specific
36+
* regression is tested whereby resizing resulted in a free-after-use
37+
* bug after type information is invalidated by the resize operation.
38+
*
39+
* There isn't a particularly good API to test for this specific condition,
40+
* but by having externs for the resizing tests it will cover this path.
41+
*/
42+
extern int LINUX_KERNEL_VERSION __kconfig;
43+
long version_sink;
44+
3545
SEC("tp/syscalls/sys_enter_getpid")
3646
int bss_array_sum(void *ctx)
3747
{
@@ -44,6 +54,9 @@ int bss_array_sum(void *ctx)
4454
for (size_t i = 0; i < bss_array_len; ++i)
4555
sum += array[i];
4656

57+
/* see above; ensure this is not optimized out */
58+
version_sink = LINUX_KERNEL_VERSION;
59+
4760
return 0;
4861
}
4962

@@ -59,6 +72,9 @@ int data_array_sum(void *ctx)
5972
for (size_t i = 0; i < data_array_len; ++i)
6073
sum += my_array[i];
6174

75+
/* see above; ensure this is not optimized out */
76+
version_sink = LINUX_KERNEL_VERSION;
77+
6278
return 0;
6379
}
6480

0 commit comments

Comments
 (0)