Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

tools/bpftool: fix a percpu_array map dump problem

I hit the following problem when I tried to use bpftool
to dump a percpu array.

$ sudo ./bpftool map show
61: percpu_array name stub flags 0x0
key 4B value 4B max_entries 1 memlock 4096B
...
$ sudo ./bpftool map dump id 61
bpftool: malloc.c:2406: sysmalloc: Assertion
`(old_top == initial_top (av) && old_size == 0) || \
((unsigned long) (old_size) >= MINSIZE && \
prev_inuse (old_top) && \
((unsigned long) old_end & (pagesize - 1)) == 0)'
failed.
Aborted

Further debugging revealed that this is due to
miscommunication between bpftool and kernel.
For example, for the above percpu_array with value size of 4B.
The map info returned to user space has value size of 4B.

In bpftool, the values array for lookup is allocated like:
info->value_size * get_possible_cpus() = 4 * get_possible_cpus()
In kernel (kernel/bpf/syscall.c), the values array size is
rounded up to multiple of 8.
round_up(map->value_size, 8) * num_possible_cpus()
= 8 * num_possible_cpus()
So when kernel copies the values to user buffer, the kernel will
overwrite beyond user buffer boundary.

This patch fixed the issue by allocating and stepping through
percpu map value array properly in bpftool.

Fixes: 71bb428fe2c19 ("tools: bpf: add bpftool")
Signed-off-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>

authored by

Yonghong Song and committed by
Daniel Borkmann
573b3aa6 61f4b237

+9 -5
+9 -5
tools/bpf/bpftool/map.c
··· 36 36 #include <assert.h> 37 37 #include <errno.h> 38 38 #include <fcntl.h> 39 + #include <linux/kernel.h> 39 40 #include <stdbool.h> 40 41 #include <stdio.h> 41 42 #include <stdlib.h> ··· 91 90 static void *alloc_value(struct bpf_map_info *info) 92 91 { 93 92 if (map_is_per_cpu(info->type)) 94 - return malloc(info->value_size * get_possible_cpus()); 93 + return malloc(round_up(info->value_size, 8) * 94 + get_possible_cpus()); 95 95 else 96 96 return malloc(info->value_size); 97 97 } ··· 163 161 jsonw_name(json_wtr, "value"); 164 162 print_hex_data_json(value, info->value_size); 165 163 } else { 166 - unsigned int i, n; 164 + unsigned int i, n, step; 167 165 168 166 n = get_possible_cpus(); 167 + step = round_up(info->value_size, 8); 169 168 170 169 jsonw_name(json_wtr, "key"); 171 170 print_hex_data_json(key, info->key_size); ··· 179 176 jsonw_int_field(json_wtr, "cpu", i); 180 177 181 178 jsonw_name(json_wtr, "value"); 182 - print_hex_data_json(value + i * info->value_size, 179 + print_hex_data_json(value + i * step, 183 180 info->value_size); 184 181 185 182 jsonw_end_object(json_wtr); ··· 210 207 211 208 printf("\n"); 212 209 } else { 213 - unsigned int i, n; 210 + unsigned int i, n, step; 214 211 215 212 n = get_possible_cpus(); 213 + step = round_up(info->value_size, 8); 216 214 217 215 printf("key:\n"); 218 216 fprint_hex(stdout, key, info->key_size, " "); ··· 221 217 for (i = 0; i < n; i++) { 222 218 printf("value (CPU %02d):%c", 223 219 i, info->value_size > 16 ? '\n' : ' '); 224 - fprint_hex(stdout, value + i * info->value_size, 220 + fprint_hex(stdout, value + i * step, 225 221 info->value_size, " "); 226 222 printf("\n"); 227 223 }