summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--kernel/bpf/diagnostics.c39
-rw-r--r--kernel/bpf/diagnostics.h12
-rw-r--r--kernel/bpf/verifier.c28
3 files changed, 74 insertions, 5 deletions
diff --git a/kernel/bpf/diagnostics.c b/kernel/bpf/diagnostics.c
index ddeaff1e90b7..15bca8a02a48 100644
--- a/kernel/bpf/diagnostics.c
+++ b/kernel/bpf/diagnostics.c
@@ -104,6 +104,7 @@ enum bpf_diag_history_kind {
BPF_DIAG_HISTORY_MOD,
BPF_DIAG_HISTORY_REF_ACQUIRE,
BPF_DIAG_HISTORY_REF_RELEASE,
+ BPF_DIAG_HISTORY_CONTEXT,
};
struct bpf_diag_history_event {
@@ -124,6 +125,11 @@ struct bpf_diag_history_event {
struct {
u32 ref_id;
} ref;
+ struct {
+ u32 depth;
+ u8 kind;
+ bool enter;
+ } ctx;
};
};
@@ -388,6 +394,19 @@ void bpf_diag_event_log_restore(struct bpf_verifier_env *env, u64 log_pos)
log->cnt = log_pos - log->first_seq;
}
+u32 bpf_diag_irq_depth(const struct bpf_verifier_state *state)
+{
+ u32 depth = 0;
+ int i;
+
+ for (i = 0; i < state->acquired_refs; i++) {
+ if (state->refs[i].type == REF_TYPE_IRQ)
+ depth++;
+ }
+
+ return depth;
+}
+
static void diag_append_history(struct bpf_verifier_env *env,
const struct bpf_diag_history_event *event)
{
@@ -1049,3 +1068,23 @@ void bpf_diag_record_ref_release(struct bpf_verifier_env *env, u32 insn_idx, u32
{
diag_record_ref(env, insn_idx, BPF_DIAG_HISTORY_REF_RELEASE, ref_id);
}
+
+void bpf_diag_record_context(struct bpf_verifier_env *env, u32 insn_idx,
+ enum bpf_diag_context_kind ctx_kind, bool enter, u32 depth)
+{
+ /*
+ * Keep leave events so context rendering can stop at a depth-zero exit
+ * and show nested-region depth accurately for the active path.
+ */
+ struct bpf_diag_history_event event = {
+ .insn_idx = insn_idx,
+ .kind = BPF_DIAG_HISTORY_CONTEXT,
+ .ctx = {
+ .kind = ctx_kind,
+ .enter = enter,
+ .depth = depth,
+ },
+ };
+
+ diag_append_history(env, &event);
+}
diff --git a/kernel/bpf/diagnostics.h b/kernel/bpf/diagnostics.h
index d17b498a3f66..ed64776736c6 100644
--- a/kernel/bpf/diagnostics.h
+++ b/kernel/bpf/diagnostics.h
@@ -11,6 +11,7 @@
struct bpf_func_state;
struct bpf_reg_state;
struct bpf_verifier_env;
+struct bpf_verifier_state;
struct btf;
enum bpf_diag_mod_reason {
@@ -23,6 +24,14 @@ enum bpf_diag_mod_reason {
BPF_DIAG_MOD_CALLER_SAVED,
};
+enum bpf_diag_context_kind {
+ BPF_DIAG_CONTEXT_NONE,
+ BPF_DIAG_CONTEXT_RCU,
+ BPF_DIAG_CONTEXT_PREEMPT,
+ BPF_DIAG_CONTEXT_IRQ,
+ BPF_DIAG_CONTEXT_LOCK,
+};
+
bool bpf_diag_enabled(const struct bpf_verifier_env *env);
int bpf_diag_init(struct bpf_verifier_env *env);
void bpf_diag_init_frame(struct bpf_verifier_env *env, struct bpf_func_state *state);
@@ -33,6 +42,7 @@ const char *bpf_diag_fmt(struct bpf_verifier_env *env, const char *fmt, ...) __p
const char *bpf_diag_fmt_btf_type(struct bpf_verifier_env *env, const struct btf *btf, u32 type_id);
u64 bpf_diag_event_log_save(struct bpf_verifier_env *env);
void bpf_diag_event_log_restore(struct bpf_verifier_env *env, u64 log_pos);
+u32 bpf_diag_irq_depth(const struct bpf_verifier_state *state);
void bpf_diag_free(struct bpf_verifier_env *env);
void bpf_diag_record_branch(struct bpf_verifier_env *env, u32 insn_idx, bool cond_true);
void bpf_diag_mod_begin(struct bpf_verifier_env *env, const struct bpf_reg_state *reg,
@@ -45,5 +55,7 @@ void bpf_diag_record_scrub_stack(struct bpf_verifier_env *env,
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);
+void bpf_diag_record_context(struct bpf_verifier_env *env, u32 insn_idx,
+ enum bpf_diag_context_kind ctx_kind, bool enter, u32 depth);
#endif /* __BPF_DIAGNOSTICS_H */
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8e32fa5fa30a..1f2a7f480ce3 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1045,7 +1045,7 @@ static int is_iter_reg_valid_init(struct bpf_verifier_env *env, struct bpf_reg_s
}
static int acquire_irq_state(struct bpf_verifier_env *env, int insn_idx);
-static int release_irq_state(struct bpf_verifier_state *state, int id);
+static int release_irq_state(struct bpf_verifier_env *env, int id);
static int mark_stack_slot_irq_flag(struct bpf_verifier_env *env,
struct bpf_call_arg_meta *meta,
@@ -1104,7 +1104,7 @@ static int unmark_stack_slot_irq_flag(struct bpf_verifier_env *env, struct bpf_r
return -EINVAL;
}
- err = release_irq_state(env->cur_state, st->id);
+ err = release_irq_state(env, st->id);
WARN_ON_ONCE(err && err != -EACCES);
if (err) {
int insn_idx = 0;
@@ -1439,6 +1439,8 @@ static int acquire_lock_state(struct bpf_verifier_env *env, int insn_idx, enum r
state->active_locks++;
state->active_lock_id = id;
state->active_lock_ptr = ptr;
+ bpf_diag_record_context(env, insn_idx, BPF_DIAG_CONTEXT_LOCK, true,
+ state->active_locks);
return 0;
}
@@ -1454,6 +1456,8 @@ static int acquire_irq_state(struct bpf_verifier_env *env, int insn_idx)
s->id = ++env->id_gen;
state->active_irq_id = s->id;
+ bpf_diag_record_context(env, insn_idx, BPF_DIAG_CONTEXT_IRQ, true,
+ bpf_diag_irq_depth(state));
return s->id;
}
@@ -1495,8 +1499,9 @@ static bool reg_is_referenced(struct bpf_verifier_env *env, const struct bpf_reg
return find_reference_state(env->cur_state, reg->id);
}
-static int release_lock_state(struct bpf_verifier_state *state, int type, int id, void *ptr)
+static int release_lock_state(struct bpf_verifier_env *env, int type, int id, void *ptr)
{
+ struct bpf_verifier_state *state = env->cur_state;
void *prev_ptr = NULL;
u32 prev_id = 0;
int i;
@@ -1509,6 +1514,8 @@ static int release_lock_state(struct bpf_verifier_state *state, int type, int id
/* Reassign active lock (id, ptr). */
state->active_lock_id = prev_id;
state->active_lock_ptr = prev_ptr;
+ bpf_diag_record_context(env, env->insn_idx, BPF_DIAG_CONTEXT_LOCK,
+ false, state->active_locks);
return 0;
}
if (state->refs[i].type & REF_TYPE_LOCK_MASK) {
@@ -1519,8 +1526,9 @@ static int release_lock_state(struct bpf_verifier_state *state, int type, int id
return -EINVAL;
}
-static int release_irq_state(struct bpf_verifier_state *state, int id)
+static int release_irq_state(struct bpf_verifier_env *env, int id)
{
+ struct bpf_verifier_state *state = env->cur_state;
u32 prev_id = 0;
int i;
@@ -1533,6 +1541,8 @@ static int release_irq_state(struct bpf_verifier_state *state, int id)
if (state->refs[i].id == id) {
release_reference_state(state, i);
state->active_irq_id = prev_id;
+ bpf_diag_record_context(env, env->insn_idx, BPF_DIAG_CONTEXT_IRQ,
+ false, bpf_diag_irq_depth(state));
return 0;
} else {
prev_id = state->refs[i].id;
@@ -7181,7 +7191,7 @@ static int process_spin_lock(struct bpf_verifier_env *env, struct bpf_reg_state
verbose(env, "%s_unlock cannot be out of order\n", lock_str);
return -EINVAL;
}
- if (release_lock_state(cur, type, reg->id, ptr)) {
+ if (release_lock_state(env, type, reg->id, ptr)) {
verbose(env, "%s_unlock of different lock\n", lock_str);
return -EINVAL;
}
@@ -13242,22 +13252,30 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
if (rcu_lock) {
env->cur_state->active_rcu_locks++;
+ bpf_diag_record_context(env, insn_idx, BPF_DIAG_CONTEXT_RCU, true,
+ env->cur_state->active_rcu_locks);
} else if (rcu_unlock) {
if (env->cur_state->active_rcu_locks == 0) {
verbose(env, "unmatched rcu read unlock (kernel function %s)\n", func_name);
return -EINVAL;
}
env->cur_state->active_rcu_locks--;
+ bpf_diag_record_context(env, insn_idx, BPF_DIAG_CONTEXT_RCU, false,
+ env->cur_state->active_rcu_locks);
if (!in_rcu_cs(env))
invalidate_rcu_protected_refs(env);
} else if (preempt_disable) {
env->cur_state->active_preempt_locks++;
+ bpf_diag_record_context(env, insn_idx, BPF_DIAG_CONTEXT_PREEMPT, true,
+ env->cur_state->active_preempt_locks);
} else if (preempt_enable) {
if (env->cur_state->active_preempt_locks == 0) {
verbose(env, "unmatched attempt to enable preemption (kernel function %s)\n", func_name);
return -EINVAL;
}
env->cur_state->active_preempt_locks--;
+ bpf_diag_record_context(env, insn_idx, BPF_DIAG_CONTEXT_PREEMPT, false,
+ env->cur_state->active_preempt_locks);
if (!in_rcu_cs(env))
invalidate_rcu_protected_refs(env);
}