Skip to content

Commit

Permalink
bpf: fix maximum stack depth tracking logic
Browse files Browse the repository at this point in the history
Instead of computing max stack depth for current call chain
during the main verifier pass track stack depth of each
function independently and after do_check() is done do
another pass over all instructions analyzing depth
of all possible call stacks.

Fixes: f4d7e40 ("bpf: introduce function calls (verification)")
Reported-by: Jann Horn <jannh@google.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
  • Loading branch information
Alexei Starovoitov authored and Daniel Borkmann committed Dec 27, 2017
1 parent 6b80ad2 commit 70a87ff
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 16 deletions.
1 change: 1 addition & 0 deletions include/linux/bpf_verifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ struct bpf_verifier_env {
struct bpf_insn_aux_data *insn_aux_data; /* array of per-insn state */
struct bpf_verifer_log log;
u32 subprog_starts[BPF_MAX_SUBPROGS];
/* computes the stack depth of each bpf function */
u16 subprog_stack_depth[BPF_MAX_SUBPROGS + 1];
u32 subprog_cnt;
};
Expand Down
82 changes: 66 additions & 16 deletions kernel/bpf/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -1430,33 +1430,80 @@ static int update_stack_depth(struct bpf_verifier_env *env,
const struct bpf_func_state *func,
int off)
{
u16 stack = env->subprog_stack_depth[func->subprogno], total = 0;
struct bpf_verifier_state *cur = env->cur_state;
int i;
u16 stack = env->subprog_stack_depth[func->subprogno];

if (stack >= -off)
return 0;

/* update known max for given subprogram */
env->subprog_stack_depth[func->subprogno] = -off;
return 0;
}

/* compute the total for current call chain */
for (i = 0; i <= cur->curframe; i++) {
u32 depth = env->subprog_stack_depth[cur->frame[i]->subprogno];

/* round up to 32-bytes, since this is granularity
* of interpreter stack sizes
*/
depth = round_up(depth, 32);
total += depth;
}
/* starting from main bpf function walk all instructions of the function
* and recursively walk all callees that given function can call.
* Ignore jump and exit insns.
* Since recursion is prevented by check_cfg() this algorithm
* only needs a local stack of MAX_CALL_FRAMES to remember callsites
*/
static int check_max_stack_depth(struct bpf_verifier_env *env)
{
int depth = 0, frame = 0, subprog = 0, i = 0, subprog_end;
struct bpf_insn *insn = env->prog->insnsi;
int insn_cnt = env->prog->len;
int ret_insn[MAX_CALL_FRAMES];
int ret_prog[MAX_CALL_FRAMES];

if (total > MAX_BPF_STACK) {
process_func:
/* round up to 32-bytes, since this is granularity
* of interpreter stack size
*/
depth += round_up(max_t(u32, env->subprog_stack_depth[subprog], 1), 32);
if (depth > MAX_BPF_STACK) {
verbose(env, "combined stack size of %d calls is %d. Too large\n",
cur->curframe, total);
frame + 1, depth);
return -EACCES;
}
return 0;
continue_func:
if (env->subprog_cnt == subprog)
subprog_end = insn_cnt;
else
subprog_end = env->subprog_starts[subprog];
for (; i < subprog_end; i++) {
if (insn[i].code != (BPF_JMP | BPF_CALL))
continue;
if (insn[i].src_reg != BPF_PSEUDO_CALL)
continue;
/* remember insn and function to return to */
ret_insn[frame] = i + 1;
ret_prog[frame] = subprog;

/* find the callee */
i = i + insn[i].imm + 1;
subprog = find_subprog(env, i);
if (subprog < 0) {
WARN_ONCE(1, "verifier bug. No program starts at insn %d\n",
i);
return -EFAULT;
}
subprog++;
frame++;
if (frame >= MAX_CALL_FRAMES) {
WARN_ONCE(1, "verifier bug. Call stack is too deep\n");
return -EFAULT;
}
goto process_func;
}
/* end of for() loop means the last insn of the 'subprog'
* was reached. Doesn't matter whether it was JA or EXIT
*/
if (frame == 0)
return 0;
depth -= round_up(max_t(u32, env->subprog_stack_depth[subprog], 1), 32);
frame--;
i = ret_insn[frame];
subprog = ret_prog[frame];
goto continue_func;
}

static int get_callee_stack_depth(struct bpf_verifier_env *env,
Expand Down Expand Up @@ -5403,6 +5450,9 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr)
if (ret == 0)
sanitize_dead_code(env);

if (ret == 0)
ret = check_max_stack_depth(env);

if (ret == 0)
/* program is valid, convert *(u32*)(ctx + off) accesses */
ret = convert_ctx_accesses(env);
Expand Down

0 comments on commit 70a87ff

Please sign in to comment.