History

bpf: Track verifier context diagnostic events

Record verifier context transitions in the diagnostic history so later reports can anchor causal paths to the critical section that made an operation invalid. This covers lock, IRQ, RCU, and preempt regions without adding any new verifier error reports. Category-specific commits decide where those recorded events should be rendered. Use context depth when selecting scoped history so nested regions anchor at the outer active region, and fall back to the earliest retained event when the matching entry was pruned.

Kumar Kartikeya Dwivedi committed 2 months ago

Commit 956a66e1 parent 9ecd7033 files · +74 −5Browse files

Unified · Split

3 files
  1. kernel/bpf/diagnostics.c +39 −0
  2. kernel/bpf/diagnostics.h +12 −0
  3. kernel/bpf/verifier.c +23 −5
kernel/bpf/diagnostics.c+39−0
@@ -104,6 +104,7 @@ enum bpf_diag_history_kind { · 103 unmodified lines
104 BPF_DIAG_HISTORY_MOD,104 BPF_DIAG_HISTORY_MOD,
105 BPF_DIAG_HISTORY_REF_ACQUIRE,105 BPF_DIAG_HISTORY_REF_ACQUIRE,
106 BPF_DIAG_HISTORY_REF_RELEASE,106 BPF_DIAG_HISTORY_REF_RELEASE,
107 BPF_DIAG_HISTORY_CONTEXT,
107};108};
108 109
109struct bpf_diag_history_event {110struct bpf_diag_history_event {
@@ -124,6 +125,11 @@ struct bpf_diag_history_event { · 14 unmodified lines
124 struct {125 struct {
125 u32 ref_id;126 u32 ref_id;
126 } ref;127 } ref;
128 struct {
129 u32 depth;
130 u8 kind;
131 bool enter;
132 } ctx;
127 };133 };
128};134};
129 135
@@ -388,6 +394,19 @@ void bpf_diag_event_log_restore(struct bpf_verifier_env *env, u64 log_pos) · 258 unmodified lines
388 log->cnt = log_pos - log->first_seq;394 log->cnt = log_pos - log->first_seq;
389}395}
390 396
397u32 bpf_diag_irq_depth(const struct bpf_verifier_state *state)
398{
399 u32 depth = 0;
400 int i;
401
402 for (i = 0; i < state->acquired_refs; i++) {
403 if (state->refs[i].type == REF_TYPE_IRQ)
404 depth++;
405 }
406
407 return depth;
408}
409
391static void diag_append_history(struct bpf_verifier_env *env,410static void diag_append_history(struct bpf_verifier_env *env,
392 const struct bpf_diag_history_event *event)411 const struct bpf_diag_history_event *event)
393{412{
@@ -1049,3 +1068,23 @@ void bpf_diag_record_ref_release(struct bpf_verifier_env *env, u32 insn_idx, u32 · 655 unmodified lines
1049{1068{
1050 diag_record_ref(env, insn_idx, BPF_DIAG_HISTORY_REF_RELEASE, ref_id);1069 diag_record_ref(env, insn_idx, BPF_DIAG_HISTORY_REF_RELEASE, ref_id);
1051}1070}
1071
1072void bpf_diag_record_context(struct bpf_verifier_env *env, u32 insn_idx,
1073 enum bpf_diag_context_kind ctx_kind, bool enter, u32 depth)
1074{
1075 /*
1076 * Keep leave events so context rendering can stop at a depth-zero exit
1077 * and show nested-region depth accurately for the active path.
1078 */
1079 struct bpf_diag_history_event event = {
1080 .insn_idx = insn_idx,
1081 .kind = BPF_DIAG_HISTORY_CONTEXT,
1082 .ctx = {
1083 .kind = ctx_kind,
1084 .enter = enter,
1085 .depth = depth,
1086 },
1087 };
1088
1089 diag_append_history(env, &event);
1090}
kernel/bpf/diagnostics.h+12−0
@@ -11,6 +11,7 @@ · 10 unmodified lines
11struct bpf_func_state;11struct bpf_func_state;
12struct bpf_reg_state;12struct bpf_reg_state;
13struct bpf_verifier_env;13struct bpf_verifier_env;
14struct bpf_verifier_state;
14struct btf;15struct btf;
15 16
16enum bpf_diag_mod_reason {17enum bpf_diag_mod_reason {
@@ -23,6 +24,14 @@ enum bpf_diag_mod_reason { · 6 unmodified lines
23 BPF_DIAG_MOD_CALLER_SAVED,24 BPF_DIAG_MOD_CALLER_SAVED,
24};25};
25 26
27enum bpf_diag_context_kind {
28 BPF_DIAG_CONTEXT_NONE,
29 BPF_DIAG_CONTEXT_RCU,
30 BPF_DIAG_CONTEXT_PREEMPT,
31 BPF_DIAG_CONTEXT_IRQ,
32 BPF_DIAG_CONTEXT_LOCK,
33};
34
26bool bpf_diag_enabled(const struct bpf_verifier_env *env);35bool bpf_diag_enabled(const struct bpf_verifier_env *env);
27int bpf_diag_init(struct bpf_verifier_env *env);36int bpf_diag_init(struct bpf_verifier_env *env);
28void bpf_diag_init_frame(struct bpf_verifier_env *env, struct bpf_func_state *state);37void 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 · 4 unmodified lines
33const char *bpf_diag_fmt_btf_type(struct bpf_verifier_env *env, const struct btf *btf, u32 type_id);42const char *bpf_diag_fmt_btf_type(struct bpf_verifier_env *env, const struct btf *btf, u32 type_id);
34u64 bpf_diag_event_log_save(struct bpf_verifier_env *env);43u64 bpf_diag_event_log_save(struct bpf_verifier_env *env);
35void bpf_diag_event_log_restore(struct bpf_verifier_env *env, u64 log_pos);44void bpf_diag_event_log_restore(struct bpf_verifier_env *env, u64 log_pos);
45u32 bpf_diag_irq_depth(const struct bpf_verifier_state *state);
36void bpf_diag_free(struct bpf_verifier_env *env);46void bpf_diag_free(struct bpf_verifier_env *env);
37void bpf_diag_record_branch(struct bpf_verifier_env *env, u32 insn_idx, bool cond_true);47void bpf_diag_record_branch(struct bpf_verifier_env *env, u32 insn_idx, bool cond_true);
38void bpf_diag_mod_begin(struct bpf_verifier_env *env, const struct bpf_reg_state *reg,48void 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, · 6 unmodified lines
45 enum bpf_diag_mod_reason reason);55 enum bpf_diag_mod_reason reason);
46void bpf_diag_record_ref_acquire(struct bpf_verifier_env *env, u32 insn_idx, u32 ref_id);56void bpf_diag_record_ref_acquire(struct bpf_verifier_env *env, u32 insn_idx, u32 ref_id);
47void bpf_diag_record_ref_release(struct bpf_verifier_env *env, u32 insn_idx, u32 ref_id);57void bpf_diag_record_ref_release(struct bpf_verifier_env *env, u32 insn_idx, u32 ref_id);
58void bpf_diag_record_context(struct bpf_verifier_env *env, u32 insn_idx,
59 enum bpf_diag_context_kind ctx_kind, bool enter, u32 depth);
48 60
49#endif /* __BPF_DIAGNOSTICS_H */61#endif /* __BPF_DIAGNOSTICS_H */
kernel/bpf/verifier.c+23−5
@@ -1045,7 +1045,7 @@ static int is_iter_reg_valid_init(struct bpf_verifier_env *env, struct bpf_reg_s · 1044 unmodified lines
1045}1045}
1046 1046
1047static int acquire_irq_state(struct bpf_verifier_env *env, int insn_idx);1047static int acquire_irq_state(struct bpf_verifier_env *env, int insn_idx);
1048static int release_irq_state(struct bpf_verifier_state *state, int id);1048static int release_irq_state(struct bpf_verifier_env *env, int id);
1049 1049
1050static int mark_stack_slot_irq_flag(struct bpf_verifier_env *env,1050static int mark_stack_slot_irq_flag(struct bpf_verifier_env *env,
1051 struct bpf_call_arg_meta *meta,1051 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 · 52 unmodified lines
1104 return -EINVAL;1104 return -EINVAL;
1105 }1105 }
1106 1106
1107 err = release_irq_state(env->cur_state, st->id);1107 err = release_irq_state(env, st->id);
1108 WARN_ON_ONCE(err && err != -EACCES);1108 WARN_ON_ONCE(err && err != -EACCES);
1109 if (err) {1109 if (err) {
1110 int insn_idx = 0;1110 int insn_idx = 0;
@@ -1439,6 +1439,8 @@ static int acquire_lock_state(struct bpf_verifier_env *env, int insn_idx, enum r · 328 unmodified lines
1439 state->active_locks++;1439 state->active_locks++;
1440 state->active_lock_id = id;1440 state->active_lock_id = id;
1441 state->active_lock_ptr = ptr;1441 state->active_lock_ptr = ptr;
1442 bpf_diag_record_context(env, insn_idx, BPF_DIAG_CONTEXT_LOCK, true,
1443 state->active_locks);
1442 return 0;1444 return 0;
1443}1445}
1444 1446
@@ -1454,6 +1456,8 @@ static int acquire_irq_state(struct bpf_verifier_env *env, int insn_idx) · 9 unmodified lines
1454 s->id = ++env->id_gen;1456 s->id = ++env->id_gen;
1455 1457
1456 state->active_irq_id = s->id;1458 state->active_irq_id = s->id;
1459 bpf_diag_record_context(env, insn_idx, BPF_DIAG_CONTEXT_IRQ, true,
1460 bpf_diag_irq_depth(state));
1457 return s->id;1461 return s->id;
1458}1462}
1459 1463
@@ -1495,8 +1499,9 @@ static bool reg_is_referenced(struct bpf_verifier_env *env, const struct bpf_reg · 35 unmodified lines
1495 return find_reference_state(env->cur_state, reg->id);1499 return find_reference_state(env->cur_state, reg->id);
1496}1500}
1497 1501
1498static int release_lock_state(struct bpf_verifier_state *state, int type, int id, void *ptr)1502static int release_lock_state(struct bpf_verifier_env *env, int type, int id, void *ptr)
1499{1503{
1504 struct bpf_verifier_state *state = env->cur_state;
1500 void *prev_ptr = NULL;1505 void *prev_ptr = NULL;
1501 u32 prev_id = 0;1506 u32 prev_id = 0;
1502 int i;1507 int i;
@@ -1509,6 +1514,8 @@ static int release_lock_state(struct bpf_verifier_state *state, int type, int id · 6 unmodified lines
1509 /* Reassign active lock (id, ptr). */1514 /* Reassign active lock (id, ptr). */
1510 state->active_lock_id = prev_id;1515 state->active_lock_id = prev_id;
1511 state->active_lock_ptr = prev_ptr;1516 state->active_lock_ptr = prev_ptr;
1517 bpf_diag_record_context(env, env->insn_idx, BPF_DIAG_CONTEXT_LOCK,
1518 false, state->active_locks);
1512 return 0;1519 return 0;
1513 }1520 }
1514 if (state->refs[i].type & REF_TYPE_LOCK_MASK) {1521 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 · 4 unmodified lines
1519 return -EINVAL;1526 return -EINVAL;
1520}1527}
1521 1528
1522static int release_irq_state(struct bpf_verifier_state *state, int id)1529static int release_irq_state(struct bpf_verifier_env *env, int id)
1523{1530{
1531 struct bpf_verifier_state *state = env->cur_state;
1524 u32 prev_id = 0;1532 u32 prev_id = 0;
1525 int i;1533 int i;
1526 1534
@@ -1533,6 +1541,8 @@ static int release_irq_state(struct bpf_verifier_state *state, int id) · 6 unmodified lines
1533 if (state->refs[i].id == id) {1541 if (state->refs[i].id == id) {
1534 release_reference_state(state, i);1542 release_reference_state(state, i);
1535 state->active_irq_id = prev_id;1543 state->active_irq_id = prev_id;
1544 bpf_diag_record_context(env, env->insn_idx, BPF_DIAG_CONTEXT_IRQ,
1545 false, bpf_diag_irq_depth(state));
1536 return 0;1546 return 0;
1537 } else {1547 } else {
1538 prev_id = state->refs[i].id;1548 prev_id = state->refs[i].id;
@@ -7181,7 +7191,7 @@ static int process_spin_lock(struct bpf_verifier_env *env, struct bpf_reg_state · 5642 unmodified lines
7181 verbose(env, "%s_unlock cannot be out of order\n", lock_str);7191 verbose(env, "%s_unlock cannot be out of order\n", lock_str);
7182 return -EINVAL;7192 return -EINVAL;
7183 }7193 }
7184 if (release_lock_state(cur, type, reg->id, ptr)) {7194 if (release_lock_state(env, type, reg->id, ptr)) {
7185 verbose(env, "%s_unlock of different lock\n", lock_str);7195 verbose(env, "%s_unlock of different lock\n", lock_str);
7186 return -EINVAL;7196 return -EINVAL;
7187 }7197 }
@@ -13242,22 +13252,30 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, · 6054 unmodified lines
13242 13252
13243 if (rcu_lock) {13253 if (rcu_lock) {
13244 env->cur_state->active_rcu_locks++;13254 env->cur_state->active_rcu_locks++;
13255 bpf_diag_record_context(env, insn_idx, BPF_DIAG_CONTEXT_RCU, true,
13256 env->cur_state->active_rcu_locks);
13245 } else if (rcu_unlock) {13257 } else if (rcu_unlock) {
13246 if (env->cur_state->active_rcu_locks == 0) {13258 if (env->cur_state->active_rcu_locks == 0) {
13247 verbose(env, "unmatched rcu read unlock (kernel function %s)\n", func_name);13259 verbose(env, "unmatched rcu read unlock (kernel function %s)\n", func_name);
13248 return -EINVAL;13260 return -EINVAL;
13249 }13261 }
13250 env->cur_state->active_rcu_locks--;13262 env->cur_state->active_rcu_locks--;
13263 bpf_diag_record_context(env, insn_idx, BPF_DIAG_CONTEXT_RCU, false,
13264 env->cur_state->active_rcu_locks);
13251 if (!in_rcu_cs(env))13265 if (!in_rcu_cs(env))
13252 invalidate_rcu_protected_refs(env);13266 invalidate_rcu_protected_refs(env);
13253 } else if (preempt_disable) {13267 } else if (preempt_disable) {
13254 env->cur_state->active_preempt_locks++;13268 env->cur_state->active_preempt_locks++;
13269 bpf_diag_record_context(env, insn_idx, BPF_DIAG_CONTEXT_PREEMPT, true,
13270 env->cur_state->active_preempt_locks);
13255 } else if (preempt_enable) {13271 } else if (preempt_enable) {
13256 if (env->cur_state->active_preempt_locks == 0) {13272 if (env->cur_state->active_preempt_locks == 0) {
13257 verbose(env, "unmatched attempt to enable preemption (kernel function %s)\n", func_name);13273 verbose(env, "unmatched attempt to enable preemption (kernel function %s)\n", func_name);
13258 return -EINVAL;13274 return -EINVAL;
13259 }13275 }
13260 env->cur_state->active_preempt_locks--;13276 env->cur_state->active_preempt_locks--;
13277 bpf_diag_record_context(env, insn_idx, BPF_DIAG_CONTEXT_PREEMPT, false,
13278 env->cur_state->active_preempt_locks);
13261 if (!in_rcu_cs(env))13279 if (!in_rcu_cs(env))
13262 invalidate_rcu_protected_refs(env);13280 invalidate_rcu_protected_refs(env);
13263 }13281 }