Skip to content

Commit be6ba7d

Browse files
eddyz87Kernel Patches Daemon
authored andcommitted
bpf: correct stack liveness for tail calls
This updates bpf_insn_successors() reflecting that control flow might jump over the instructions between tail call and function exit, verifier might assume that some writes to parent stack always happen, which is not the case. Signed-off-by: Eduard Zingerman <[email protected]> Signed-off-by: Martin Teichmann <[email protected]>
1 parent 61654df commit be6ba7d

File tree

3 files changed

+34
-7
lines changed

3 files changed

+34
-7
lines changed

include/linux/bpf_verifier.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,6 @@ struct bpf_insn_aux_data {
527527
struct {
528528
u32 map_index; /* index into used_maps[] */
529529
u32 map_off; /* offset from value base address */
530-
struct bpf_iarray *jt; /* jump table for gotox instruction */
531530
};
532531
struct {
533532
enum bpf_reg_type reg_type; /* type of pseudo_btf_id */
@@ -550,6 +549,7 @@ struct bpf_insn_aux_data {
550549
/* remember the offset of node field within type to rewrite */
551550
u64 insert_off;
552551
};
552+
struct bpf_iarray *jt; /* jump table for gotox or bpf_tailcall call instruction */
553553
struct btf_struct_meta *kptr_struct_meta;
554554
u64 map_key_state; /* constant (32 bit) key tracking for maps */
555555
int ctx_field_size; /* the ctx field size for load insn, maybe 0 */
@@ -652,6 +652,7 @@ struct bpf_subprog_info {
652652
u32 start; /* insn idx of function entry point */
653653
u32 linfo_idx; /* The idx to the main_prog->aux->linfo */
654654
u32 postorder_start; /* The idx to the env->cfg.insn_postorder */
655+
u32 exit_idx; /* Index of one of the BPF_EXIT instructions in this subprogram */
655656
u16 stack_depth; /* max. stack depth used by this function */
656657
u16 stack_extra;
657658
/* offsets in range [stack_depth .. fastcall_stack_off)
@@ -669,9 +670,9 @@ struct bpf_subprog_info {
669670
bool keep_fastcall_stack: 1;
670671
bool changes_pkt_data: 1;
671672
bool might_sleep: 1;
673+
u8 arg_cnt:3;
672674

673675
enum priv_stack_mode priv_stack_mode;
674-
u8 arg_cnt;
675676
struct bpf_subprog_arg_info args[MAX_BPF_FUNC_REG_ARGS];
676677
};
677678

kernel/bpf/liveness.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -482,11 +482,12 @@ bpf_insn_successors(struct bpf_verifier_env *env, u32 idx)
482482
struct bpf_prog *prog = env->prog;
483483
struct bpf_insn *insn = &prog->insnsi[idx];
484484
const struct opcode_info *opcode_info;
485-
struct bpf_iarray *succ;
485+
struct bpf_iarray *succ, *jt;
486486
int insn_sz;
487487

488-
if (unlikely(insn_is_gotox(insn)))
489-
return env->insn_aux_data[idx].jt;
488+
jt = env->insn_aux_data[idx].jt;
489+
if (unlikely(jt))
490+
return jt;
490491

491492
/* pre-allocated array of size up to 2; reset cnt, as it may have been used already */
492493
succ = env->succ;

kernel/bpf/verifier.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3555,8 +3555,12 @@ static int check_subprogs(struct bpf_verifier_env *env)
35553555
subprog[cur_subprog].has_ld_abs = true;
35563556
if (BPF_CLASS(code) != BPF_JMP && BPF_CLASS(code) != BPF_JMP32)
35573557
goto next;
3558-
if (BPF_OP(code) == BPF_EXIT || BPF_OP(code) == BPF_CALL)
3558+
if (BPF_OP(code) == BPF_CALL)
35593559
goto next;
3560+
if (BPF_OP(code) == BPF_EXIT) {
3561+
subprog[cur_subprog].exit_idx = i;
3562+
goto next;
3563+
}
35603564
off = i + bpf_jmp_offset(&insn[i]) + 1;
35613565
if (off < subprog_start || off >= subprog_end) {
35623566
verbose(env, "jump out of range from insn %d to %d\n", i, off);
@@ -18150,6 +18154,25 @@ static int visit_gotox_insn(int t, struct bpf_verifier_env *env)
1815018154
return keep_exploring ? KEEP_EXPLORING : DONE_EXPLORING;
1815118155
}
1815218156

18157+
static int visit_tailcall_insn(struct bpf_verifier_env *env, int t)
18158+
{
18159+
static struct bpf_subprog_info *subprog;
18160+
struct bpf_iarray *jt;
18161+
18162+
if (env->insn_aux_data[t].jt)
18163+
return 0;
18164+
18165+
jt = iarray_realloc(NULL, 2);
18166+
if (!jt)
18167+
return -ENOMEM;
18168+
18169+
subprog = bpf_find_containing_subprog(env, t);
18170+
jt->items[0] = t + 1;
18171+
jt->items[1] = subprog->exit_idx;
18172+
env->insn_aux_data[t].jt = jt;
18173+
return 0;
18174+
}
18175+
1815318176
/* Visits the instruction at index t and returns one of the following:
1815418177
* < 0 - an error occurred
1815518178
* DONE_EXPLORING - the instruction was fully explored
@@ -18210,6 +18233,8 @@ static int visit_insn(int t, struct bpf_verifier_env *env)
1821018233
mark_subprog_might_sleep(env, t);
1821118234
if (bpf_helper_changes_pkt_data(insn->imm))
1821218235
mark_subprog_changes_pkt_data(env, t);
18236+
if (insn->imm == BPF_FUNC_tail_call)
18237+
visit_tailcall_insn(env, t);
1821318238
} else if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
1821418239
struct bpf_kfunc_call_arg_meta meta;
1821518240

@@ -21471,7 +21496,7 @@ static void clear_insn_aux_data(struct bpf_verifier_env *env, int start, int len
2147121496
int i;
2147221497

2147321498
for (i = start; i < end; i++) {
21474-
if (insn_is_gotox(&insns[i])) {
21499+
if (aux_data[i].jt) {
2147521500
kvfree(aux_data[i].jt);
2147621501
aux_data[i].jt = NULL;
2147721502
}

0 commit comments

Comments
 (0)