Skip to content

Commit 0250179

Browse files
committed
bpf: properly verify tail call behavior
A successful ebpf tail call does not return to the caller, but to the caller-of-the-caller, often just finishing the ebpf program altogether. Any restrictions that the verifier needs to take into account - notably the fact that the tail call might have modified packet pointers - are to be checked on the caller-of-the-caller. Checking it on the caller made the verifier refuse perfectly fine programs that would use the packet pointers after a tail call, which is no problem as this code is only executed if the tail call was unsuccessful, i.e. nothing happened. This patch simulates the behavior of a tail call in the verifier. A conditional jump to the code after the tail call is added for the case of an unsucessful tail call, and a return to the caller is simulated for a successful tail call. For the successful case we assume that the tail call returns an int, as tail calls are currently only allowed in functions that return and int. We always assume that the tail call modified the packet pointers, as we do not know what the tail call did. For the unsuccessful case we know nothing happened, so we do not need to add new constraints. This approach also allows to check other problems that may occur with tail calls, namely we are now able to check that precision is properly propagated into subprograms using tail calls, as well as checking the live slots in such a subprogram. Fixes: 1a4607f ("bpf: consider that tail calls invalidate packet pointers") Link: https://lore.kernel.org/bpf/20251029105828.1488347-1-martin.teichmann@xfel.eu/ Signed-off-by: Martin Teichmann <martin.teichmann@xfel.eu>
1 parent b6555d5 commit 0250179

File tree

2 files changed

+32
-3
lines changed

2 files changed

+32
-3
lines changed

kernel/bpf/liveness.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,10 @@ bpf_insn_successors(struct bpf_verifier_env *env, u32 idx)
500500
if (opcode_info->can_jump)
501501
succ->items[succ->cnt++] = idx + bpf_jmp_offset(insn) + 1;
502502

503+
if (unlikely(insn->code == (BPF_JMP | BPF_CALL) && insn->src_reg == 0
504+
&& insn->imm == BPF_FUNC_tail_call))
505+
succ->items[succ->cnt++] = idx;
506+
503507
return succ;
504508
}
505509

kernel/bpf/verifier.c

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4411,6 +4411,11 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
44114411
bt_reg_mask(bt));
44124412
return -EFAULT;
44134413
}
4414+
if (insn->src_reg == BPF_REG_0 && insn->imm == BPF_FUNC_tail_call
4415+
&& subseq_idx - idx != 1) {
4416+
if (bt_subprog_enter(bt))
4417+
return -EFAULT;
4418+
}
44144419
} else if (opcode == BPF_EXIT) {
44154420
bool r0_precise;
44164421

@@ -11034,6 +11039,10 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
1103411039
bool in_callback_fn;
1103511040
int err;
1103611041

11042+
err = bpf_update_live_stack(env);
11043+
if (err)
11044+
return err;
11045+
1103711046
callee = state->frame[state->curframe];
1103811047
r0 = &callee->regs[BPF_REG_0];
1103911048
if (r0->type == PTR_TO_STACK) {
@@ -11940,6 +11949,25 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
1194011949
env->prog->call_get_func_ip = true;
1194111950
}
1194211951

11952+
if (func_id == BPF_FUNC_tail_call) {
11953+
if (env->cur_state->curframe) {
11954+
struct bpf_verifier_state *branch;
11955+
11956+
mark_reg_scratched(env, BPF_REG_0);
11957+
branch = push_stack(env, env->insn_idx + 1, env->insn_idx, false);
11958+
if (IS_ERR(branch))
11959+
return PTR_ERR(branch);
11960+
clear_all_pkt_pointers(env);
11961+
mark_reg_unknown(env, regs, BPF_REG_0);
11962+
err = prepare_func_exit(env, &env->insn_idx);
11963+
if (err)
11964+
return err;
11965+
env->insn_idx--;
11966+
} else {
11967+
changes_data = false;
11968+
}
11969+
}
11970+
1194311971
if (changes_data)
1194411972
clear_all_pkt_pointers(env);
1194511973
return 0;
@@ -20110,9 +20138,6 @@ static int process_bpf_exit_full(struct bpf_verifier_env *env,
2011020138
return PROCESS_BPF_EXIT;
2011120139

2011220140
if (env->cur_state->curframe) {
20113-
err = bpf_update_live_stack(env);
20114-
if (err)
20115-
return err;
2011620141
/* exit from nested function */
2011720142
err = prepare_func_exit(env, &env->insn_idx);
2011820143
if (err)

0 commit comments

Comments
 (0)