Commit 573b3aa6 authored by Yonghong Song's avatar Yonghong Song Committed by Daniel Borkmann

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: 71bb428f ("tools: bpf: add bpftool")
Signed-off-by: default avatarYonghong Song <yhs@fb.com>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
parent 61f4b237
...@@ -36,6 +36,7 @@ ...@@ -36,6 +36,7 @@
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <linux/kernel.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
...@@ -90,7 +91,8 @@ static bool map_is_map_of_progs(__u32 type) ...@@ -90,7 +91,8 @@ static bool map_is_map_of_progs(__u32 type)
static void *alloc_value(struct bpf_map_info *info) static void *alloc_value(struct bpf_map_info *info)
{ {
if (map_is_per_cpu(info->type)) if (map_is_per_cpu(info->type))
return malloc(info->value_size * get_possible_cpus()); return malloc(round_up(info->value_size, 8) *
get_possible_cpus());
else else
return malloc(info->value_size); return malloc(info->value_size);
} }
...@@ -161,9 +163,10 @@ static void print_entry_json(struct bpf_map_info *info, unsigned char *key, ...@@ -161,9 +163,10 @@ static void print_entry_json(struct bpf_map_info *info, unsigned char *key,
jsonw_name(json_wtr, "value"); jsonw_name(json_wtr, "value");
print_hex_data_json(value, info->value_size); print_hex_data_json(value, info->value_size);
} else { } else {
unsigned int i, n; unsigned int i, n, step;
n = get_possible_cpus(); n = get_possible_cpus();
step = round_up(info->value_size, 8);
jsonw_name(json_wtr, "key"); jsonw_name(json_wtr, "key");
print_hex_data_json(key, info->key_size); print_hex_data_json(key, info->key_size);
...@@ -176,7 +179,7 @@ static void print_entry_json(struct bpf_map_info *info, unsigned char *key, ...@@ -176,7 +179,7 @@ static void print_entry_json(struct bpf_map_info *info, unsigned char *key,
jsonw_int_field(json_wtr, "cpu", i); jsonw_int_field(json_wtr, "cpu", i);
jsonw_name(json_wtr, "value"); jsonw_name(json_wtr, "value");
print_hex_data_json(value + i * info->value_size, print_hex_data_json(value + i * step,
info->value_size); info->value_size);
jsonw_end_object(json_wtr); jsonw_end_object(json_wtr);
...@@ -207,9 +210,10 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key, ...@@ -207,9 +210,10 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,
printf("\n"); printf("\n");
} else { } else {
unsigned int i, n; unsigned int i, n, step;
n = get_possible_cpus(); n = get_possible_cpus();
step = round_up(info->value_size, 8);
printf("key:\n"); printf("key:\n");
fprint_hex(stdout, key, info->key_size, " "); fprint_hex(stdout, key, info->key_size, " ");
...@@ -217,7 +221,7 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key, ...@@ -217,7 +221,7 @@ static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
printf("value (CPU %02d):%c", printf("value (CPU %02d):%c",
i, info->value_size > 16 ? '\n' : ' '); i, info->value_size > 16 ? '\n' : ' ');
fprint_hex(stdout, value + i * info->value_size, fprint_hex(stdout, value + i * step,
info->value_size, " "); info->value_size, " ");
printf("\n"); printf("\n");
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment