mirror of
https://github.com/torvalds/linux.git
synced 2026-05-26 08:02:27 +02:00
Merge branch 'fix-bpf_throw-global-subprogs-interaction'
Kumar Kartikeya Dwivedi says: ==================== Fix bpf_throw() vs global subprogs interaction There is a bug where bpf_throw()'s reachability across global subprogs is missed by the verifier, leading to successful verification when any kernel resource or lock is held across global subprog call boundary. Fix this by effect summarization like other related side effects and propagate exception reachability into callees. Changelog: ---------- v1 -> v2 v1: https://lore.kernel.org/bpf/20260516022426.2109698-1-memxor@gmail.com * Reorder might_throw bit to avoid bpf-next conflicts. (Alexei) ==================== Link: https://patch.msgid.link/20260517075530.3461166-1-memxor@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
201166d79f
|
|
@ -729,6 +729,7 @@ struct bpf_subprog_info {
|
|||
*/
|
||||
s16 fastcall_stack_off;
|
||||
bool has_tail_call: 1;
|
||||
bool might_throw: 1;
|
||||
bool tail_call_reachable: 1;
|
||||
bool has_ld_abs: 1;
|
||||
bool is_cb: 1;
|
||||
|
|
@ -1308,6 +1309,7 @@ void bpf_fmt_stack_mask(char *buf, ssize_t buf_sz, u64 stack_mask);
|
|||
bool bpf_subprog_is_global(const struct bpf_verifier_env *env, int subprog);
|
||||
|
||||
int bpf_find_subprog(struct bpf_verifier_env *env, int off);
|
||||
bool bpf_is_throw_kfunc(struct bpf_insn *insn);
|
||||
int bpf_compute_const_regs(struct bpf_verifier_env *env);
|
||||
int bpf_prune_dead_branches(struct bpf_verifier_env *env);
|
||||
int bpf_check_cfg(struct bpf_verifier_env *env);
|
||||
|
|
|
|||
|
|
@ -64,11 +64,19 @@ static void mark_subprog_might_sleep(struct bpf_verifier_env *env, int off)
|
|||
subprog->might_sleep = true;
|
||||
}
|
||||
|
||||
static void mark_subprog_might_throw(struct bpf_verifier_env *env, int off)
|
||||
{
|
||||
struct bpf_subprog_info *subprog;
|
||||
|
||||
subprog = bpf_find_containing_subprog(env, off);
|
||||
subprog->might_throw = true;
|
||||
}
|
||||
|
||||
/* 't' is an index of a call-site.
|
||||
* 'w' is a callee entry point.
|
||||
* Eventually this function would be called when env->cfg.insn_state[w] == EXPLORED.
|
||||
* Rely on DFS traversal order and absence of recursive calls to guarantee that
|
||||
* callee's change_pkt_data marks would be correct at that moment.
|
||||
* callee's effect marks would be correct at that moment.
|
||||
*/
|
||||
static void merge_callee_effects(struct bpf_verifier_env *env, int t, int w)
|
||||
{
|
||||
|
|
@ -78,6 +86,7 @@ static void merge_callee_effects(struct bpf_verifier_env *env, int t, int w)
|
|||
callee = bpf_find_containing_subprog(env, w);
|
||||
caller->changes_pkt_data |= callee->changes_pkt_data;
|
||||
caller->might_sleep |= callee->might_sleep;
|
||||
caller->might_throw |= callee->might_throw;
|
||||
}
|
||||
|
||||
enum {
|
||||
|
|
@ -509,6 +518,8 @@ static int visit_insn(int t, struct bpf_verifier_env *env)
|
|||
mark_subprog_might_sleep(env, t);
|
||||
if (ret == 0 && bpf_is_kfunc_pkt_changing(&meta))
|
||||
mark_subprog_changes_pkt_data(env, t);
|
||||
if (ret == 0 && bpf_is_throw_kfunc(insn))
|
||||
mark_subprog_might_throw(env, t);
|
||||
}
|
||||
return visit_func_call_insn(t, insns, env, insn->src_reg == BPF_PSEUDO_CALL);
|
||||
|
||||
|
|
|
|||
|
|
@ -442,7 +442,6 @@ static bool is_dynptr_ref_function(enum bpf_func_id func_id)
|
|||
static bool is_sync_callback_calling_kfunc(u32 btf_id);
|
||||
static bool is_async_callback_calling_kfunc(u32 btf_id);
|
||||
static bool is_callback_calling_kfunc(u32 btf_id);
|
||||
static bool is_bpf_throw_kfunc(struct bpf_insn *insn);
|
||||
|
||||
static bool is_bpf_wq_set_callback_kfunc(u32 btf_id);
|
||||
static bool is_task_work_add_kfunc(u32 func_id);
|
||||
|
|
@ -5405,7 +5404,7 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
|
|||
if (bpf_pseudo_kfunc_call(insn + i) && !insn[i].off) {
|
||||
bool err = false;
|
||||
|
||||
if (!is_bpf_throw_kfunc(insn + i))
|
||||
if (!bpf_is_throw_kfunc(insn + i))
|
||||
continue;
|
||||
for (tmp = idx; tmp >= 0 && !err; tmp = dinfo[tmp].caller) {
|
||||
if (subprog[tmp].is_cb) {
|
||||
|
|
@ -9499,6 +9498,9 @@ static int push_callback_call(struct bpf_verifier_env *env, struct bpf_insn *ins
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int process_bpf_exit_full(struct bpf_verifier_env *env,
|
||||
bool *do_print_state, bool exception_exit);
|
||||
|
||||
static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
|
||||
int *insn_idx)
|
||||
{
|
||||
|
|
@ -9552,6 +9554,17 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
|
|||
caller->regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG;
|
||||
}
|
||||
|
||||
if (env->subprog_info[subprog].might_throw) {
|
||||
struct bpf_verifier_state *branch;
|
||||
|
||||
branch = push_stack(env, *insn_idx + 1, *insn_idx, false);
|
||||
if (IS_ERR(branch)) {
|
||||
verbose(env, "failed to push state for global subprog exception path\n");
|
||||
return PTR_ERR(branch);
|
||||
}
|
||||
return process_bpf_exit_full(env, NULL, true);
|
||||
}
|
||||
|
||||
/* continue with next insn after call */
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -11782,7 +11795,7 @@ static bool is_async_callback_calling_kfunc(u32 btf_id)
|
|||
is_task_work_add_kfunc(btf_id);
|
||||
}
|
||||
|
||||
static bool is_bpf_throw_kfunc(struct bpf_insn *insn)
|
||||
bool bpf_is_throw_kfunc(struct bpf_insn *insn)
|
||||
{
|
||||
return bpf_pseudo_kfunc_call(insn) && insn->off == 0 &&
|
||||
insn->imm == special_kfunc_list[KF_bpf_throw];
|
||||
|
|
@ -12972,8 +12985,6 @@ static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_ca
|
|||
}
|
||||
|
||||
static int check_return_code(struct bpf_verifier_env *env, int regno, const char *reg_name);
|
||||
static int process_bpf_exit_full(struct bpf_verifier_env *env,
|
||||
bool *do_print_state, bool exception_exit);
|
||||
|
||||
static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
|
||||
int *insn_idx_p)
|
||||
|
|
@ -13354,7 +13365,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
|
|||
if (meta.func_id == special_kfunc_list[KF_bpf_session_cookie])
|
||||
env->prog->call_session_cookie = true;
|
||||
|
||||
if (is_bpf_throw_kfunc(insn))
|
||||
if (bpf_is_throw_kfunc(insn))
|
||||
return process_bpf_exit_full(env, NULL, true);
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -208,6 +208,28 @@ int reject_with_reference(void *ctx)
|
|||
return 0;
|
||||
}
|
||||
|
||||
__noinline int global_subprog_may_throw(struct __sk_buff *ctx)
|
||||
{
|
||||
if (ctx->len)
|
||||
bpf_throw(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("Unreleased reference")
|
||||
int reject_global_subprog_throw_with_reference(struct __sk_buff *ctx)
|
||||
{
|
||||
struct foo *f;
|
||||
|
||||
f = bpf_obj_new(typeof(*f));
|
||||
if (!f)
|
||||
return 0;
|
||||
if (ctx->protocol)
|
||||
global_subprog_may_throw(ctx);
|
||||
bpf_obj_drop(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__noinline static int subprog_ref(struct __sk_buff *ctx)
|
||||
{
|
||||
struct foo *f;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user