From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
To: bpf@vger.kernel.org
Cc: Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Eduard Zingerman <eddyz87@gmail.com>,
Emil Tsalapatis <emil@etsalapatis.com>,
kkd@meta.com, kernel-team@meta.com
Subject: [PATCH bpf-next v3 10/17] bpf: Report Resource Lifetime reference leaks
Date: Mon, 13 Jul 2026 17:38:59 +0200 [thread overview]
Message-ID: <20260713153910.2556007-11-memxor@gmail.com> (raw)
In-Reply-To: <20260713153910.2556007-1-memxor@gmail.com>
Augment selected Resource Lifetime Safety failures with structured diagnostics
while preserving the existing verifier messages.
Report unreleased references from check_reference_leak() using
reference-scoped diagnostic history, and add state reports for dynptr,
iterator, lock, and IRQ-flag lifetime misuse.
IRQ restore mismatch and out-of-order diagnostics use IRQ context-scoped
history when an IRQ-disabled region is active, so retained save/restore context
is still visible after per-state history removal.
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
kernel/bpf/diagnostics.c | 91 ++++++++++++++++++
kernel/bpf/diagnostics.h | 9 ++
kernel/bpf/verifier.c | 198 ++++++++++++++++++++++++++++++++++-----
3 files changed, 273 insertions(+), 25 deletions(-)
diff --git a/kernel/bpf/diagnostics.c b/kernel/bpf/diagnostics.c
index d4588b19aac9..e454bcd0a0e0 100644
--- a/kernel/bpf/diagnostics.c
+++ b/kernel/bpf/diagnostics.c
@@ -1644,6 +1644,97 @@ void bpf_diag_report_mem_bounds(struct bpf_verifier_env *env, u32 insn_idx, int
"stays within the object.");
}
+static const char *diag_lock_name(const struct bpf_reference_state *lock)
+{
+ switch (lock->type) {
+ case REF_TYPE_LOCK:
+ return "bpf_spin_lock";
+ case REF_TYPE_RES_LOCK:
+ return "resource spin lock";
+ case REF_TYPE_RES_LOCK_IRQ:
+ return "IRQ-saving resource spin lock";
+ default:
+ return "lock";
+ }
+}
+
+static void diag_res_report(struct bpf_verifier_env *env, u32 insn_idx, const char *problem,
+ const char *reason)
+{
+ bpf_diag_report_header(env, CATEGORY_RESOURCE_LIFETIME_SAFETY, problem);
+ diag_report_reason(env, "%s", reason);
+
+ diag_report_section(env, "At");
+ bpf_diag_report_source(env, insn_idx, "error", "%s", problem);
+}
+
+void bpf_diag_res(struct bpf_verifier_env *env, u32 insn_idx, const char *problem,
+ const char *reason, const char *suggestion)
+{
+ diag_res_report(env, insn_idx, problem, reason);
+ diag_report_suggestion(env, "%s", suggestion);
+}
+
+void bpf_diag_lock(struct bpf_verifier_env *env, u32 insn_idx, const char *problem,
+ const char *reason, const char *suggestion,
+ const struct bpf_reference_state *active_lock)
+{
+ diag_res_report(env, insn_idx, problem, reason);
+
+ if (active_lock) {
+ diag_report_section(env, "Active lock");
+ bpf_diag_report_source(env, active_lock->insn_idx, "acquired",
+ "active %s has verifier identity %d",
+ diag_lock_name(active_lock), active_lock->id);
+ }
+
+ diag_report_suggestion(env, "%s", suggestion);
+}
+
+void bpf_diag_irq(struct bpf_verifier_env *env, u32 insn_idx, const char *problem,
+ const char *reason, const char *suggestion, u32 depth)
+{
+ struct bpf_diag_history_opts opts = {
+ .scope = BPF_DIAG_HISTORY_SCOPE_CONTEXT,
+ .ctx_kind = BPF_DIAG_CONTEXT_IRQ,
+ .ctx_depth = depth,
+ };
+
+ bpf_diag_report_header(env, CATEGORY_RESOURCE_LIFETIME_SAFETY, problem);
+ diag_report_reason(env, "%s", reason);
+
+ diag_report_section(env, "At");
+ bpf_diag_report_source(env, insn_idx, "error", "%s", problem);
+
+ if (depth)
+ diag_print_history(env, &opts);
+
+ diag_report_suggestion(env, "%s", suggestion);
+}
+
+void bpf_diag_leak(struct bpf_verifier_env *env, u32 ref_id, u32 alloc_insn, u32 fail_insn)
+{
+ struct bpf_diag_history_opts opts = {
+ .scope = BPF_DIAG_HISTORY_SCOPE_REF,
+ .ref_id = ref_id,
+ };
+
+ bpf_diag_report_header(env, CATEGORY_RESOURCE_LIFETIME_SAFETY, "unreleased resource");
+ diag_report_reason(env,
+ "Owned resource (id=%u) was acquired at instruction %u and still needs "
+ "to be released before this exit path.",
+ ref_id, alloc_insn);
+
+ diag_report_section(env, "At");
+ bpf_diag_report_source(env, fail_insn, "error",
+ "owned resource (id=%u) still needs release", ref_id);
+
+ diag_print_history(env, &opts);
+
+ diag_report_suggestion(env, "Release or transfer ownership of the acquired resource on "
+ "every path before the program exits.");
+}
+
static void diag_format_var_offset(struct bpf_diag_reg_fmt *fmt, char *buf, size_t size,
const struct bpf_diag_reg_snapshot *snapshot)
{
diff --git a/kernel/bpf/diagnostics.h b/kernel/bpf/diagnostics.h
index 4fc8abb2f2b9..73ddede41d17 100644
--- a/kernel/bpf/diagnostics.h
+++ b/kernel/bpf/diagnostics.h
@@ -8,6 +8,7 @@
#include <linux/compiler_attributes.h>
#include <linux/types.h>
+struct bpf_reference_state;
struct bpf_reg_state;
struct bpf_verifier_env;
struct bpf_verifier_state;
@@ -142,6 +143,14 @@ void bpf_diag_report_memory(struct bpf_verifier_env *env, u32 insn_idx, const ch
void bpf_diag_report_mem_bounds(struct bpf_verifier_env *env, u32 insn_idx, int regno,
const char *reg_name, const char *type_name, const char *proof,
int off, int size, u32 mem_size, const struct bpf_reg_state *reg);
+void bpf_diag_res(struct bpf_verifier_env *env, u32 insn_idx, const char *problem,
+ const char *reason, const char *suggestion);
+void bpf_diag_lock(struct bpf_verifier_env *env, u32 insn_idx, const char *problem,
+ const char *reason, const char *suggestion,
+ const struct bpf_reference_state *active_lock);
+void bpf_diag_irq(struct bpf_verifier_env *env, u32 insn_idx, const char *problem,
+ const char *reason, const char *suggestion, u32 depth);
+void bpf_diag_leak(struct bpf_verifier_env *env, u32 ref_id, u32 alloc_insn, u32 fail_insn);
int bpf_diag_record_branch(struct bpf_verifier_env *env, u32 insn_idx, bool cond_true);
void bpf_diag_record_mod(struct bpf_verifier_env *env, u32 insn_idx,
struct bpf_diag_mod_target target, enum bpf_diag_mod_reason reason,
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index bd233083b495..84eda5e83f87 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -840,6 +840,12 @@ static int destroy_if_dynptr_stack_slot(struct bpf_verifier_env *env,
if (dynptr_type_referenced(state->stack[spi].spilled_ptr.dynptr.type) &&
dynptr_ref_cnt(env, state->stack[spi].spilled_ptr.parent_id) <= 1) {
verbose(env, "cannot overwrite referenced dynptr\n");
+ bpf_diag_res(env, env->insn_idx, "referenced dynptr overwrite",
+ "This stack slot contains a dynptr that owns or protects a referenced "
+ "resource. Overwriting the last dynptr for that resource would lose "
+ "the verifier-tracked release path.",
+ "Release or clone the dynptr so another live dynptr still tracks the "
+ "referenced resource before overwriting this stack slot.");
return -EINVAL;
}
@@ -910,26 +916,29 @@ static bool is_dynptr_reg_valid_init(struct bpf_verifier_env *env, struct bpf_re
return true;
}
-static bool is_dynptr_type_expected(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
- enum bpf_arg_type arg_type)
+static enum bpf_dynptr_type dynptr_reg_type(struct bpf_verifier_env *env, struct bpf_reg_state *reg)
{
- struct bpf_func_state *state = bpf_func(env, reg);
- enum bpf_dynptr_type dynptr_type;
+ struct bpf_func_state *state;
int spi;
+ if (reg->type == CONST_PTR_TO_DYNPTR)
+ return reg->dynptr.type;
+
+ spi = dynptr_get_spi(env, reg);
+ if (spi < 0)
+ return BPF_DYNPTR_TYPE_INVALID;
+ state = bpf_func(env, reg);
+ return state->stack[spi].spilled_ptr.dynptr.type;
+}
+
+static bool is_dynptr_type_expected(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
+ enum bpf_arg_type arg_type)
+{
/* ARG_PTR_TO_DYNPTR takes any type of dynptr */
if (arg_type == ARG_PTR_TO_DYNPTR)
return true;
- dynptr_type = arg_to_dynptr_type(arg_type);
- if (reg->type == CONST_PTR_TO_DYNPTR) {
- return reg->dynptr.type == dynptr_type;
- } else {
- spi = dynptr_get_spi(env, reg);
- if (spi < 0)
- return false;
- return state->stack[spi].spilled_ptr.dynptr.type == dynptr_type;
- }
+ return dynptr_reg_type(env, reg) == arg_to_dynptr_type(arg_type);
}
static void __mark_reg_known_zero(struct bpf_reg_state *reg);
@@ -1120,11 +1129,21 @@ static int unmark_stack_slot_irq_flag(struct bpf_verifier_env *env, struct bpf_r
st = &slot->spilled_ptr;
if (st->irq.kfunc_class != kfunc_class) {
- const char *flag_kfunc = st->irq.kfunc_class == IRQ_NATIVE_KFUNC ? "native" : "lock";
+ const char *fmt = "This IRQ flag was saved by %s IRQ kfuncs, but the restore call "
+ "belongs to the %s IRQ kfunc family. Save and restore operations "
+ "must use the same family.";
+ const char *flag_kfunc = st->irq.kfunc_class == IRQ_NATIVE_KFUNC ? "native" :
+ "lock";
const char *used_kfunc = kfunc_class == IRQ_NATIVE_KFUNC ? "native" : "lock";
+ const char *reason;
verbose(env, "irq flag acquired by %s kfuncs cannot be restored with %s kfuncs\n",
flag_kfunc, used_kfunc);
+ reason = bpf_diag_scratch_printf(env, 0, fmt, flag_kfunc, used_kfunc);
+ bpf_diag_irq(env, env->insn_idx, "IRQ flag restore mismatch", reason,
+ "Restore the flag with the matching IRQ restore kfunc for the save "
+ "operation that created it.",
+ bpf_diag_irq_depth(env->cur_state));
return -EINVAL;
}
@@ -1142,6 +1161,11 @@ static int unmark_stack_slot_irq_flag(struct bpf_verifier_env *env, struct bpf_r
verbose(env, "cannot restore irq state out of order, expected id=%d acquired at insn_idx=%d\n",
env->cur_state->active_irq_id, insn_idx);
+ bpf_diag_irq(env, env->insn_idx, "IRQ flag restore out of order",
+ "IRQ-disabled regions must be restored in last-in, first-out order, "
+ "but this restore does not match the currently active IRQ flag.",
+ "Restore nested IRQ flags in the reverse order they were saved.",
+ bpf_diag_irq_depth(env->cur_state));
return err;
}
@@ -7356,11 +7380,13 @@ enum {
* env->cur_state->active_locks remembers which map value element or allocated
* object got locked and clears it after bpf_spin_unlock.
*/
-static int process_spin_lock(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno, int flags)
+static int process_spin_lock(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,
+ int flags)
{
bool is_lock = flags & PROCESS_SPIN_LOCK, is_res_lock = flags & PROCESS_RES_LOCK;
const char *lock_str = is_res_lock ? "bpf_res_spin" : "bpf_spin";
struct bpf_verifier_state *cur = env->cur_state;
+ struct bpf_reference_state *lock;
bool is_const = tnum_is_const(reg->var_off);
bool is_irq = flags & PROCESS_LOCK_IRQ;
u64 val = reg->var_off.value;
@@ -7410,14 +7436,30 @@ static int process_spin_lock(struct bpf_verifier_env *env, struct bpf_reg_state
ptr = btf;
if (!is_res_lock && cur->active_locks) {
- if (find_lock_state(env->cur_state, REF_TYPE_LOCK, 0, NULL)) {
+ lock = find_lock_state(cur, REF_TYPE_LOCK, 0, NULL);
+ if (lock) {
verbose(env,
"Locking two bpf_spin_locks are not allowed\n");
+ bpf_diag_lock(env, env->insn_idx, "nested spin lock",
+ "This path already holds a bpf_spin_lock. The "
+ "verifier allows only one regular BPF spin lock at a "
+ "time.",
+ "Unlock the current bpf_spin_lock before taking "
+ "another one.",
+ lock);
return -EINVAL;
}
} else if (is_res_lock && cur->active_locks) {
- if (find_lock_state(env->cur_state, REF_TYPE_RES_LOCK | REF_TYPE_RES_LOCK_IRQ, reg->id, ptr)) {
+ lock = find_lock_state(cur, REF_TYPE_RES_LOCK | REF_TYPE_RES_LOCK_IRQ,
+ reg->id, ptr);
+ if (lock) {
verbose(env, "Acquiring the same lock again, AA deadlock detected\n");
+ bpf_diag_lock(env, env->insn_idx, "recursive resource spin lock",
+ "This path already holds the same resource spin "
+ "lock. Taking it again would deadlock.",
+ "Avoid reacquiring the same resource spin lock "
+ "before it is unlocked.",
+ lock);
return -EINVAL;
}
}
@@ -7444,6 +7486,11 @@ static int process_spin_lock(struct bpf_verifier_env *env, struct bpf_reg_state
if (!cur->active_locks) {
verbose(env, "%s_unlock without taking a lock\n", lock_str);
+ bpf_diag_res(env, env->insn_idx, "unlock without lock",
+ "This unlock operation has no matching active lock on the "
+ "current path.",
+ "Take the matching lock before this unlock, or remove the "
+ "unmatched unlock path.");
return -EINVAL;
}
@@ -7453,16 +7500,40 @@ static int process_spin_lock(struct bpf_verifier_env *env, struct bpf_reg_state
type = REF_TYPE_RES_LOCK;
else
type = REF_TYPE_LOCK;
- if (!find_lock_state(cur, type, reg->id, ptr)) {
- verbose(env, "%s_unlock of different lock\n", lock_str);
+
+ lock = find_lock_state(cur, type, reg->id, ptr);
+ if (!lock) {
+ verbose(env, "%s_unlock of a different lock\n", lock_str);
+ lock = find_lock_state(cur, REF_TYPE_LOCK_MASK, cur->active_lock_id,
+ cur->active_lock_ptr);
+ bpf_diag_lock(env, env->insn_idx, "unlock of a different lock",
+ "This unlock does not match any active lock with the same "
+ "tracked identity on the current path.",
+ "Unlock the same lock object that was most recently "
+ "acquired.",
+ lock);
return -EINVAL;
}
if (reg->id != cur->active_lock_id || ptr != cur->active_lock_ptr) {
verbose(env, "%s_unlock cannot be out of order\n", lock_str);
+ lock = find_lock_state(cur, REF_TYPE_LOCK_MASK, cur->active_lock_id,
+ cur->active_lock_ptr);
+ bpf_diag_lock(env, env->insn_idx, "unlock out of order",
+ "Locks must be released in last-in, first-out order, but "
+ "this unlock does not match the currently active lock.",
+ "Release nested locks in the reverse order they were "
+ "acquired.",
+ lock);
return -EINVAL;
}
if (release_lock_state(env, type, reg->id, ptr)) {
- verbose(env, "%s_unlock of different lock\n", lock_str);
+ verbose(env, "%s_unlock of a different lock\n", lock_str);
+ bpf_diag_lock(env, env->insn_idx, "unlock of a different lock",
+ "The verifier could not release a lock state matching this "
+ "unlock operation.",
+ "Pass the same lock object and lock kind that were used for "
+ "the matching lock operation.",
+ lock);
return -EINVAL;
}
@@ -7471,7 +7542,6 @@ static int process_spin_lock(struct bpf_verifier_env *env, struct bpf_reg_state
return 0;
}
-/* Check if @regno is a pointer to a specific field in a map value */
static int check_map_field_pointer(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,
enum btf_field_type field_type,
struct bpf_map_desc *map_desc)
@@ -7625,9 +7695,18 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
int spi, err = 0;
if (reg->type != PTR_TO_STACK && reg->type != CONST_PTR_TO_DYNPTR) {
+ const char *fmt = "A dynptr argument must be a pointer to a dynptr stack slot or a "
+ "verifier-provided const struct bpf_dynptr, but %s is %s.";
+ const char *reason;
+
verbose(env,
"%s expected pointer to stack or const struct bpf_dynptr\n",
reg_arg_name(env, argno));
+ reason = bpf_diag_scratch_printf(env, 0, fmt, reg_arg_name(env, argno),
+ bpf_diag_reg_type_plain(env, reg->type));
+ bpf_diag_res(env, insn_idx, "invalid dynptr argument", reason,
+ "Pass the address of a stack dynptr object, or use a const dynptr "
+ "pointer returned by the verifier-supported path.");
return -EINVAL;
}
@@ -7650,6 +7729,12 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
if (!is_dynptr_reg_valid_uninit(env, reg)) {
verbose(env, "Dynptr has to be an uninitialized dynptr\n");
+ bpf_diag_res(env, insn_idx, "dynptr is already initialized",
+ "This kfunc constructs a dynptr and requires an uninitialized "
+ "dynptr stack slot, but the selected slot already holds "
+ "dynptr state.",
+ "Use a fresh stack dynptr slot, or release/destroy the "
+ "existing dynptr before reusing the slot.");
return -EINVAL;
}
@@ -7666,21 +7751,41 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
/* For the reg->type == PTR_TO_STACK case, bpf_dynptr is never const */
if (reg->type == CONST_PTR_TO_DYNPTR && (arg_type & OBJ_RELEASE)) {
verbose(env, "CONST_PTR_TO_DYNPTR cannot be released\n");
+ bpf_diag_res(env, insn_idx, "const dynptr release",
+ "This release operation was given a const dynptr. Const "
+ "dynptr values are verifier-provided views and cannot be "
+ "released by the program.",
+ "Release only mutable dynptrs that the program initialized or "
+ "reserved.");
return -EINVAL;
}
if (!is_dynptr_reg_valid_init(env, reg)) {
verbose(env, "Expected an initialized dynptr as %s\n",
reg_arg_name(env, argno));
+ bpf_diag_res(env, insn_idx, "uninitialized dynptr use",
+ "This operation requires an initialized dynptr, but the stack "
+ "slot does not currently hold a valid dynptr on this path.",
+ "Initialize the dynptr on every path before this call, and "
+ "avoid overwriting or releasing it before this use.");
return -EINVAL;
}
/* Fold modifiers (in this case, OBJ_RELEASE) when checking expected type */
if (!is_dynptr_type_expected(env, reg, arg_type & ~OBJ_RELEASE)) {
- verbose(env,
- "Expected a dynptr of type %s as %s\n",
- dynptr_type_str(arg_to_dynptr_type(arg_type)),
- reg_arg_name(env, argno));
+ const char *fmt = "The dynptr is initialized with backing object type %s, "
+ "but this operation expects dynptr type %s.";
+ enum bpf_dynptr_type expected_type = arg_to_dynptr_type(arg_type);
+ enum bpf_dynptr_type actual_type = dynptr_reg_type(env, reg);
+ const char *reason;
+
+ verbose(env, "Expected a dynptr of type %s as %s\n",
+ dynptr_type_str(expected_type), reg_arg_name(env, argno));
+ reason = bpf_diag_scratch_printf(env, 0, fmt, dynptr_type_str(actual_type),
+ dynptr_type_str(expected_type));
+ bpf_diag_res(env, insn_idx, "wrong dynptr type", reason,
+ "Use a dynptr constructor that matches this operation, or "
+ "call an operation that accepts the dynptr's current type.");
return -EINVAL;
}
@@ -7747,8 +7852,17 @@ static int process_iter_arg(struct bpf_verifier_env *env, struct bpf_reg_state *
int spi, err, i, nr_slots, btf_id;
if (reg->type != PTR_TO_STACK) {
+ const char *fmt = "Iterator state must live in verifier-tracked stack memory, but "
+ "%s is %s.";
+ const char *reason;
+
verbose(env, "%s expected pointer to an iterator on stack\n",
reg_arg_name(env, argno));
+ reason = bpf_diag_scratch_printf(env, 0, fmt, reg_arg_name(env, argno),
+ bpf_diag_reg_type_plain(env, reg->type));
+ bpf_diag_res(env, insn_idx, "invalid iterator argument", reason,
+ "Pass the address of a stack iterator object for iterator new, next, "
+ "and destroy calls.");
return -EINVAL;
}
@@ -7762,6 +7876,10 @@ static int process_iter_arg(struct bpf_verifier_env *env, struct bpf_reg_state *
if (btf_id < 0) {
verbose(env, "expected valid iter pointer as %s\n",
reg_arg_name(env, argno));
+ bpf_diag_res(env, insn_idx, "invalid iterator type",
+ "The kfunc expects a recognized iterator state pointer, but this "
+ "argument does not match a valid iterator type.",
+ "Pass the exact iterator state type expected by this kfunc.");
return -EINVAL;
}
t = btf_type_by_id(meta->btf, btf_id);
@@ -7772,6 +7890,12 @@ static int process_iter_arg(struct bpf_verifier_env *env, struct bpf_reg_state *
if (!is_iter_reg_valid_uninit(env, reg, nr_slots)) {
verbose(env, "expected uninitialized iter_%s as %s\n",
iter_type_str(meta->btf, btf_id), reg_arg_name(env, argno));
+ bpf_diag_res(env, insn_idx, "iterator is already initialized",
+ "Iterator creation requires an uninitialized iterator stack "
+ "object, but this stack range already contains iterator "
+ "state.",
+ "Use a fresh iterator stack slot, or destroy the existing "
+ "iterator before reusing the slot.");
return -EINVAL;
}
@@ -7796,9 +7920,21 @@ static int process_iter_arg(struct bpf_verifier_env *env, struct bpf_reg_state *
case -EINVAL:
verbose(env, "expected an initialized iter_%s as %s\n",
iter_type_str(meta->btf, btf_id), reg_arg_name(env, argno));
+ bpf_diag_res(env, insn_idx, "uninitialized iterator use",
+ "This iterator operation requires an initialized iterator "
+ "state object, but the stack range does not contain a live "
+ "iterator on this path.",
+ "Call the matching iterator new kfunc on every path before "
+ "calling next or destroy, and do not destroy the iterator "
+ "before this use.");
return err;
case -EPROTO:
verbose(env, "expected an RCU CS when using %s\n", meta->func_name);
+ bpf_diag_res(env, insn_idx, "iterator requires RCU read lock",
+ "This iterator was created for use under RCU protection, but "
+ "this path is not currently inside an RCU read lock region.",
+ "Wrap iterator use in bpf_rcu_read_lock() and "
+ "bpf_rcu_read_unlock(), keeping all exit paths balanced.");
return err;
default:
return err;
@@ -10493,6 +10629,7 @@ static int check_reference_leak(struct bpf_verifier_env *env, bool exception_exi
continue;
verbose(env, "Unreleased reference id=%d alloc_insn=%d\n",
state->refs[i].id, state->refs[i].insn_idx);
+ bpf_diag_leak(env, state->refs[i].id, state->refs[i].insn_idx, env->insn_idx);
refs_lingering = true;
}
return refs_lingering ? -EINVAL : 0;
@@ -11988,6 +12125,12 @@ static int process_irq_flag(struct bpf_verifier_env *env, struct bpf_reg_state *
if (!is_irq_flag_reg_valid_uninit(env, reg)) {
verbose(env, "expected uninitialized irq flag as %s\n",
reg_arg_name(env, argno));
+ bpf_diag_res(env, env->insn_idx, "IRQ flag is already initialized",
+ "Saving IRQ state requires an uninitialized stack slot for "
+ "the IRQ flag, but this slot already contains tracked IRQ "
+ "flag state.",
+ "Use a fresh stack slot for this save operation, or restore "
+ "the existing IRQ flag before reusing the slot.");
return -EINVAL;
}
@@ -12004,6 +12147,11 @@ static int process_irq_flag(struct bpf_verifier_env *env, struct bpf_reg_state *
if (err) {
verbose(env, "expected an initialized irq flag as %s\n",
reg_arg_name(env, argno));
+ bpf_diag_res(env, env->insn_idx, "uninitialized IRQ flag restore",
+ "Restoring IRQ state requires a stack slot that was "
+ "initialized by a matching IRQ save operation on this path.",
+ "Pass the same stack slot that was previously initialized by "
+ "the matching IRQ save kfunc.");
return err;
}
--
2.53.0
next prev parent reply other threads:[~2026-07-13 15:39 UTC|newest]
Thread overview: 28+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-13 15:38 [PATCH bpf-next v3 00/17] Redesign Verification Errors Kumar Kartikeya Dwivedi
2026-07-13 15:38 ` [PATCH bpf-next v3 01/17] bpf: Add verifier diagnostics report helpers Kumar Kartikeya Dwivedi
2026-07-13 15:50 ` sashiko-bot
2026-07-13 15:38 ` [PATCH bpf-next v3 02/17] bpf: Add source and instruction diagnostic context Kumar Kartikeya Dwivedi
2026-07-13 15:38 ` [PATCH bpf-next v3 03/17] bpf: Add verifier diagnostic event log Kumar Kartikeya Dwivedi
2026-07-13 15:54 ` sashiko-bot
2026-07-13 15:38 ` [PATCH bpf-next v3 04/17] bpf: Prune verifier diagnostics when switching paths Kumar Kartikeya Dwivedi
2026-07-13 16:02 ` sashiko-bot
2026-07-13 15:38 ` [PATCH bpf-next v3 05/17] bpf: Track verifier register diagnostic events Kumar Kartikeya Dwivedi
2026-07-13 16:06 ` sashiko-bot
2026-07-13 15:38 ` [PATCH bpf-next v3 06/17] bpf: Track verifier reference " Kumar Kartikeya Dwivedi
2026-07-13 15:38 ` [PATCH bpf-next v3 07/17] bpf: Track verifier context " Kumar Kartikeya Dwivedi
2026-07-13 15:38 ` [PATCH bpf-next v3 08/17] bpf: Report Register Type Safety errors Kumar Kartikeya Dwivedi
2026-07-13 15:38 ` [PATCH bpf-next v3 09/17] bpf: Report Memory Safety bounds errors Kumar Kartikeya Dwivedi
2026-07-13 15:38 ` Kumar Kartikeya Dwivedi [this message]
2026-07-13 16:04 ` [PATCH bpf-next v3 10/17] bpf: Report Resource Lifetime reference leaks sashiko-bot
2026-07-13 15:39 ` [PATCH bpf-next v3 11/17] bpf: Report Call Type Safety argument errors Kumar Kartikeya Dwivedi
2026-07-13 15:51 ` sashiko-bot
2026-07-13 15:39 ` [PATCH bpf-next v3 12/17] bpf: Report Execution Context Safety errors Kumar Kartikeya Dwivedi
2026-07-13 15:53 ` sashiko-bot
2026-07-13 15:39 ` [PATCH bpf-next v3 13/17] bpf: Report Program Structure CFG errors Kumar Kartikeya Dwivedi
2026-07-13 15:39 ` [PATCH bpf-next v3 14/17] bpf: Report Policy helper and kfunc errors Kumar Kartikeya Dwivedi
2026-07-13 16:09 ` sashiko-bot
2026-07-13 15:39 ` [PATCH bpf-next v3 15/17] bpf: Report Verifier Limit errors Kumar Kartikeya Dwivedi
2026-07-13 15:39 ` [PATCH bpf-next v3 16/17] bpf: Report Verifier Internal errors Kumar Kartikeya Dwivedi
2026-07-13 15:39 ` [PATCH bpf-next v3 17/17] bpf: Gate verifier diagnostics on log level Kumar Kartikeya Dwivedi
2026-07-14 3:36 ` kernel test robot
2026-07-14 6:10 ` [syzbot ci] Re: Redesign Verification Errors syzbot ci
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260713153910.2556007-11-memxor@gmail.com \
--to=memxor@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=emil@etsalapatis.com \
--cc=kernel-team@meta.com \
--cc=kkd@meta.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.