Skip to content

Commit

Permalink
Merge branch 'bpftool-improvements-kallsymfix'
Browse files Browse the repository at this point in the history
Daniel Borkmann says:

====================
This work adds correlation of maps and calls into the bpftool
xlated dump in order to help debugging and introspection of
loaded BPF progs. First patch makes kallsyms work on subprogs
with bpf calls, and second implements the actual correlation.
Details and example output can be found in the 2nd patch.
====================

Signed-off-by: Alexei Starovoitov <ast@kernel.org>
  • Loading branch information
Alexei Starovoitov committed Dec 21, 2017
2 parents 7d9890e + 7105e82 commit 7466177
Show file tree
Hide file tree
Showing 7 changed files with 379 additions and 35 deletions.
9 changes: 9 additions & 0 deletions include/linux/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <linux/capability.h>
#include <linux/cryptohash.h>
#include <linux/set_memory.h>
#include <linux/kallsyms.h>

#include <net/sch_generic.h>

Expand Down Expand Up @@ -724,6 +725,14 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog);
void bpf_jit_compile(struct bpf_prog *prog);
bool bpf_helper_changes_pkt_data(void *func);

static inline bool bpf_dump_raw_ok(void)
{
/* Reconstruction of call-sites is dependent on kallsyms,
* thus make dump the same restriction.
*/
return kallsyms_show_value() == 1;
}

struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
const struct bpf_insn *patch, u32 len);

Expand Down
4 changes: 3 additions & 1 deletion kernel/bpf/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,9 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_prog *prog)

/* Base function for offset calculation. Needs to go into .text section,
* therefore keeping it non-static as well; will also be used by JITs
* anyway later on, so do not let the compiler omit it.
* anyway later on, so do not let the compiler omit it. This also needs
* to go into kallsyms for correlation from e.g. bpftool, so naming
* must not change.
*/
noinline u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5)
{
Expand Down
65 changes: 54 additions & 11 deletions kernel/bpf/disasm.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,39 @@ static const char * const func_id_str[] = {
};
#undef __BPF_FUNC_STR_FN

const char *func_id_name(int id)
static const char *__func_get_name(const struct bpf_insn_cbs *cbs,
const struct bpf_insn *insn,
char *buff, size_t len)
{
BUILD_BUG_ON(ARRAY_SIZE(func_id_str) != __BPF_FUNC_MAX_ID);

if (insn->src_reg != BPF_PSEUDO_CALL &&
insn->imm >= 0 && insn->imm < __BPF_FUNC_MAX_ID &&
func_id_str[insn->imm])
return func_id_str[insn->imm];

if (cbs && cbs->cb_call)
return cbs->cb_call(cbs->private_data, insn);

if (insn->src_reg == BPF_PSEUDO_CALL)
snprintf(buff, len, "%+d", insn->imm);

return buff;
}

static const char *__func_imm_name(const struct bpf_insn_cbs *cbs,
const struct bpf_insn *insn,
u64 full_imm, char *buff, size_t len)
{
if (cbs && cbs->cb_imm)
return cbs->cb_imm(cbs->private_data, insn, full_imm);

snprintf(buff, len, "0x%llx", (unsigned long long)full_imm);
return buff;
}

const char *func_id_name(int id)
{
if (id >= 0 && id < __BPF_FUNC_MAX_ID && func_id_str[id])
return func_id_str[id];
else
Expand Down Expand Up @@ -83,7 +112,7 @@ static const char *const bpf_jmp_string[16] = {
[BPF_EXIT >> 4] = "exit",
};

static void print_bpf_end_insn(bpf_insn_print_cb verbose,
static void print_bpf_end_insn(bpf_insn_print_t verbose,
struct bpf_verifier_env *env,
const struct bpf_insn *insn)
{
Expand All @@ -92,9 +121,12 @@ static void print_bpf_end_insn(bpf_insn_print_cb verbose,
insn->imm, insn->dst_reg);
}

void print_bpf_insn(bpf_insn_print_cb verbose, struct bpf_verifier_env *env,
const struct bpf_insn *insn, bool allow_ptr_leaks)
void print_bpf_insn(const struct bpf_insn_cbs *cbs,
struct bpf_verifier_env *env,
const struct bpf_insn *insn,
bool allow_ptr_leaks)
{
const bpf_insn_print_t verbose = cbs->cb_print;
u8 class = BPF_CLASS(insn->code);

if (class == BPF_ALU || class == BPF_ALU64) {
Expand Down Expand Up @@ -175,12 +207,15 @@ void print_bpf_insn(bpf_insn_print_cb verbose, struct bpf_verifier_env *env,
*/
u64 imm = ((u64)(insn + 1)->imm << 32) | (u32)insn->imm;
bool map_ptr = insn->src_reg == BPF_PSEUDO_MAP_FD;
char tmp[64];

if (map_ptr && !allow_ptr_leaks)
imm = 0;

verbose(env, "(%02x) r%d = 0x%llx\n", insn->code,
insn->dst_reg, (unsigned long long)imm);
verbose(env, "(%02x) r%d = %s\n",
insn->code, insn->dst_reg,
__func_imm_name(cbs, insn, imm,
tmp, sizeof(tmp)));
} else {
verbose(env, "BUG_ld_%02x\n", insn->code);
return;
Expand All @@ -189,12 +224,20 @@ void print_bpf_insn(bpf_insn_print_cb verbose, struct bpf_verifier_env *env,
u8 opcode = BPF_OP(insn->code);

if (opcode == BPF_CALL) {
if (insn->src_reg == BPF_PSEUDO_CALL)
verbose(env, "(%02x) call pc%+d\n", insn->code,
insn->imm);
else
char tmp[64];

if (insn->src_reg == BPF_PSEUDO_CALL) {
verbose(env, "(%02x) call pc%s\n",
insn->code,
__func_get_name(cbs, insn,
tmp, sizeof(tmp)));
} else {
strcpy(tmp, "unknown");
verbose(env, "(%02x) call %s#%d\n", insn->code,
func_id_name(insn->imm), insn->imm);
__func_get_name(cbs, insn,
tmp, sizeof(tmp)),
insn->imm);
}
} else if (insn->code == (BPF_JMP | BPF_JA)) {
verbose(env, "(%02x) goto pc%+d\n",
insn->code, insn->off);
Expand Down
29 changes: 24 additions & 5 deletions kernel/bpf/disasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,35 @@
#include <linux/bpf.h>
#include <linux/kernel.h>
#include <linux/stringify.h>
#ifndef __KERNEL__
#include <stdio.h>
#include <string.h>
#endif

struct bpf_verifier_env;

extern const char *const bpf_alu_string[16];
extern const char *const bpf_class_string[8];

const char *func_id_name(int id);

struct bpf_verifier_env;
typedef void (*bpf_insn_print_cb)(struct bpf_verifier_env *env,
const char *, ...);
void print_bpf_insn(bpf_insn_print_cb verbose, struct bpf_verifier_env *env,
const struct bpf_insn *insn, bool allow_ptr_leaks);
typedef void (*bpf_insn_print_t)(struct bpf_verifier_env *env,
const char *, ...);
typedef const char *(*bpf_insn_revmap_call_t)(void *private_data,
const struct bpf_insn *insn);
typedef const char *(*bpf_insn_print_imm_t)(void *private_data,
const struct bpf_insn *insn,
__u64 full_imm);

struct bpf_insn_cbs {
bpf_insn_print_t cb_print;
bpf_insn_revmap_call_t cb_call;
bpf_insn_print_imm_t cb_imm;
void *private_data;
};

void print_bpf_insn(const struct bpf_insn_cbs *cbs,
struct bpf_verifier_env *env,
const struct bpf_insn *insn,
bool allow_ptr_leaks);
#endif
93 changes: 88 additions & 5 deletions kernel/bpf/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -937,10 +937,16 @@ static void __bpf_prog_put_rcu(struct rcu_head *rcu)
static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock)
{
if (atomic_dec_and_test(&prog->aux->refcnt)) {
int i;

trace_bpf_prog_put_rcu(prog);
/* bpf_prog_free_id() must be called first */
bpf_prog_free_id(prog, do_idr_lock);

for (i = 0; i < prog->aux->func_cnt; i++)
bpf_prog_kallsyms_del(prog->aux->func[i]);
bpf_prog_kallsyms_del(prog);

call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu);
}
}
Expand Down Expand Up @@ -1552,6 +1558,67 @@ static int bpf_map_get_fd_by_id(const union bpf_attr *attr)
return fd;
}

static const struct bpf_map *bpf_map_from_imm(const struct bpf_prog *prog,
unsigned long addr)
{
int i;

for (i = 0; i < prog->aux->used_map_cnt; i++)
if (prog->aux->used_maps[i] == (void *)addr)
return prog->aux->used_maps[i];
return NULL;
}

static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog)
{
const struct bpf_map *map;
struct bpf_insn *insns;
u64 imm;
int i;

insns = kmemdup(prog->insnsi, bpf_prog_insn_size(prog),
GFP_USER);
if (!insns)
return insns;

for (i = 0; i < prog->len; i++) {
if (insns[i].code == (BPF_JMP | BPF_TAIL_CALL)) {
insns[i].code = BPF_JMP | BPF_CALL;
insns[i].imm = BPF_FUNC_tail_call;
/* fall-through */
}
if (insns[i].code == (BPF_JMP | BPF_CALL) ||
insns[i].code == (BPF_JMP | BPF_CALL_ARGS)) {
if (insns[i].code == (BPF_JMP | BPF_CALL_ARGS))
insns[i].code = BPF_JMP | BPF_CALL;
if (!bpf_dump_raw_ok())
insns[i].imm = 0;
continue;
}

if (insns[i].code != (BPF_LD | BPF_IMM | BPF_DW))
continue;

imm = ((u64)insns[i + 1].imm << 32) | (u32)insns[i].imm;
map = bpf_map_from_imm(prog, imm);
if (map) {
insns[i].src_reg = BPF_PSEUDO_MAP_FD;
insns[i].imm = map->id;
insns[i + 1].imm = 0;
continue;
}

if (!bpf_dump_raw_ok() &&
imm == (unsigned long)prog->aux) {
insns[i].imm = 0;
insns[i + 1].imm = 0;
continue;
}
}

return insns;
}

static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
const union bpf_attr *attr,
union bpf_attr __user *uattr)
Expand Down Expand Up @@ -1602,18 +1669,34 @@ static int bpf_prog_get_info_by_fd(struct bpf_prog *prog,
ulen = info.jited_prog_len;
info.jited_prog_len = prog->jited_len;
if (info.jited_prog_len && ulen) {
uinsns = u64_to_user_ptr(info.jited_prog_insns);
ulen = min_t(u32, info.jited_prog_len, ulen);
if (copy_to_user(uinsns, prog->bpf_func, ulen))
return -EFAULT;
if (bpf_dump_raw_ok()) {
uinsns = u64_to_user_ptr(info.jited_prog_insns);
ulen = min_t(u32, info.jited_prog_len, ulen);
if (copy_to_user(uinsns, prog->bpf_func, ulen))
return -EFAULT;
} else {
info.jited_prog_insns = 0;
}
}

ulen = info.xlated_prog_len;
info.xlated_prog_len = bpf_prog_insn_size(prog);
if (info.xlated_prog_len && ulen) {
struct bpf_insn *insns_sanitized;
bool fault;

if (prog->blinded && !bpf_dump_raw_ok()) {
info.xlated_prog_insns = 0;
goto done;
}
insns_sanitized = bpf_insn_prepare_dump(prog);
if (!insns_sanitized)
return -ENOMEM;
uinsns = u64_to_user_ptr(info.xlated_prog_insns);
ulen = min_t(u32, info.xlated_prog_len, ulen);
if (copy_to_user(uinsns, prog->insnsi, ulen))
fault = copy_to_user(uinsns, insns_sanitized, ulen);
kfree(insns_sanitized);
if (fault)
return -EFAULT;
}

Expand Down
33 changes: 29 additions & 4 deletions kernel/bpf/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -4427,9 +4427,12 @@ static int do_check(struct bpf_verifier_env *env)
}

if (env->log.level) {
const struct bpf_insn_cbs cbs = {
.cb_print = verbose,
};

verbose(env, "%d: ", insn_idx);
print_bpf_insn(verbose, env, insn,
env->allow_ptr_leaks);
print_bpf_insn(&cbs, env, insn, env->allow_ptr_leaks);
}

err = ext_analyzer_insn_hook(env, insn_idx, prev_insn_idx);
Expand Down Expand Up @@ -5017,14 +5020,14 @@ static int jit_subprogs(struct bpf_verifier_env *env)
{
struct bpf_prog *prog = env->prog, **func, *tmp;
int i, j, subprog_start, subprog_end = 0, len, subprog;
struct bpf_insn *insn = prog->insnsi;
struct bpf_insn *insn;
void *old_bpf_func;
int err = -ENOMEM;

if (env->subprog_cnt == 0)
return 0;

for (i = 0; i < prog->len; i++, insn++) {
for (i = 0, insn = prog->insnsi; i < prog->len; i++, insn++) {
if (insn->code != (BPF_JMP | BPF_CALL) ||
insn->src_reg != BPF_PSEUDO_CALL)
continue;
Expand Down Expand Up @@ -5063,7 +5066,10 @@ static int jit_subprogs(struct bpf_verifier_env *env)
goto out_free;
memcpy(func[i]->insnsi, &prog->insnsi[subprog_start],
len * sizeof(struct bpf_insn));
func[i]->type = prog->type;
func[i]->len = len;
if (bpf_prog_calc_tag(func[i]))
goto out_free;
func[i]->is_func = 1;
/* Use bpf_prog_F_tag to indicate functions in stack traces.
* Long term would need debug info to populate names
Expand Down Expand Up @@ -5113,6 +5119,25 @@ static int jit_subprogs(struct bpf_verifier_env *env)
bpf_prog_lock_ro(func[i]);
bpf_prog_kallsyms_add(func[i]);
}

/* Last step: make now unused interpreter insns from main
* prog consistent for later dump requests, so they can
* later look the same as if they were interpreted only.
*/
for (i = 0, insn = prog->insnsi; i < prog->len; i++, insn++) {
unsigned long addr;

if (insn->code != (BPF_JMP | BPF_CALL) ||
insn->src_reg != BPF_PSEUDO_CALL)
continue;
insn->off = env->insn_aux_data[i].call_imm;
subprog = find_subprog(env, i + insn->off + 1);
addr = (unsigned long)func[subprog + 1]->bpf_func;
addr &= PAGE_MASK;
insn->imm = (u64 (*)(u64, u64, u64, u64, u64))
addr - __bpf_call_base;
}

prog->jited = 1;
prog->bpf_func = func[0]->bpf_func;
prog->aux->func = func;
Expand Down
Loading

0 comments on commit 7466177

Please sign in to comment.