mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 08:15:07 -04:00
bpf: Track verifier reference diagnostic events
Add reference acquire and release events to diagnostic history so Resource Lifetime Safety reports can show the lifetime of a specific reference id along the path. Record acquisitions after the verifier assigns the reference id. Record releases only after release_reference_nomark() succeeds, including the kptr_xchg RCU conversion path and owning-to-non-owning conversion path that consume an owning reference. Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com> Acked-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://patch.msgid.link/20260815064612.378577-7-memxor@gmail.com Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
This commit is contained in:
committed by
Eduard Zingerman
parent
af4ea6e20f
commit
9ecd70304e
@@ -102,6 +102,8 @@ struct bpf_diag_reg_snapshot {
|
||||
enum bpf_diag_history_kind {
|
||||
BPF_DIAG_HISTORY_BRANCH,
|
||||
BPF_DIAG_HISTORY_MOD,
|
||||
BPF_DIAG_HISTORY_REF_ACQUIRE,
|
||||
BPF_DIAG_HISTORY_REF_RELEASE,
|
||||
};
|
||||
|
||||
struct bpf_diag_history_event {
|
||||
@@ -119,6 +121,9 @@ struct bpf_diag_history_event {
|
||||
u8 reason;
|
||||
bool origin_valid;
|
||||
} mod;
|
||||
struct {
|
||||
u32 ref_id;
|
||||
} ref;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1021,3 +1026,26 @@ void bpf_diag_record_scrub_stack(struct bpf_verifier_env *env,
|
||||
diag_stack_range_target(state->diag_frame_id, state->frameno, min_off, max_off),
|
||||
reason, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static void diag_record_ref(struct bpf_verifier_env *env, u32 insn_idx, u8 kind, u32 ref_id)
|
||||
{
|
||||
struct bpf_diag_history_event event = {
|
||||
.insn_idx = insn_idx,
|
||||
.kind = kind,
|
||||
.ref = {
|
||||
.ref_id = ref_id,
|
||||
},
|
||||
};
|
||||
|
||||
diag_append_history(env, &event);
|
||||
}
|
||||
|
||||
void bpf_diag_record_ref_acquire(struct bpf_verifier_env *env, u32 insn_idx, u32 ref_id)
|
||||
{
|
||||
diag_record_ref(env, insn_idx, BPF_DIAG_HISTORY_REF_ACQUIRE, ref_id);
|
||||
}
|
||||
|
||||
void bpf_diag_record_ref_release(struct bpf_verifier_env *env, u32 insn_idx, u32 ref_id)
|
||||
{
|
||||
diag_record_ref(env, insn_idx, BPF_DIAG_HISTORY_REF_RELEASE, ref_id);
|
||||
}
|
||||
|
||||
@@ -43,5 +43,7 @@ void bpf_diag_record_scrub(struct bpf_verifier_env *env, const struct bpf_reg_st
|
||||
void bpf_diag_record_scrub_stack(struct bpf_verifier_env *env,
|
||||
const struct bpf_func_state *state, s16 min_off, s16 max_off,
|
||||
enum bpf_diag_mod_reason reason);
|
||||
void bpf_diag_record_ref_acquire(struct bpf_verifier_env *env, u32 insn_idx, u32 ref_id);
|
||||
void bpf_diag_record_ref_release(struct bpf_verifier_env *env, u32 insn_idx, u32 ref_id);
|
||||
|
||||
#endif /* __BPF_DIAGNOSTICS_H */
|
||||
|
||||
@@ -205,7 +205,8 @@ struct bpf_verifier_stack_elem {
|
||||
#define BPF_PRIV_STACK_MIN_SIZE 64
|
||||
|
||||
static int acquire_reference(struct bpf_verifier_env *env, int insn_idx, int parent_id);
|
||||
static int release_reference_nomark(struct bpf_verifier_state *state, int id);
|
||||
static int __release_reference_nomark(struct bpf_verifier_state *state, int id);
|
||||
static int release_reference_nomark(struct bpf_verifier_env *env, int id);
|
||||
static int release_reference(struct bpf_verifier_env *env, int id);
|
||||
static void invalidate_non_owning_refs(struct bpf_verifier_env *env);
|
||||
static void invalidate_rcu_protected_refs(struct bpf_verifier_env *env);
|
||||
@@ -1418,6 +1419,7 @@ static int acquire_reference(struct bpf_verifier_env *env, int insn_idx, int par
|
||||
s->type = REF_TYPE_PTR;
|
||||
s->id = ++env->id_gen;
|
||||
s->parent_id = parent_id;
|
||||
bpf_diag_record_ref_acquire(env, insn_idx, s->id);
|
||||
return s->id;
|
||||
}
|
||||
|
||||
@@ -9017,7 +9019,7 @@ static void mark_pkt_end(struct bpf_verifier_state *vstate, int regn, bool range
|
||||
reg->range = AT_PKT_END;
|
||||
}
|
||||
|
||||
static int release_reference_nomark(struct bpf_verifier_state *state, int id)
|
||||
static int __release_reference_nomark(struct bpf_verifier_state *state, int id)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -9032,6 +9034,16 @@ static int release_reference_nomark(struct bpf_verifier_state *state, int id)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int release_reference_nomark(struct bpf_verifier_env *env, int id)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = __release_reference_nomark(env->cur_state, id);
|
||||
if (!err)
|
||||
bpf_diag_record_ref_release(env, env->insn_idx, id);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int idstack_push(struct bpf_idmap *idmap, u32 id)
|
||||
{
|
||||
int i;
|
||||
@@ -9074,8 +9086,10 @@ static int release_reference(struct bpf_verifier_env *env, int id)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (find_reference_state(vstate, id))
|
||||
WARN_ON_ONCE(release_reference_nomark(vstate, id));
|
||||
if (find_reference_state(vstate, id)) {
|
||||
err = release_reference_nomark(env, id);
|
||||
WARN_ON_ONCE(err);
|
||||
}
|
||||
|
||||
while ((id = idstack_pop(idstack))) {
|
||||
/*
|
||||
@@ -9164,7 +9178,9 @@ static int ref_convert_alloc_rcu_protected(struct bpf_verifier_env *env, u32 id)
|
||||
struct bpf_reg_state *reg;
|
||||
int err;
|
||||
|
||||
err = release_reference_nomark(env->cur_state, id);
|
||||
err = release_reference_nomark(env, id);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
bpf_for_each_reg_in_vstate(env->cur_state, state, reg, ({
|
||||
if (reg->id != id)
|
||||
@@ -11757,8 +11773,10 @@ static void ref_convert_owning_non_owning(struct bpf_verifier_env *env, u32 id)
|
||||
{
|
||||
struct bpf_func_state *unused;
|
||||
struct bpf_reg_state *reg;
|
||||
int err;
|
||||
|
||||
WARN_ON_ONCE(release_reference_nomark(env->cur_state, id));
|
||||
err = release_reference_nomark(env, id);
|
||||
WARN_ON_ONCE(err);
|
||||
|
||||
bpf_for_each_reg_in_vstate(env->cur_state, unused, reg, ({
|
||||
if (reg->id == id) {
|
||||
@@ -15890,7 +15908,7 @@ static void mark_ptr_or_null_regs(struct bpf_verifier_state *vstate, u32 regno,
|
||||
* No one could have freed the reference state before
|
||||
* doing the NULL check.
|
||||
*/
|
||||
WARN_ON_ONCE(release_reference_nomark(vstate, id));
|
||||
WARN_ON_ONCE(__release_reference_nomark(vstate, id));
|
||||
|
||||
bpf_for_each_reg_in_vstate(vstate, state, reg, ({
|
||||
mark_ptr_or_null_reg(state, reg, id, is_null);
|
||||
|
||||
Reference in New Issue
Block a user