AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/06 10:05 flow patch-triage
1m Args:
null

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL"
  ],
  "FocusSymbols": [
    "bpf_check",
    "do_check_subprogs",
    "do_check_common",
    "check_func_call",
    "do_check_main"
  ],
  "KMSANReasoning": "The patch modifies the BPF verifier to track and verify global subprogram calls separately for sleepable and non-sleepable/RCU contexts. It replaces the single-bit `called` and `verified` flags in `struct bpf_func_info_aux` with two-element boolean arrays indexed by `in_sleepable`.\n\n- KMSAN applicability: There is no uninitialized memory risk. The `bpf_func_info_aux` structures are zero-initialized upon allocation (via `kzalloc_objs`), and all local variables introduced or modified in `do_check_subprogs()`, `do_check_common()`, and `check_func_call()` are properly initialized. The arrays are indexed using boolean values (0 or 1), matching the array dimension. No uninitialized memory or structure padding is exposed or copied to user space.\n- KASAN / Standard Detectors: Any memory access issues (such as array out-of-bounds access, invalid pointer dereferences, state allocation issues, or logic/verifier traversal bugs) would be covered by KASAN and standard kernel debugging facilities.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch modifies the BPF verifier to track and verify global subprograms separately across sleepable and non-sleepable execution contexts, modifying subprogram verification loops, topological traversal order, and context state handling in kernel/bpf/verifier.c. This is reachable via the bpf(BPF_PROG_LOAD) syscall and warrants fuzzing.",
  "WorthFuzzing": true
}

1/1 2026/09/06 10:05 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 56efa603a0d522e6eec63230d19a6e260dfee3d8\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Sun Sep 6 10:05:12 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/include/linux/bpf.h b/include/linux/bpf.h\nindex 3a7eb2185c354..66d04244c7378 100644\n--- a/include/linux/bpf.h\n+++ b/include/linux/bpf.h\n@@ -1650,8 +1650,9 @@ static inline void bpf_trampoline_set_flags(struct bpf_trampoline *tr, u32 flags\n struct bpf_func_info_aux {\n \tu16 linkage;\n \tbool unreliable;\n-\tbool called : 1;\n-\tbool verified : 1;\n+\t/* Indexed by in_sleepable. */\n+\tbool called[2];\n+\tbool verified[2];\n };\n \n enum bpf_jit_poke_reason {\ndiff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c\nindex 5b51e7ee1a3f9..f759a020c8a5a 100644\n--- a/kernel/bpf/verifier.c\n+++ b/kernel/bpf/verifier.c\n@@ -9957,6 +9957,7 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n \tif (err == -EFAULT)\n \t\treturn err;\n \tif (bpf_subprog_is_global(env, subprog)) {\n+\t\tstruct bpf_func_info_aux *sub_aux = subprog_aux(env, subprog);\n \t\tconst char *sub_name = bpf_subprog_name(env, subprog);\n \t\tconst char *operation;\n \t\tbool returns_void;\n@@ -9988,11 +9989,10 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n \t\tif (env-\u003elog.level \u0026 BPF_LOG_LEVEL)\n \t\t\tverbose(env, \"Func#%d ('%s') is global and assumed valid.\\n\",\n \t\t\t\tsubprog, sub_name);\n+\t\tsub_aux-\u003ecalled[in_sleepable_context(env)] = true;\n \t\treturns_void = subprog_returns_void(env, subprog);\n \t\tif (env-\u003esubprog_info[subprog].changes_pkt_data)\n \t\t\tclear_all_pkt_pointers(env);\n-\t\t/* mark global subprog for verifying after main prog */\n-\t\tsubprog_aux(env, subprog)-\u003ecalled = true;\n \t\tif (returns_void)\n \t\t\tbpf_diag_record_scrub(env, \u0026caller-\u003eregs[BPF_REG_0], BPF_DIAG_MOD_CALLER_SAVED);\n \t\telse\n@@ -10804,7 +10804,12 @@ int bpf_get_helper_proto(struct bpf_verifier_env *env, int func_id,\n \treturn *ptr \u0026\u0026 (*ptr)-\u003efunc ? 0 : -EINVAL;\n }\n \n-/* Check if we're in a sleepable context. */\n+/*\n+ * This predicate is the inverse of in_rcu_cs(): non-sleepable programs and\n+ * every condition that prevents sleeping also provide RCU protection. Global\n+ * subprog verification relies on this equivalence to represent the caller's\n+ * execution context using only the in_sleepable bit.\n+ */\n static inline bool in_sleepable_context(struct bpf_verifier_env *env)\n {\n \treturn !env-\u003ecur_state-\u003eactive_rcu_locks \u0026\u0026\n@@ -19560,13 +19565,14 @@ static void free_states(struct bpf_verifier_env *env)\n \t}\n }\n \n-static int do_check_common(struct bpf_verifier_env *env, int subprog)\n+static int do_check_common(struct bpf_verifier_env *env, int subprog, bool in_sleepable)\n {\n \tbool pop_log = !(env-\u003elog.level \u0026 BPF_LOG_LEVEL2);\n \tstruct bpf_subprog_info *sub = subprog_info(env, subprog);\n \tstruct bpf_prog_aux *aux = env-\u003eprog-\u003eaux;\n \tstruct bpf_verifier_state *state;\n \tstruct bpf_reg_state *regs;\n+\tu32 old_insns_total = sub-\u003einsns_total;\n \tu32 insn_processed = env-\u003einsn_processed;\n \tint ret, i;\n \n@@ -19579,7 +19585,7 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)\n \tstate-\u003ecurframe = 0;\n \tstate-\u003especulative = false;\n \tstate-\u003ebranches = 1;\n-\tstate-\u003ein_sleepable = env-\u003eprog-\u003esleepable;\n+\tstate-\u003ein_sleepable = in_sleepable;\n \tstate-\u003eframe[0] = kzalloc_obj(struct bpf_func_state, GFP_KERNEL_ACCOUNT);\n \tif (!state-\u003eframe[0]) {\n \t\tkfree(state);\n@@ -19721,7 +19727,8 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)\n \t * Accumulate their total counts as total counts of the main or\n \t * global subprog hosting the async call.\n \t */\n-\tenv-\u003esubprog_info[subprog].insns_total = env-\u003einsn_processed - insn_processed;\n+\tenv-\u003esubprog_info[subprog].insns_total = old_insns_total +\n+\t\t\t\t\t\t(env-\u003einsn_processed - insn_processed);\n \treturn ret;\n }\n \n@@ -19749,45 +19756,55 @@ static int do_check_subprogs(struct bpf_verifier_env *env)\n {\n \tstruct bpf_prog_aux *aux = env-\u003eprog-\u003eaux;\n \tstruct bpf_func_info_aux *sub_aux;\n-\tint i, ret, new_cnt;\n+\tint context, i, j, ret, new_cnt;\n \n \tif (!aux-\u003efunc_info)\n \t\treturn 0;\n \n \t/* exception callback is presumed to be always called */\n-\tif (env-\u003eexception_callback_subprog)\n-\t\tsubprog_aux(env, env-\u003eexception_callback_subprog)-\u003ecalled = true;\n+\tif (env-\u003eexception_callback_subprog) {\n+\t\tsub_aux = subprog_aux(env, env-\u003eexception_callback_subprog);\n+\t\tsub_aux-\u003ecalled[env-\u003eprog-\u003esleepable] = true;\n+\t}\n \n again:\n \tnew_cnt = 0;\n-\tfor (i = 1; i \u003c env-\u003esubprog_cnt; i++) {\n+\t/*\n+\t * Walk callers before callees so each global subprog normally sees all\n+\t * of its contexts before it is verified. Async callback cycles can add a\n+\t * context to an earlier subprog, so repeat until every called context is\n+\t * verified.\n+\t */\n+\tfor (j = env-\u003esubprog_cnt - 1; j \u003e= 0; j--) {\n+\t\ti = env-\u003esubprog_topo_order[j];\n+\t\tif (!i)\n+\t\t\tcontinue;\n \t\tif (!bpf_subprog_is_global(env, i))\n \t\t\tcontinue;\n \n \t\tsub_aux = subprog_aux(env, i);\n-\t\tif (!sub_aux-\u003ecalled || sub_aux-\u003everified)\n-\t\t\tcontinue;\n+\t\tfor (context = 0; context \u003c ARRAY_SIZE(sub_aux-\u003ecalled); context++) {\n+\t\t\tif (!sub_aux-\u003ecalled[context] || sub_aux-\u003everified[context])\n+\t\t\t\tcontinue;\n \n-\t\tenv-\u003einsn_idx = env-\u003esubprog_info[i].start;\n-\t\tWARN_ON_ONCE(env-\u003einsn_idx == 0);\n-\t\tret = do_check_common(env, i);\n-\t\tif (ret) {\n-\t\t\treturn ret;\n-\t\t} else if (env-\u003elog.level \u0026 BPF_LOG_LEVEL) {\n-\t\t\tverbose(env, \"Func#%d ('%s') is safe for any args that match its prototype\\n\",\n-\t\t\t\ti, bpf_subprog_name(env, i));\n-\t\t}\n+\t\t\tenv-\u003einsn_idx = env-\u003esubprog_info[i].start;\n+\t\t\tWARN_ON_ONCE(env-\u003einsn_idx == 0);\n+\t\t\tret = do_check_common(env, i, context);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t\tif (env-\u003elog.level \u0026 BPF_LOG_LEVEL)\n+\t\t\t\tverbose(env, \"Func#%d ('%s') is safe for any args \"\n+\t\t\t\t\t\"that match its prototype\\n\",\n+\t\t\t\t\ti, bpf_subprog_name(env, i));\n \n-\t\t/* We verified new global subprog, it might have called some\n-\t\t * more global subprogs that we haven't verified yet, so we\n-\t\t * need to do another pass over subprogs to verify those.\n-\t\t */\n-\t\tsub_aux-\u003everified = true;\n-\t\tnew_cnt++;\n+\t\t\tsub_aux-\u003everified[context] = true;\n+\t\t\tnew_cnt++;\n+\t\t}\n \t}\n \n-\t/* We can't loop forever as we verify at least one global subprog on\n-\t * each pass.\n+\t/*\n+\t * We can't loop forever as each pass verifies at least one new context,\n+\t * and there are only two contexts per global subprog.\n \t */\n \tif (new_cnt)\n \t\tgoto again;\n@@ -19800,7 +19817,7 @@ static int do_check_main(struct bpf_verifier_env *env)\n \tint ret;\n \n \tenv-\u003einsn_idx = 0;\n-\tret = do_check_common(env, 0);\n+\tret = do_check_common(env, 0, env-\u003eprog-\u003esleepable);\n \tif (!ret)\n \t\tenv-\u003eprog-\u003eaux-\u003estack_depth = env-\u003esubprog_info[0].stack_depth;\n \treturn ret;\ndiff --git a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c\nindex 6bf95550a0248..987ca9dec6b7f 100644\n--- a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c\n+++ b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c\n@@ -9,6 +9,11 @@\n \n char _license[] SEC(\"license\") = \"GPL\";\n \n+struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym;\n+void bpf_task_release(struct task_struct *p) __ksym;\n+void bpf_rcu_read_lock(void) __ksym;\n+void bpf_rcu_read_unlock(void) __ksym;\n+\n /* Timer tests */\n \n struct timer_elem {\n@@ -66,6 +71,7 @@ int timer_sleepable_prog(void *ctx)\n \n struct wq_elem {\n \tstruct bpf_wq w;\n+\tstruct task_struct __kptr *task;\n };\n \n struct {\n@@ -119,6 +125,106 @@ int wq_sleepable_prog(void *ctx)\n \treturn 0;\n }\n \n+__noinline int wq_global_acquire(void)\n+{\n+\tstruct task_struct *task, *acquired;\n+\tstruct wq_elem *val;\n+\tint key = 0;\n+\n+\tval = bpf_map_lookup_elem(\u0026wq_map, \u0026key);\n+\tif (!val)\n+\t\treturn 0;\n+\n+\ttask = val-\u003etask;\n+\tif (!task)\n+\t\treturn 0;\n+\n+\tacquired = bpf_task_acquire(task);\n+\tif (acquired)\n+\t\tbpf_task_release(acquired);\n+\treturn 0;\n+}\n+\n+static int wq_global_rcu_cb(void *map, int *key, void *value)\n+{\n+\twq_global_acquire();\n+\treturn 0;\n+}\n+\n+SEC(\"fentry/bpf_fentry_test1\")\n+__failure __msg(\"R1 must be a rcu pointer\")\n+int wq_global_rcu_prog(void *ctx)\n+{\n+\tstruct wq_elem *val;\n+\tint key = 0;\n+\n+\tval = bpf_map_lookup_elem(\u0026wq_map, \u0026key);\n+\tif (!val)\n+\t\treturn 0;\n+\n+\tbpf_wq_init(\u0026val-\u003ew, \u0026wq_map, 0);\n+\tbpf_wq_set_callback(\u0026val-\u003ew, wq_global_rcu_cb, 0);\n+\treturn 0;\n+}\n+\n+static int wq_global_rcu_lock_cb(void *map, int *key, void *value)\n+{\n+\tbpf_rcu_read_lock();\n+\twq_global_acquire();\n+\tbpf_rcu_read_unlock();\n+\treturn 0;\n+}\n+\n+SEC(\"fentry/bpf_fentry_test1\")\n+__success\n+int wq_global_rcu_lock_prog(void *ctx)\n+{\n+\tstruct wq_elem *val;\n+\tint key = 0;\n+\n+\t/* Verify the same global subprog in non-sleepable and protected contexts. */\n+\twq_global_acquire();\n+\n+\tval = bpf_map_lookup_elem(\u0026wq_map, \u0026key);\n+\tif (!val)\n+\t\treturn 0;\n+\n+\tbpf_wq_init(\u0026val-\u003ew, \u0026wq_map, 0);\n+\tbpf_wq_set_callback(\u0026val-\u003ew, wq_global_rcu_lock_cb, 0);\n+\treturn 0;\n+}\n+\n+__noinline int wq_global_no_rcu(void)\n+{\n+\tbarrier();\n+\treturn 0;\n+}\n+\n+static int wq_global_no_rcu_cb(void *map, int *key, void *value)\n+{\n+\twq_global_no_rcu();\n+\treturn 0;\n+}\n+\n+SEC(\"fentry/bpf_fentry_test1\")\n+__success\n+int wq_global_no_rcu_prog(void *ctx)\n+{\n+\tstruct wq_elem *val;\n+\tint key = 0;\n+\n+\t/* Verify the same global in non-sleepable and unprotected contexts. */\n+\twq_global_no_rcu();\n+\n+\tval = bpf_map_lookup_elem(\u0026wq_map, \u0026key);\n+\tif (!val)\n+\t\treturn 0;\n+\n+\tbpf_wq_init(\u0026val-\u003ew, \u0026wq_map, 0);\n+\tbpf_wq_set_callback(\u0026val-\u003ew, wq_global_no_rcu_cb, 0);\n+\treturn 0;\n+}\n+\n /* Task work tests */\n \n struct task_work_elem {\n@@ -179,3 +285,29 @@ int task_work_sleepable_prog(void *ctx)\n \tbpf_task_work_schedule_resume(task, \u0026val-\u003etw, \u0026task_work_map, task_work_cb);\n \treturn 0;\n }\n+\n+static int task_work_global_rcu_cb(struct bpf_map *map, void *key, void *value)\n+{\n+\treturn wq_global_acquire();\n+}\n+\n+SEC(\"fentry/bpf_fentry_test1\")\n+__failure __msg(\"R1 must be a rcu pointer\")\n+int task_work_global_rcu_prog(void *ctx)\n+{\n+\tstruct task_work_elem *val;\n+\tstruct task_struct *task;\n+\tint key = 0;\n+\n+\tval = bpf_map_lookup_elem(\u0026task_work_map, \u0026key);\n+\tif (!val)\n+\t\treturn 0;\n+\n+\ttask = bpf_get_current_task_btf();\n+\tif (!task)\n+\t\treturn 0;\n+\n+\tbpf_task_work_schedule_resume(task, \u0026val-\u003etw, \u0026task_work_map,\n+\t\t\t\t      task_work_global_rcu_cb);\n+\treturn 0;\n+}\n"
}

2/1 2026/09/06 10:05 agent patch-evaluator
0m Model:
core

Args:
null

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL"
  ],
  "FocusSymbols": [
    "bpf_check",
    "do_check_subprogs",
    "do_check_common",
    "check_func_call",
    "do_check_main"
  ],
  "Reasoning": "The patch modifies the BPF verifier to track and verify global subprograms separately across sleepable and non-sleepable execution contexts, modifying subprogram verification loops, topological traversal order, and context state handling in kernel/bpf/verifier.c. This is reachable via the bpf(BPF_PROG_LOAD) syscall and warrants fuzzing.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 56efa603a0d522e6eec63230d19a6e260dfee3d8
Author: syz-cluster <triage@syzkaller.com>
Date:   Sun Sep 6 10:05:12 2026 +0000

    syz-cluster: applied patch under review

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 3a7eb2185c354..66d04244c7378 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1650,8 +1650,9 @@ static inline void bpf_trampoline_set_flags(struct bpf_trampoline *tr, u32 flags
 struct bpf_func_info_aux {
 	u16 linkage;
 	bool unreliable;
-	bool called : 1;
-	bool verified : 1;
+	/* Indexed by in_sleepable. */
+	bool called[2];
+	bool verified[2];
 };
 
 enum bpf_jit_poke_reason {
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 5b51e7ee1a3f9..f759a020c8a5a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9957,6 +9957,7 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 	if (err == -EFAULT)
 		return err;
 	if (bpf_subprog_is_global(env, subprog)) {
+		struct bpf_func_info_aux *sub_aux = subprog_aux(env, subprog);
 		const char *sub_name = bpf_subprog_name(env, subprog);
 		const char *operation;
 		bool returns_void;
@@ -9988,11 +9989,10 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 		if (env->log.level & BPF_LOG_LEVEL)
 			verbose(env, "Func#%d ('%s') is global and assumed valid.\n",
 				subprog, sub_name);
+		sub_aux->called[in_sleepable_context(env)] = true;
 		returns_void = subprog_returns_void(env, subprog);
 		if (env->subprog_info[subprog].changes_pkt_data)
 			clear_all_pkt_pointers(env);
-		/* mark global subprog for verifying after main prog */
-		subprog_aux(env, subprog)->called = true;
 		if (returns_void)
 			bpf_diag_record_scrub(env, &caller->regs[BPF_REG_0], BPF_DIAG_MOD_CALLER_SAVED);
 		else
@@ -10804,7 +10804,12 @@ int bpf_get_helper_proto(struct bpf_verifier_env *env, int func_id,
 	return *ptr && (*ptr)->func ? 0 : -EINVAL;
 }
 
-/* Check if we're in a sleepable context. */
+/*
+ * This predicate is the inverse of in_rcu_cs(): non-sleepable programs and
+ * every condition that prevents sleeping also provide RCU protection. Global
+ * subprog verification relies on this equivalence to represent the caller's
+ * execution context using only the in_sleepable bit.
+ */
 static inline bool in_sleepable_context(struct bpf_verifier_env *env)
 {
 	return !env->cur_state->active_rcu_locks &&
@@ -19560,13 +19565,14 @@ static void free_states(struct bpf_verifier_env *env)
 	}
 }
 
-static int do_check_common(struct bpf_verifier_env *env, int subprog)
+static int do_check_common(struct bpf_verifier_env *env, int subprog, bool in_sleepable)
 {
 	bool pop_log = !(env->log.level & BPF_LOG_LEVEL2);
 	struct bpf_subprog_info *sub = subprog_info(env, subprog);
 	struct bpf_prog_aux *aux = env->prog->aux;
 	struct bpf_verifier_state *state;
 	struct bpf_reg_state *regs;
+	u32 old_insns_total = sub->insns_total;
 	u32 insn_processed = env->insn_processed;
 	int ret, i;
 
@@ -19579,7 +19585,7 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
 	state->curframe = 0;
 	state->speculative = false;
 	state->branches = 1;
-	state->in_sleepable = env->prog->sleepable;
+	state->in_sleepable = in_sleepable;
 	state->frame[0] = kzalloc_obj(struct bpf_func_state, GFP_KERNEL_ACCOUNT);
 	if (!state->frame[0]) {
 		kfree(state);
@@ -19721,7 +19727,8 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
 	 * Accumulate their total counts as total counts of the main or
 	 * global subprog hosting the async call.
 	 */
-	env->subprog_info[subprog].insns_total = env->insn_processed - insn_processed;
+	env->subprog_info[subprog].insns_total = old_insns_total +
+						(env->insn_processed - insn_processed);
 	return ret;
 }
 
@@ -19749,45 +19756,55 @@ static int do_check_subprogs(struct bpf_verifier_env *env)
 {
 	struct bpf_prog_aux *aux = env->prog->aux;
 	struct bpf_func_info_aux *sub_aux;
-	int i, ret, new_cnt;
+	int context, i, j, ret, new_cnt;
 
 	if (!aux->func_info)
 		return 0;
 
 	/* exception callback is presumed to be always called */
-	if (env->exception_callback_subprog)
-		subprog_aux(env, env->exception_callback_subprog)->called = true;
+	if (env->exception_callback_subprog) {
+		sub_aux = subprog_aux(env, env->exception_callback_subprog);
+		sub_aux->called[env->prog->sleepable] = true;
+	}
 
 again:
 	new_cnt = 0;
-	for (i = 1; i < env->subprog_cnt; i++) {
+	/*
+	 * Walk callers before callees so each global subprog normally sees all
+	 * of its contexts before it is verified. Async callback cycles can add a
+	 * context to an earlier subprog, so repeat until every called context is
+	 * verified.
+	 */
+	for (j = env->subprog_cnt - 1; j >= 0; j--) {
+		i = env->subprog_topo_order[j];
+		if (!i)
+			continue;
 		if (!bpf_subprog_is_global(env, i))
 			continue;
 
 		sub_aux = subprog_aux(env, i);
-		if (!sub_aux->called || sub_aux->verified)
-			continue;
+		for (context = 0; context < ARRAY_SIZE(sub_aux->called); context++) {
+			if (!sub_aux->called[context] || sub_aux->verified[context])
+				continue;
 
-		env->insn_idx = env->subprog_info[i].start;
-		WARN_ON_ONCE(env->insn_idx == 0);
-		ret = do_check_common(env, i);
-		if (ret) {
-			return ret;
-		} else if (env->log.level & BPF_LOG_LEVEL) {
-			verbose(env, "Func#%d ('%s') is safe for any args that match its prototype\n",
-				i, bpf_subprog_name(env, i));
-		}
+			env->insn_idx = env->subprog_info[i].start;
+			WARN_ON_ONCE(env->insn_idx == 0);
+			ret = do_check_common(env, i, context);
+			if (ret)
+				return ret;
+			if (env->log.level & BPF_LOG_LEVEL)
+				verbose(env, "Func#%d ('%s') is safe for any args "
+					"that match its prototype\n",
+					i, bpf_subprog_name(env, i));
 
-		/* We verified new global subprog, it might have called some
-		 * more global subprogs that we haven't verified yet, so we
-		 * need to do another pass over subprogs to verify those.
-		 */
-		sub_aux->verified = true;
-		new_cnt++;
+			sub_aux->verified[context] = true;
+			new_cnt++;
+		}
 	}
 
-	/* We can't loop forever as we verify at least one global subprog on
-	 * each pass.
+	/*
+	 * We can't loop forever as each pass verifies at least one new context,
+	 * and there are only two contexts per global subprog.
 	 */
 	if (new_cnt)
 		goto again;
@@ -19800,7 +19817,7 @@ static int do_check_main(struct bpf_verifier_env *env)
 	int ret;
 
 	env->insn_idx = 0;
-	ret = do_check_common(env, 0);
+	ret = do_check_common(env, 0, env->prog->sleepable);
 	if (!ret)
 		env->prog->aux->stack_depth = env->subprog_info[0].stack_depth;
 	return ret;
diff --git a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
index 6bf95550a0248..987ca9dec6b7f 100644
--- a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
+++ b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
@@ -9,6 +9,11 @@
 
 char _license[] SEC("license") = "GPL";
 
+struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym;
+void bpf_task_release(struct task_struct *p) __ksym;
+void bpf_rcu_read_lock(void) __ksym;
+void bpf_rcu_read_unlock(void) __ksym;
+
 /* Timer tests */
 
 struct timer_elem {
@@ -66,6 +71,7 @@ int timer_sleepable_prog(void *ctx)
 
 struct wq_elem {
 	struct bpf_wq w;
+	struct task_struct __kptr *task;
 };
 
 struct {
@@ -119,6 +125,106 @@ int wq_sleepable_prog(void *ctx)
 	return 0;
 }
 
+__noinline int wq_global_acquire(void)
+{
+	struct task_struct *task, *acquired;
+	struct wq_elem *val;
+	int key = 0;
+
+	val = bpf_map_lookup_elem(&wq_map, &key);
+	if (!val)
+		return 0;
+
+	task = val->task;
+	if (!task)
+		return 0;
+
+	acquired = bpf_task_acquire(task);
+	if (acquired)
+		bpf_task_release(acquired);
+	return 0;
+}
+
+static int wq_global_rcu_cb(void *map, int *key, void *value)
+{
+	wq_global_acquire();
+	return 0;
+}
+
+SEC("fentry/bpf_fentry_test1")
+__failure __msg("R1 must be a rcu pointer")
+int wq_global_rcu_prog(void *ctx)
+{
+	struct wq_elem *val;
+	int key = 0;
+
+	val = bpf_map_lookup_elem(&wq_map, &key);
+	if (!val)
+		return 0;
+
+	bpf_wq_init(&val->w, &wq_map, 0);
+	bpf_wq_set_callback(&val->w, wq_global_rcu_cb, 0);
+	return 0;
+}
+
+static int wq_global_rcu_lock_cb(void *map, int *key, void *value)
+{
+	bpf_rcu_read_lock();
+	wq_global_acquire();
+	bpf_rcu_read_unlock();
+	return 0;
+}
+
+SEC("fentry/bpf_fentry_test1")
+__success
+int wq_global_rcu_lock_prog(void *ctx)
+{
+	struct wq_elem *val;
+	int key = 0;
+
+	/* Verify the same global subprog in non-sleepable and protected contexts. */
+	wq_global_acquire();
+
+	val = bpf_map_lookup_elem(&wq_map, &key);
+	if (!val)
+		return 0;
+
+	bpf_wq_init(&val->w, &wq_map, 0);
+	bpf_wq_set_callback(&val->w, wq_global_rcu_lock_cb, 0);
+	return 0;
+}
+
+__noinline int wq_global_no_rcu(void)
+{
+	barrier();
+	return 0;
+}
+
+static int wq_global_no_rcu_cb(void *map, int *key, void *value)
+{
+	wq_global_no_rcu();
+	return 0;
+}
+
+SEC("fentry/bpf_fentry_test1")
+__success
+int wq_global_no_rcu_prog(void *ctx)
+{
+	struct wq_elem *val;
+	int key = 0;
+
+	/* Verify the same global in non-sleepable and unprotected contexts. */
+	wq_global_no_rcu();
+
+	val = bpf_map_lookup_elem(&wq_map, &key);
+	if (!val)
+		return 0;
+
+	bpf_wq_init(&val->w, &wq_map, 0);
+	bpf_wq_set_callback(&val->w, wq_global_no_rcu_cb, 0);
+	return 0;
+}
+
 /* Task work tests */
 
 struct task_work_elem {
@@ -179,3 +285,29 @@ int task_work_sleepable_prog(void *ctx)
 	bpf_task_work_schedule_resume(task, &val->tw, &task_work_map, task_work_cb);
 	return 0;
 }
+
+static int task_work_global_rcu_cb(struct bpf_map *map, void *key, void *value)
+{
+	return wq_global_acquire();
+}
+
+SEC("fentry/bpf_fentry_test1")
+__failure __msg("R1 must be a rcu pointer")
+int task_work_global_rcu_prog(void *ctx)
+{
+	struct task_work_elem *val;
+	struct task_struct *task;
+	int key = 0;
+
+	val = bpf_map_lookup_elem(&task_work_map, &key);
+	if (!val)
+		return 0;
+
+	task = bpf_get_current_task_btf();
+	if (!task)
+		return 0;
+
+	bpf_task_work_schedule_resume(task, &val->tw, &task_work_map,
+				      task_work_global_rcu_cb);
+	return 0;
+}

3/2 2026/09/06 10:05 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 6888
							output: 27
							thoughts: 616
						

Thoughts:
**My Initial Thoughts on the BPF Verifier Patch**

Okay, here's what I'm thinking as I look over this patch series for the kernel. The user wants me to analyze this, and my expert intuition tells me this touches something important, the BPF verifier.

First, I see the core changes are in `include/linux/bpf.h` and `kernel/bpf/verifier.c`.  Specifically, the struct `bpf_func_info_aux` has been modified. The `called` and `verified` booleans are now arrays of two elements, `called[2]` and `verified[2]`.  This immediately piques my interest. Why two?  I suspect this relates to tracking verification state in different contexts - likely sleepable and non-sleepable ones, which is confirmed later.

Looking deeper into `kernel/bpf/verifier.c`, I see functions like `check_func_call`, `do_check_common`, `do_check_subprogs`, and `do_check_main` are the focus. I notice `in_sleepable_context()` is used, and the patch seems to be strategically marking function calls as `called` and `verified` in different contexts, based on whether the program is operating in a sleepable context, which is likely critical.  The `do_check_subprogs` function is particularly interesting.  It now handles an exception callback and iterates through subprograms in a topological order. It does a loop `for (context = 0; context < ARRAY_SIZE(sub_aux->called); context++)` - this is the key.  It does *multiple* verification passes per context! That's a strong signal of significant logic change.

Then `do_check_main` calls `do_check_common`, again linking verification to the sleepable context. The changes related to the accumulating total instruction count in `do_check_common` also need to be scrutinized for potential issues related to instruction counting across these different verification states.

Now, from a fuzzing perspective, this looks promising. The BPF verifier is a critical path and easily reachable via the `bpf(BPF_PROG_LOAD, ...)` syscall. Syzkaller's deep BPF support makes it even easier to trigger. The core subsystem, logic changes around sleepable vs. non-sleepable contexts, the topological order loop and especially the multiple verification passes per context, all scream "WorthFuzzing=true."  This is definitely an area where subtle bugs could be lurking.

Finally, I need to check the Kconfig settings. The BPF verifier will likely require `CONFIG_BPF_SYSCALL` (and possibly `CONFIG_BPF`) to be enabled. I'll need to use `grepper` or check the kernel's `Kconfig` or relevant `Makefile` files to verify the exact configuration requirements before moving forward with any fuzzing plans.



4/2 2026/09/06 10:05 tool grepper
0m Args:
{
  "Expression": "verifier\\.c",
  "PathPrefix": "kernel/bpf"
}

Results:
{
  "Output": "kernel/bpf/bpf_iter.c=727=BPF_CALL_4(bpf_loop, u32, nr_loops, void *, callback_fn, void *, callback_ctx,\n--\nkernel/bpf/bpf_iter.c-735-\t * is inlined, be careful to modify this code in sync. See\nkernel/bpf/bpf_iter.c:736:\t * function verifier.c:inline_bpf_loop.\nkernel/bpf/bpf_iter.c-737-\t */\n--\nkernel/bpf/helpers.c=2464=__bpf_kfunc void *bpf_refcount_acquire(void *p__refcounted_kptr, struct btf_struct_meta *meta)\n--\nkernel/bpf/helpers.c-2475-\t/* Verifier strips KF_RET_NULL if input is owned ref, see is_kfunc_ret_null\nkernel/bpf/helpers.c:2476:\t * in verifier.c\nkernel/bpf/helpers.c-2477-\t */\n--\nkernel/bpf/task_iter.c=992=__bpf_kfunc struct vm_area_struct *bpf_iter_task_vma_next(struct bpf_iter_task_vma *it)\n--\nkernel/bpf/task_iter.c-1011-\t * The verifier only trusts vm_mm and vm_file (see\nkernel/bpf/task_iter.c:1012:\t * BTF_TYPE_SAFE_TRUSTED_OR_NULL in verifier.c). Take a reference\nkernel/bpf/task_iter.c-1013-\t * on vm_file; vm_mm is already correct because lock_vma_under_rcu()\n--\nkernel/bpf/verifier.c=14720=static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env, struct bpf_insn *insn,\n--\nkernel/bpf/verifier.c-14897-\t\t/* We don't allow subtraction from FP, because (according to\nkernel/bpf/verifier.c:14898:\t\t * test_verifier.c test \"invalid fp arithmetic\", JITs might not\nkernel/bpf/verifier.c-14899-\t\t * be able to deal with it.\n"
}

5/2 2026/09/06 10:05 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 8756
							output: 27
							thoughts: 22
						

Thoughts:
Here's my thought process, as if I were considering this myself:

**Investigating the BPF Verifier Configuration**

Okay, so I need to figure out what Kconfig option is responsible for enabling the BPF verifier code, specifically `verifier.c`. My initial move would be to dive right into the `kernel/bpf/Makefile`. That's where the build rules and dependencies for this subsystem would reside, and the Kconfig dependencies are typically defined alongside the source files. Hopefully, the inclusion of `verifier.c` will be conditional based on a specific configuration flag. That's the most likely place to start the hunt. Let's see what we can find in there.



6/2 2026/09/06 10:05 tool grepper
0m Args:
{
  "Expression": "verifier\\.o",
  "PathPrefix": "kernel/bpf"
}

Results:
{
  "Output": "kernel/bpf/Makefile=7=CFLAGS_core.o += -Wno-override-init $(cflags-nogcse-yy)\nkernel/bpf/Makefile-8-\nkernel/bpf/Makefile:9:obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o cnum.o log.o token.o liveness.o const_fold.o diagnostics.o\nkernel/bpf/Makefile-10-obj-$(CONFIG_BPF_SYSCALL) += bpf_iter.o map_iter.o task_iter.o prog_iter.o link_iter.o\n"
}

7/2 2026/09/06 10:05 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 9117
							output: 29
							thoughts: 67
						

Thoughts:
Here's my summary, as though I'm thinking through this process:

**Diving into the BPF Build Configuration**

Okay, so I'm looking at the `kernel/bpf/Makefile` and specifically line 9. It's building the BPF subsystem, and this line is key: `obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o ...` I see that the object files `syscall.o`, `verifier.o`, `inode.o`, `helpers.o` and potentially more, are conditionally included based on the `CONFIG_BPF_SYSCALL` Kconfig variable.

My next step is clear: I need to understand when and why `CONFIG_BPF_SYSCALL` is defined. Where does this get set? A quick look in `init/Kconfig` and `kernel/bpf/Kconfig` is definitely in order. I'm guessing it's in one of those, since they handle a lot of the initial kernel config. I need to figure out what triggers this config option to be selected. Is it a direct dependency? Another option? I need to understand its implications for the BPF system and how it affects the build process. From there, I can better understand the BPF subsystem's architecture and configuration.



8/2 2026/09/06 10:05 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "config BPF_SYSCALL",
  "PathPrefix": "init/"
}

Results:
{
  "error": "no matches"
}

9/2 2026/09/06 10:05 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 9513
							output: 21
							thoughts: 12
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Hunting for BPF_SYSCALL in the Config**

Okay, so I need to track down the configuration option for `BPF_SYSCALL`. I'm going to start by assuming it's likely a kernel configuration parameter, probably under one of the broader BPF or security-related sections. Since it's a syscall-related feature, I can almost guarantee its existence. I need to figure out how it's defined and what dependencies it might have. My first instinct is to use the standard tools; a `grep` through the kernel's `Kconfig` files should do the trick, maybe starting with a wildcard search like `Kconfig* BPF_SYSCALL`. Or perhaps I will use 'find' and 'grep' commands to search. Depending on the size of the codebase, I might need to refine that search if it's too broad initially. I'll need to pay close attention to the context around that option – what other features does it enable or disable? Are there any prerequisites I need to be aware of? And more importantly, what's the default value and what are the implications of enabling or disabling it? I'll probably also check relevant `Makefile`s to understand how this config option translates into build flags and ultimately, how it affects the generated kernel code. Ideally, I'll find a concise description of what the option actually *does* along the way. That's the priority right now, finding its definition.



10/2 2026/09/06 10:05 tool grepper
0m Args:
{
  "Expression": "config BPF_SYSCALL"
}

Results:
{
  "Output": "kernel/bpf/Kconfig=29=menu \"BPF subsystem\"\nkernel/bpf/Kconfig-30-\nkernel/bpf/Kconfig:31:config BPF_SYSCALL\nkernel/bpf/Kconfig-32-\tbool \"Enable bpf() system call\"\n"
}

11/2 2026/09/06 10:05 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 9954
							output: 34
							thoughts: 20
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Examining BPF Kconfig Dependencies**

Okay, so I need to understand the configuration dependencies for BPF. I'm going to jump into the `kernel/bpf/Kconfig` file, specifically around line 31. That area usually holds the core definitions and their dependencies. I'm expecting to see what options need to be enabled at compile time to get the basic BPF infrastructure up and running. I'll be looking for things like whether specific architecture features are required, any kernel configuration options that BPF relies on (like tracing or instrumentation capabilities, specific hardware support). I'm also going to see if any other BPF related configurations are also defined in this file. I'll make sure to note if the dependencies make sense or seem unusual.



12/2 2026/09/06 10:05 tool read-file
0m Args:
{
  "File": "kernel/bpf/Kconfig",
  "FirstLine": 25,
  "LineCount": 40
}

Results:
{
  "Contents": "  25:\t# default for kernels that were compiled with BPF JIT support.\n  26:\tconfig ARCH_WANT_DEFAULT_BPF_JIT\n  27:\t\tbool\n  28:\t\n  29:\tmenu \"BPF subsystem\"\n  30:\t\n  31:\tconfig BPF_SYSCALL\n  32:\t\tbool \"Enable bpf() system call\"\n  33:\t\tselect BPF\n  34:\t\tselect IRQ_WORK\n  35:\t\tselect NEED_TASKS_RCU\n  36:\t\tselect TASKS_TRACE_RCU\n  37:\t\tselect BINARY_PRINTF\n  38:\t\tselect NET_SOCK_MSG if NET\n  39:\t\tselect NET_XGRESS if NET\n  40:\t\tselect PAGE_POOL if NET\n  41:\t\tdefault n\n  42:\t\thelp\n  43:\t\t  Enable the bpf() system call that allows to manipulate BPF programs\n  44:\t\t  and maps via file descriptors.\n  45:\t\n  46:\tconfig BPF_JIT\n  47:\t\tbool \"Enable BPF Just In Time compiler\"\n  48:\t\tdepends on BPF\n  49:\t\tdepends on HAVE_CBPF_JIT || HAVE_EBPF_JIT\n  50:\t\tselect EXECMEM\n  51:\t\thelp\n  52:\t\t  BPF programs are normally handled by a BPF interpreter. This option\n  53:\t\t  allows the kernel to generate native code when a program is loaded\n  54:\t\t  into the kernel. This will significantly speed-up processing of BPF\n  55:\t\t  programs.\n  56:\t\n  57:\t\t  Note, an admin should enable this feature changing:\n  58:\t\t  /proc/sys/net/core/bpf_jit_enable\n  59:\t\t  /proc/sys/net/core/bpf_jit_harden   (optional)\n  60:\t\t  /proc/sys/net/core/bpf_jit_kallsyms (optional)\n  61:\t\n  62:\tconfig BPF_JIT_ALWAYS_ON\n  63:\t\tbool \"Permanently enable BPF JIT and remove BPF interpreter\"\n  64:\t\tdepends on BPF_SYSCALL \u0026\u0026 HAVE_EBPF_JIT \u0026\u0026 BPF_JIT\n"
}

13/2 2026/09/06 10:05 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 10772
							output: 35
							thoughts: 129
						

Thoughts:
**Analyzing the BPF Verifier Modifications**

Okay, let's break this down. First, I need to pinpoint exactly *what* has changed within the BPF verifier. It looks like the focus is on `kernel/bpf/verifier.c`. My initial scan highlights a set of modified functions that are central to the verification process. I see `check_func_call`, which is key to how function calls are handled, and a static inline function called `in_sleepable_context`, likely related to context checks. Then there's the core logic: `do_check_common`, `do_check_subprogs`, and `do_check_main`. These seem to be the workhorses of the verifier, each handling a specific aspect. And of course, the entry point, `bpf_check`, which orchestrates everything by calling both `do_check_main` and `do_check_subprogs`.

My next step will be to thoroughly review `bpf_check` itself. I need to understand its control flow and how it interacts with the other functions in the call hierarchy. I'll need to trace the paths of execution through the different `do_check_*` functions, carefully examining their roles and data dependencies. This will give me a clear picture of the modified verifier's behavior and potential impact.



14/2 2026/09/06 10:05 tool grepper
0m Args:
{
  "Expression": "do_check_subprogs",
  "PathPrefix": "kernel/bpf/verifier.c"
}

Results:
{
  "Output": "kernel/bpf/verifier.c=19568=static int do_check_common(struct bpf_verifier_env *env, int subprog, bool in_sleepable)\n--\nkernel/bpf/verifier.c-19754- */\nkernel/bpf/verifier.c:19755:static int do_check_subprogs(struct bpf_verifier_env *env)\nkernel/bpf/verifier.c-19756-{\n--\nkernel/bpf/verifier.c=21306=int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\n--\nkernel/bpf/verifier.c-21497-\tret = do_check_main(env);\nkernel/bpf/verifier.c:21498:\tret = ret ?: do_check_subprogs(env);\nkernel/bpf/verifier.c-21499-\n"
}

15/2 2026/09/06 10:05 llm patch-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 11472
							output: 152
							thoughts: 634
						

Thoughts:
**My Assessment of the BPF Verifier Patch**

Okay, let's break this down. First, I wanted to get a handle on the key functions modified, so I traced the calls to `bpf_check`. It looks like it's primarily called by `bpf_prog_load` within `kernel/bpf/syscall.c`. I then expanded my focus to include the related functions that were also modified: `check_func_call`, `do_check_common`, `do_check_subprogs`, and `do_check_main`.

Are these good targets for focused testing? Definitely. `bpf_check` is the main entry point for the BPF verifier. `do_check_subprogs` is particularly interesting, given the significant modifications related to iterating over sleepable versus non-sleepable contexts and the topological order of subprograms. `do_check_common` verifies the subprograms and the main program under a given sleepable context, while `check_func_call` handles the pseudo-calls and marks the call context. And `do_check_main` is, naturally, the main program verifier.

It's clear that these are *not* ubiquitous hotpaths, which is good. BPF verification is only triggered by the `bpf(BPF_PROG_LOAD)` syscall. This narrows the scope and makes these functions even better targets for focussed analysis.

Now, looking at the modified files, we have `include/linux/bpf.h`, `kernel/bpf/verifier.c`, and a self-test in `tools/testing/selftests/bpf/progs/verifier_async_cb_context.c`. The config we need to enable is `BPF_SYSCALL`.

Considering this, the analysis says `WorthFuzzing = true`. The justification is solid: the patch fundamentally alters the BPF verifier in `kernel/bpf/verifier.c` to handle global subprogram verification differently based on sleepable and non-sleepable contexts. This involves changes to loop orders, topological ordering, and context tracking. Because this touches core verification logic and is accessible through the `bpf(BPF_PROG_LOAD)` system call, there's a strong potential for state validation problems, infinite loops, or even verifier crashes. This is exactly the kind of change we want to target.

So, I'm going to set these results.



16/2 2026/09/06 10:05 tool set-results
0m Args:
{
  "EnableConfigs": [
    "BPF_SYSCALL"
  ],
  "FocusSymbols": [
    "bpf_check",
    "do_check_subprogs",
    "do_check_common",
    "check_func_call",
    "do_check_main"
  ],
  "Reasoning": "The patch modifies the BPF verifier to track and verify global subprograms separately across sleepable and non-sleepable execution contexts, modifying subprogram verification loops, topological traversal order, and context state handling in kernel/bpf/verifier.c. This is reachable via the bpf(BPF_PROG_LOAD) syscall and warrants fuzzing.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL"
  ],
  "FocusSymbols": [
    "bpf_check",
    "do_check_subprogs",
    "do_check_common",
    "check_func_call",
    "do_check_main"
  ],
  "Reasoning": "The patch modifies the BPF verifier to track and verify global subprograms separately across sleepable and non-sleepable execution contexts, modifying subprogram verification loops, topological traversal order, and context state handling in kernel/bpf/verifier.c. This is reachable via the bpf(BPF_PROG_LOAD) syscall and warrants fuzzing.",
  "WorthFuzzing": true
}

17/1 2026/09/06 10:05 action If
0m Args:
{
  "WorthFuzzing": true
}

Results:
null

18/2 2026/09/06 10:05 agent kmsan-evaluator
0m Model:
core

Args:
null

Results:
{
  "KMSANReasoning": "The patch modifies the BPF verifier to track and verify global subprogram calls separately for sleepable and non-sleepable/RCU contexts. It replaces the single-bit `called` and `verified` flags in `struct bpf_func_info_aux` with two-element boolean arrays indexed by `in_sleepable`.\n\n- KMSAN applicability: There is no uninitialized memory risk. The `bpf_func_info_aux` structures are zero-initialized upon allocation (via `kzalloc_objs`), and all local variables introduced or modified in `do_check_subprogs()`, `do_check_common()`, and `check_func_call()` are properly initialized. The arrays are indexed using boolean values (0 or 1), matching the array dimension. No uninitialized memory or structure padding is exposed or copied to user space.\n- KASAN / Standard Detectors: Any memory access issues (such as array out-of-bounds access, invalid pointer dereferences, state allocation issues, or logic/verifier traversal bugs) would be covered by KASAN and standard kernel debugging facilities.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 56efa603a0d522e6eec63230d19a6e260dfee3d8
Author: syz-cluster <triage@syzkaller.com>
Date:   Sun Sep 6 10:05:12 2026 +0000

    syz-cluster: applied patch under review

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 3a7eb2185c354..66d04244c7378 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1650,8 +1650,9 @@ static inline void bpf_trampoline_set_flags(struct bpf_trampoline *tr, u32 flags
 struct bpf_func_info_aux {
 	u16 linkage;
 	bool unreliable;
-	bool called : 1;
-	bool verified : 1;
+	/* Indexed by in_sleepable. */
+	bool called[2];
+	bool verified[2];
 };
 
 enum bpf_jit_poke_reason {
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 5b51e7ee1a3f9..f759a020c8a5a 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9957,6 +9957,7 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 	if (err == -EFAULT)
 		return err;
 	if (bpf_subprog_is_global(env, subprog)) {
+		struct bpf_func_info_aux *sub_aux = subprog_aux(env, subprog);
 		const char *sub_name = bpf_subprog_name(env, subprog);
 		const char *operation;
 		bool returns_void;
@@ -9988,11 +9989,10 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 		if (env->log.level & BPF_LOG_LEVEL)
 			verbose(env, "Func#%d ('%s') is global and assumed valid.\n",
 				subprog, sub_name);
+		sub_aux->called[in_sleepable_context(env)] = true;
 		returns_void = subprog_returns_void(env, subprog);
 		if (env->subprog_info[subprog].changes_pkt_data)
 			clear_all_pkt_pointers(env);
-		/* mark global subprog for verifying after main prog */
-		subprog_aux(env, subprog)->called = true;
 		if (returns_void)
 			bpf_diag_record_scrub(env, &caller->regs[BPF_REG_0], BPF_DIAG_MOD_CALLER_SAVED);
 		else
@@ -10804,7 +10804,12 @@ int bpf_get_helper_proto(struct bpf_verifier_env *env, int func_id,
 	return *ptr && (*ptr)->func ? 0 : -EINVAL;
 }
 
-/* Check if we're in a sleepable context. */
+/*
+ * This predicate is the inverse of in_rcu_cs(): non-sleepable programs and
+ * every condition that prevents sleeping also provide RCU protection. Global
+ * subprog verification relies on this equivalence to represent the caller's
+ * execution context using only the in_sleepable bit.
+ */
 static inline bool in_sleepable_context(struct bpf_verifier_env *env)
 {
 	return !env->cur_state->active_rcu_locks &&
@@ -19560,13 +19565,14 @@ static void free_states(struct bpf_verifier_env *env)
 	}
 }
 
-static int do_check_common(struct bpf_verifier_env *env, int subprog)
+static int do_check_common(struct bpf_verifier_env *env, int subprog, bool in_sleepable)
 {
 	bool pop_log = !(env->log.level & BPF_LOG_LEVEL2);
 	struct bpf_subprog_info *sub = subprog_info(env, subprog);
 	struct bpf_prog_aux *aux = env->prog->aux;
 	struct bpf_verifier_state *state;
 	struct bpf_reg_state *regs;
+	u32 old_insns_total = sub->insns_total;
 	u32 insn_processed = env->insn_processed;
 	int ret, i;
 
@@ -19579,7 +19585,7 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
 	state->curframe = 0;
 	state->speculative = false;
 	state->branches = 1;
-	state->in_sleepable = env->prog->sleepable;
+	state->in_sleepable = in_sleepable;
 	state->frame[0] = kzalloc_obj(struct bpf_func_state, GFP_KERNEL_ACCOUNT);
 	if (!state->frame[0]) {
 		kfree(state);
@@ -19721,7 +19727,8 @@ static int do_check_common(struct bpf_verifier_env *env, int subprog)
 	 * Accumulate their total counts as total counts of the main or
 	 * global subprog hosting the async call.
 	 */
-	env->subprog_info[subprog].insns_total = env->insn_processed - insn_processed;
+	env->subprog_info[subprog].insns_total = old_insns_total +
+						(env->insn_processed - insn_processed);
 	return ret;
 }
 
@@ -19749,45 +19756,55 @@ static int do_check_subprogs(struct bpf_verifier_env *env)
 {
 	struct bpf_prog_aux *aux = env->prog->aux;
 	struct bpf_func_info_aux *sub_aux;
-	int i, ret, new_cnt;
+	int context, i, j, ret, new_cnt;
 
 	if (!aux->func_info)
 		return 0;
 
 	/* exception callback is presumed to be always called */
-	if (env->exception_callback_subprog)
-		subprog_aux(env, env->exception_callback_subprog)->called = true;
+	if (env->exception_callback_subprog) {
+		sub_aux = subprog_aux(env, env->exception_callback_subprog);
+		sub_aux->called[env->prog->sleepable] = true;
+	}
 
 again:
 	new_cnt = 0;
-	for (i = 1; i < env->subprog_cnt; i++) {
+	/*
+	 * Walk callers before callees so each global subprog normally sees all
+	 * of its contexts before it is verified. Async callback cycles can add a
+	 * context to an earlier subprog, so repeat until every called context is
+	 * verified.
+	 */
+	for (j = env->subprog_cnt - 1; j >= 0; j--) {
+		i = env->subprog_topo_order[j];
+		if (!i)
+			continue;
 		if (!bpf_subprog_is_global(env, i))
 			continue;
 
 		sub_aux = subprog_aux(env, i);
-		if (!sub_aux->called || sub_aux->verified)
-			continue;
+		for (context = 0; context < ARRAY_SIZE(sub_aux->called); context++) {
+			if (!sub_aux->called[context] || sub_aux->verified[context])
+				continue;
 
-		env->insn_idx = env->subprog_info[i].start;
-		WARN_ON_ONCE(env->insn_idx == 0);
-		ret = do_check_common(env, i);
-		if (ret) {
-			return ret;
-		} else if (env->log.level & BPF_LOG_LEVEL) {
-			verbose(env, "Func#%d ('%s') is safe for any args that match its prototype\n",
-				i, bpf_subprog_name(env, i));
-		}
+			env->insn_idx = env->subprog_info[i].start;
+			WARN_ON_ONCE(env->insn_idx == 0);
+			ret = do_check_common(env, i, context);
+			if (ret)
+				return ret;
+			if (env->log.level & BPF_LOG_LEVEL)
+				verbose(env, "Func#%d ('%s') is safe for any args "
+					"that match its prototype\n",
+					i, bpf_subprog_name(env, i));
 
-		/* We verified new global subprog, it might have called some
-		 * more global subprogs that we haven't verified yet, so we
-		 * need to do another pass over subprogs to verify those.
-		 */
-		sub_aux->verified = true;
-		new_cnt++;
+			sub_aux->verified[context] = true;
+			new_cnt++;
+		}
 	}
 
-	/* We can't loop forever as we verify at least one global subprog on
-	 * each pass.
+	/*
+	 * We can't loop forever as each pass verifies at least one new context,
+	 * and there are only two contexts per global subprog.
 	 */
 	if (new_cnt)
 		goto again;
@@ -19800,7 +19817,7 @@ static int do_check_main(struct bpf_verifier_env *env)
 	int ret;
 
 	env->insn_idx = 0;
-	ret = do_check_common(env, 0);
+	ret = do_check_common(env, 0, env->prog->sleepable);
 	if (!ret)
 		env->prog->aux->stack_depth = env->subprog_info[0].stack_depth;
 	return ret;
diff --git a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
index 6bf95550a0248..987ca9dec6b7f 100644
--- a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
+++ b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
@@ -9,6 +9,11 @@
 
 char _license[] SEC("license") = "GPL";
 
+struct task_struct *bpf_task_acquire(struct task_struct *p) __ksym;
+void bpf_task_release(struct task_struct *p) __ksym;
+void bpf_rcu_read_lock(void) __ksym;
+void bpf_rcu_read_unlock(void) __ksym;
+
 /* Timer tests */
 
 struct timer_elem {
@@ -66,6 +71,7 @@ int timer_sleepable_prog(void *ctx)
 
 struct wq_elem {
 	struct bpf_wq w;
+	struct task_struct __kptr *task;
 };
 
 struct {
@@ -119,6 +125,106 @@ int wq_sleepable_prog(void *ctx)
 	return 0;
 }
 
+__noinline int wq_global_acquire(void)
+{
+	struct task_struct *task, *acquired;
+	struct wq_elem *val;
+	int key = 0;
+
+	val = bpf_map_lookup_elem(&wq_map, &key);
+	if (!val)
+		return 0;
+
+	task = val->task;
+	if (!task)
+		return 0;
+
+	acquired = bpf_task_acquire(task);
+	if (acquired)
+		bpf_task_release(acquired);
+	return 0;
+}
+
+static int wq_global_rcu_cb(void *map, int *key, void *value)
+{
+	wq_global_acquire();
+	return 0;
+}
+
+SEC("fentry/bpf_fentry_test1")
+__failure __msg("R1 must be a rcu pointer")
+int wq_global_rcu_prog(void *ctx)
+{
+	struct wq_elem *val;
+	int key = 0;
+
+	val = bpf_map_lookup_elem(&wq_map, &key);
+	if (!val)
+		return 0;
+
+	bpf_wq_init(&val->w, &wq_map, 0);
+	bpf_wq_set_callback(&val->w, wq_global_rcu_cb, 0);
+	return 0;
+}
+
+static int wq_global_rcu_lock_cb(void *map, int *key, void *value)
+{
+	bpf_rcu_read_lock();
+	wq_global_acquire();
+	bpf_rcu_read_unlock();
+	return 0;
+}
+
+SEC("fentry/bpf_fentry_test1")
+__success
+int wq_global_rcu_lock_prog(void *ctx)
+{
+	struct wq_elem *val;
+	int key = 0;
+
+	/* Verify the same global subprog in non-sleepable and protected contexts. */
+	wq_global_acquire();
+
+	val = bpf_map_lookup_elem(&wq_map, &key);
+	if (!val)
+		return 0;
+
+	bpf_wq_init(&val->w, &wq_map, 0);
+	bpf_wq_set_callback(&val->w, wq_global_rcu_lock_cb, 0);
+	return 0;
+}
+
+__noinline int wq_global_no_rcu(void)
+{
+	barrier();
+	return 0;
+}
+
+static int wq_global_no_rcu_cb(void *map, int *key, void *value)
+{
+	wq_global_no_rcu();
+	return 0;
+}
+
+SEC("fentry/bpf_fentry_test1")
+__success
+int wq_global_no_rcu_prog(void *ctx)
+{
+	struct wq_elem *val;
+	int key = 0;
+
+	/* Verify the same global in non-sleepable and unprotected contexts. */
+	wq_global_no_rcu();
+
+	val = bpf_map_lookup_elem(&wq_map, &key);
+	if (!val)
+		return 0;
+
+	bpf_wq_init(&val->w, &wq_map, 0);
+	bpf_wq_set_callback(&val->w, wq_global_no_rcu_cb, 0);
+	return 0;
+}
+
 /* Task work tests */
 
 struct task_work_elem {
@@ -179,3 +285,29 @@ int task_work_sleepable_prog(void *ctx)
 	bpf_task_work_schedule_resume(task, &val->tw, &task_work_map, task_work_cb);
 	return 0;
 }
+
+static int task_work_global_rcu_cb(struct bpf_map *map, void *key, void *value)
+{
+	return wq_global_acquire();
+}
+
+SEC("fentry/bpf_fentry_test1")
+__failure __msg("R1 must be a rcu pointer")
+int task_work_global_rcu_prog(void *ctx)
+{
+	struct task_work_elem *val;
+	struct task_struct *task;
+	int key = 0;
+
+	val = bpf_map_lookup_elem(&task_work_map, &key);
+	if (!val)
+		return 0;
+
+	task = bpf_get_current_task_btf();
+	if (!task)
+		return 0;
+
+	bpf_task_work_schedule_resume(task, &val->tw, &task_work_map,
+				      task_work_global_rcu_cb);
+	return 0;
+}

19/3 2026/09/06 10:05 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 6100
							output: 19
							thoughts: 442
						

Thoughts:
**Evaluating the Need for a Dedicated KMSAN Fuzzing Session**

Okay, let's break this down. I need to figure out if this patch necessitates spinning up a separate KMSAN fuzzing session, beyond what we're already doing with KASAN. My initial thought is to go through the changes methodically and then make the call.

First, I need to understand what this patch actually *does*. I'll examine the code changes, specifically focusing on how they relate to potential memory safety issues that KMSAN might catch better than KASAN.

Alright, the patch touches `include/linux/bpf.h` and `kernel/bpf/verifier.c`, and includes a self-test. Let’s start with `include/linux/bpf.h`. They've modified `struct bpf_func_info_aux`. The important thing here is the change from single `called` and `verified` bools to arrays: `called[2]` and `verified[2]`. Indexed by `in_sleepable_context`. This immediately suggests a context-sensitive change, likely dealing with how BPF functions behave in sleepable versus non-sleepable contexts.

Moving on to `kernel/bpf/verifier.c`.  I see the `check_func_call()` function is updated to set `sub_aux->called[in_sleepable_context(env)] = true;`.  The verifier is now tracking calls based on whether they occur in a sleepable context.  Also, `do_check_common` now explicitly passes `in_sleepable` to `state->in_sleepable`.  Finally, `do_check_subprogs()` iterates and sets `sub_aux->verified[context] = true;`. `do_check_main()` looks like the entry point for the verifier, so I'll check if the change is limited to a single context (e.g. `main` only).

This all points to changes in how the BPF verifier handles sleepable and non-sleepable contexts. This is my intuition. It's likely that they're trying to improve verification of BPF programs that might interact with these sleepable contexts.

Now, I need to know more about the allocation and initialization of `struct bpf_func_info_aux`.  I'll use `grepper` (or similar tools) to find where this struct is allocated and initialized. This is crucial for understanding the scope of the change and if there are new potential paths of memory corruption. Based on where it's allocated and how it's used, I can get a better sense if this is a KMSAN trigger or not.

From this, I'll assess whether a separate KMSAN fuzzing session is likely to expose new vulnerabilities that are missed by the standard KASAN fuzzing. If the allocation/use of this struct is in a critical section, or uses locking that is not KASAN-verified, then I will strongly consider KMSAN fuzzing.



20/3 2026/09/06 10:05 tool grepper
0m Args:
{
  "Expression": "func_info_aux"
}

Results:
{
  "Output": "Documentation/bpf/drgn.rst=120=It produces the output::\n--\nDocumentation/bpf/drgn.rst-182-            .func_info = (struct bpf_func_info *)0xffff889ff795d240,\nDocumentation/bpf/drgn.rst:183:            .func_info_aux = (struct bpf_func_info_aux *)0xffff889ff795de20,\nDocumentation/bpf/drgn.rst-184-            .linfo = (struct bpf_line_info *)0xffff888a707afc00,\n--\ninclude/linux/bpf.h=1647=static inline void bpf_trampoline_set_flags(struct bpf_trampoline *tr, u32 flags) {}\n--\ninclude/linux/bpf.h-1649-\ninclude/linux/bpf.h:1650:struct bpf_func_info_aux {\ninclude/linux/bpf.h-1651-\tu16 linkage;\n--\ninclude/linux/bpf.h=1742=struct bpf_prog_aux {\n--\ninclude/linux/bpf.h-1828-\tstruct bpf_func_info *func_info;\ninclude/linux/bpf.h:1829:\tstruct bpf_func_info_aux *func_info_aux;\ninclude/linux/bpf.h-1830-\t/* bpf_line_info loaded from userspace.  linfo-\u003einsn_off\n--\ninclude/linux/bpf_verifier.h=919=struct bpf_verifier_env {\n--\ninclude/linux/bpf_verifier.h-1050-\ninclude/linux/bpf_verifier.h:1051:static inline struct bpf_func_info_aux *subprog_aux(struct bpf_verifier_env *env, int subprog)\ninclude/linux/bpf_verifier.h-1052-{\ninclude/linux/bpf_verifier.h:1053:\treturn \u0026env-\u003eprog-\u003eaux-\u003efunc_info_aux[subprog];\ninclude/linux/bpf_verifier.h-1054-}\n--\nkernel/bpf/btf.c=8007=int btf_prepare_func_args(struct bpf_verifier_env *env, int subprog)\n--\nkernel/bpf/btf.c-8047-\nkernel/bpf/btf.c:8048:\tif (prog-\u003eaux-\u003efunc_info_aux[subprog].unreliable) {\nkernel/bpf/btf.c-8049-\t\tverifier_bug(env, \"unreliable BTF for function %s()\", tname);\n--\nkernel/bpf/check_btf.c=133=static int check_btf_func(struct bpf_verifier_env *env,\n--\nkernel/bpf/check_btf.c-139-\tstruct bpf_func_info *krecord;\nkernel/bpf/check_btf.c:140:\tstruct bpf_func_info_aux *info_aux = NULL;\nkernel/bpf/check_btf.c-141-\tstruct bpf_prog *prog;\n--\nkernel/bpf/check_btf.c-201-\nkernel/bpf/check_btf.c:202:\tprog-\u003eaux-\u003efunc_info_aux = info_aux;\nkernel/bpf/check_btf.c-203-\treturn 0;\n--\nkernel/bpf/fixups.c=401=static int adjust_subprog_starts_after_remove(struct bpf_verifier_env *env,\n--\nkernel/bpf/fixups.c-438-\t\t\t\tsizeof(*aux-\u003efunc_info) * move);\nkernel/bpf/fixups.c:439:\t\t\tif (aux-\u003efunc_info_aux)\nkernel/bpf/fixups.c:440:\t\t\t\tmemmove(aux-\u003efunc_info_aux + i,\nkernel/bpf/fixups.c:441:\t\t\t\t\taux-\u003efunc_info_aux + j,\nkernel/bpf/fixups.c:442:\t\t\t\t\tsizeof(*aux-\u003efunc_info_aux) * move);\nkernel/bpf/fixups.c-443-\t\t\taux-\u003efunc_info_cnt -= j - i;\n--\nkernel/bpf/syscall.c=2440=static void __bpf_prog_put_rcu(struct rcu_head *rcu)\n--\nkernel/bpf/syscall.c-2444-\tkvfree(aux-\u003efunc_info);\nkernel/bpf/syscall.c:2445:\tkfree(aux-\u003efunc_info_aux);\nkernel/bpf/syscall.c-2446-\tfree_uid(aux-\u003euser);\n--\nkernel/bpf/verifier.c=382=bool bpf_subprog_is_global(const struct bpf_verifier_env *env, int subprog)\nkernel/bpf/verifier.c-383-{\nkernel/bpf/verifier.c:384:\tstruct bpf_func_info_aux *aux = env-\u003eprog-\u003eaux-\u003efunc_info_aux;\nkernel/bpf/verifier.c-385-\n--\nkernel/bpf/verifier.c=9839=static int btf_check_subprog_call(struct bpf_verifier_env *env, int subprog,\n--\nkernel/bpf/verifier.c-9853-\nkernel/bpf/verifier.c:9854:\tif (prog-\u003eaux-\u003efunc_info_aux[subprog].unreliable)\nkernel/bpf/verifier.c-9855-\t\treturn -EINVAL;\n--\nkernel/bpf/verifier.c-9862-\tif (err)\nkernel/bpf/verifier.c:9863:\t\tprog-\u003eaux-\u003efunc_info_aux[subprog].unreliable = true;\nkernel/bpf/verifier.c-9864-\treturn err;\n--\nkernel/bpf/verifier.c=9939=static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n--\nkernel/bpf/verifier.c-9959-\tif (bpf_subprog_is_global(env, subprog)) {\nkernel/bpf/verifier.c:9960:\t\tstruct bpf_func_info_aux *sub_aux = subprog_aux(env, subprog);\nkernel/bpf/verifier.c-9961-\t\tconst char *sub_name = bpf_subprog_name(env, subprog);\n--\nkernel/bpf/verifier.c=17303=static int check_ld_imm(struct bpf_verifier_env *env, struct bpf_insn *insn)\n--\nkernel/bpf/verifier.c-17363-\t\t}\nkernel/bpf/verifier.c:17364:\t\tif (aux-\u003efunc_info_aux[subprogno].linkage != BTF_FUNC_STATIC) {\nkernel/bpf/verifier.c-17365-\t\t\tverbose(env, \"callback function not static\\n\");\n--\nkernel/bpf/verifier.c=19568=static int do_check_common(struct bpf_verifier_env *env, int subprog, bool in_sleepable)\n--\nkernel/bpf/verifier.c-19687-\t\t */\nkernel/bpf/verifier.c:19688:\t\tif (env-\u003eprog-\u003eaux-\u003efunc_info_aux) {\nkernel/bpf/verifier.c-19689-\t\t\tret = btf_prepare_func_args(env, 0);\nkernel/bpf/verifier.c-19690-\t\t\tif (ret || sub-\u003earg_cnt != 1 || sub-\u003eargs[0].arg_type != ARG_PTR_TO_CTX) {\nkernel/bpf/verifier.c:19691:\t\t\t\tenv-\u003eprog-\u003eaux-\u003efunc_info_aux[0].unreliable = true;\nkernel/bpf/verifier.c-19692-\t\t\t\tsub-\u003earg_cnt = 1;\n--\nkernel/bpf/verifier.c=19755=static int do_check_subprogs(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-19757-\tstruct bpf_prog_aux *aux = env-\u003eprog-\u003eaux;\nkernel/bpf/verifier.c:19758:\tstruct bpf_func_info_aux *sub_aux;\nkernel/bpf/verifier.c-19759-\tint context, i, j, ret, new_cnt;\n--\nkernel/bpf/verifier.c=20193=int bpf_check_attach_target(struct bpf_verifier_log *log,\n--\nkernel/bpf/verifier.c-20267-\t\t}\nkernel/bpf/verifier.c:20268:\t\tconservative = aux-\u003efunc_info_aux[subprog].unreliable;\nkernel/bpf/verifier.c-20269-\t\tif (prog_extension) {\n"
}

21/3 2026/09/06 10:05 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 9505
							output: 39
							thoughts: 34
						

Thoughts:
Here's a summary of my thought process, as requested:

**Diving into BTF Verification**

Okay, so I need to understand how this `info_aux` structure is being allocated in the BTF (Berkeley Packet Filter Type Format) checking process. My immediate thought is to zero in on the relevant code. Given the context, the allocation logic is most likely within the verification function, so I'll check `kernel/bpf/check_btf.c`. Line 133 is a good starting point, as that sounds like a likely spot to find the specific memory management details. Let's see what's happening around there. This should help me pinpoint any potential issues or areas for optimization in the BTF validation process.



22/3 2026/09/06 10:05 tool read-file
0m Args:
{
  "File": "kernel/bpf/check_btf.c",
  "FirstLine": 140,
  "LineCount": 65
}

Results:
{
  "Contents": " 140:\t\tstruct bpf_func_info_aux *info_aux = NULL;\n 141:\t\tstruct bpf_prog *prog;\n 142:\t\tconst struct btf *btf;\n 143:\t\tbpfptr_t urecord;\n 144:\t\tbool scalar_return;\n 145:\t\tint ret = -ENOMEM;\n 146:\t\n 147:\t\tnfuncs = attr-\u003efunc_info_cnt;\n 148:\t\tif (!nfuncs) {\n 149:\t\t\tif (check_abnormal_return(env))\n 150:\t\t\t\treturn -EINVAL;\n 151:\t\t\treturn 0;\n 152:\t\t}\n 153:\t\tif (nfuncs != env-\u003esubprog_cnt) {\n 154:\t\t\tverbose(env, \"number of funcs in func_info doesn't match number of subprogs\\n\");\n 155:\t\t\treturn -EINVAL;\n 156:\t\t}\n 157:\t\n 158:\t\turec_size = attr-\u003efunc_info_rec_size;\n 159:\t\n 160:\t\tprog = env-\u003eprog;\n 161:\t\tbtf = prog-\u003eaux-\u003ebtf;\n 162:\t\n 163:\t\turecord = make_bpfptr(attr-\u003efunc_info, uattr.is_kernel);\n 164:\t\n 165:\t\tkrecord = prog-\u003eaux-\u003efunc_info;\n 166:\t\tinfo_aux = kzalloc_objs(*info_aux, nfuncs,\n 167:\t\t\t\t\tGFP_KERNEL_ACCOUNT | __GFP_NOWARN);\n 168:\t\tif (!info_aux)\n 169:\t\t\treturn -ENOMEM;\n 170:\t\n 171:\t\tfor (i = 0; i \u003c nfuncs; i++) {\n 172:\t\t\t/* check insn_off */\n 173:\t\t\tret = -EINVAL;\n 174:\t\n 175:\t\t\tif (env-\u003esubprog_info[i].start != krecord[i].insn_off) {\n 176:\t\t\t\tverbose(env, \"func_info BTF section doesn't match subprog layout in BPF program\\n\");\n 177:\t\t\t\tgoto err_free;\n 178:\t\t\t}\n 179:\t\n 180:\t\t\t/* Already checked type_id */\n 181:\t\t\ttype = btf_type_by_id(btf, krecord[i].type_id);\n 182:\t\t\tinfo_aux[i].linkage = BTF_INFO_VLEN(type-\u003einfo);\n 183:\t\t\t/* Already checked func_proto */\n 184:\t\t\tfunc_proto = btf_type_by_id(btf, type-\u003etype);\n 185:\t\n 186:\t\t\tret_type = btf_type_skip_modifiers(btf, func_proto-\u003etype, NULL);\n 187:\t\t\tscalar_return =\n 188:\t\t\t\tbtf_type_is_small_int(ret_type) || btf_is_any_enum(ret_type);\n 189:\t\t\tif (i \u0026\u0026 !scalar_return \u0026\u0026 env-\u003esubprog_info[i].has_ld_abs) {\n 190:\t\t\t\tverbose(env, \"LD_ABS is only allowed in functions that return 'int'.\\n\");\n 191:\t\t\t\tgoto err_free;\n 192:\t\t\t}\n 193:\t\t\tif (i \u0026\u0026 !scalar_return \u0026\u0026 env-\u003esubprog_info[i].has_tail_call) {\n 194:\t\t\t\tverbose(env, \"tail_call is only allowed in functions that return 'int'.\\n\");\n 195:\t\t\t\tgoto err_free;\n 196:\t\t\t}\n 197:\t\n 198:\t\t\tenv-\u003esubprog_info[i].name = btf_name_by_offset(btf, type-\u003ename_off);\n 199:\t\t\tbpfptr_add(\u0026urecord, urec_size);\n 200:\t\t}\n 201:\t\n 202:\t\tprog-\u003eaux-\u003efunc_info_aux = info_aux;\n 203:\t\treturn 0;\n 204:\t\n"
}

23/3 2026/09/06 10:05 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 10826
							output: 29
							thoughts: 605
						

Thoughts:
**My Analysis of the BPF Verifier Patch**

Okay, so I'm digging into this BPF verifier patch. My initial thought is to understand the core change and potential implications. First, the `info_aux` structure, which seems to be central to this, is allocated using `kzalloc_objs`. This is good; it guarantees the memory is zeroed out from the start, which is a good baseline to avoid any uninitialized memory issues.

The core of the patch revolves around how the verifier tracks subprogram calls and verification status in both sleepable and non-sleepable contexts. Previously, we had these as single boolean flags: `called` and `verified`. Now, it's been changed to `bool called[2];` and `bool verified[2];`. This is indexed by `in_sleepable`, which is either 0 or 1, representing these two contexts. Makes sense; this looks like a straightforward way to differentiate the call/verification state between sleepable and non-sleepable contexts.

Looking at the code, in `check_func_call()`, the `called` flag is being set: `sub_aux->called[in_sleepable_context(env)] = true;`. `in_sleepable_context(env)` returns a bool - which I quickly verified can only return 0 or 1. So using this boolean as an array index is safe.

Then, in `do_check_subprogs()`, we iterate through subprograms in topological order, for both contexts (0 and 1). If a subprogram has been called in a particular context *but* not yet verified for that context, we call `do_check_common()`, and then set `verified[context] = true`.

In `do_check_main()`, `do_check_common()` is called with `env->prog->sleepable` as the context.

I see `do_check_common()` updating `state->in_sleepable`, and also the `insns_total`.

Now, the important part: are we introducing any uninitialized memory issues with this change? I've got to be sure. I've broken down my check:

1.  `info_aux` is `kzalloc_objs`, which means that all of `called[0]`, `called[1]`, `verified[0]`, and `verified[1]` are initialized to zero. That's a huge relief.
2.  I did a quick scan - No direct data copying to userspace (no `copy_to_user`, `put_user`, etc.) without initialization.
3.  No conditional branches depending on *uninitialized* data.
4.  No changes in buffer sizing and no uninitialized padding leaks. No uninitialized stack or heap reads.
5.  All variables used within the modified functions (`context`, `i`, `j`, `ret`, `new_cnt`, `old_insns_total`) are correctly initialized before usage.

So, *as far as I can see*, there are no obvious uninitialized memory issues here. The patch seems solid on that front.

I'll do a quick `grep` for `called` and `verified` within `kernel/bpf/` to make doubly sure I'm not missing anything. I need to be thorough. It's too important to be complacent. I'll need to check the search results to be absolutely certain there are no other places these variables are used, and that they're being used safely and correctly.



24/3 2026/09/06 10:05 tool grepper
0m Args:
{
  "Expression": "(called|verified)",
  "PathPrefix": "kernel/bpf"
}

Results:
{
  "Output": "\nFull output is too long, showing 500 out of 639 lines.\nUse more precise expression if possible.\n\nkernel/bpf/arraymap.c=932=static void *fd_array_map_lookup_elem(struct bpf_map *map, void *key)\n--\nkernel/bpf/arraymap.c-936-\nkernel/bpf/arraymap.c:937:/* only called from syscall */\nkernel/bpf/arraymap.c-938-int bpf_fd_array_map_lookup_elem(struct bpf_map *map, void *key, u32 *value)\n--\nkernel/bpf/arraymap.c-956-\nkernel/bpf/arraymap.c:957:/* only called from syscall */\nkernel/bpf/arraymap.c-958-int bpf_fd_array_map_update_elem(struct bpf_map *map, struct file *map_file,\n--\nkernel/bpf/backtrack.c=237=void bpf_fmt_stack_mask(char *buf, ssize_t buf_sz, u64 stack_mask)\n--\nkernel/bpf/backtrack.c-255-\nkernel/bpf/backtrack.c:256:/* For given verifier state backtrack_insn() is called from the last insn to\nkernel/bpf/backtrack.c-257- * the first insn. Its purpose is to compute a bitmask of registers and\n--\nkernel/bpf/backtrack.c=669=void bpf_mark_all_scalars_precise(struct bpf_verifier_env *env,\n--\nkernel/bpf/backtrack.c-728- * One important and subtle aspect is that precise marks *do not matter* in\nkernel/bpf/backtrack.c:729: * the currently verified state (current state). It is important to understand\nkernel/bpf/backtrack.c-730- * why this is the case.\n--\nkernel/bpf/bpf_inode_storage.c=125=BPF_CALL_4(bpf_inode_storage_get, struct bpf_map *, map, struct inode *, inode,\n--\nkernel/bpf/bpf_inode_storage.c-145-\nkernel/bpf/bpf_inode_storage.c:146:\t/* This helper must only called from where the inode is guaranteed\nkernel/bpf/bpf_inode_storage.c-147-\t * to have a refcount and cannot be freed.\n--\nkernel/bpf/bpf_inode_storage.c=160=BPF_CALL_2(bpf_inode_storage_delete,\n--\nkernel/bpf/bpf_inode_storage.c-166-\nkernel/bpf/bpf_inode_storage.c:167:\t/* This helper must only called from where the inode is guaranteed\nkernel/bpf/bpf_inode_storage.c-168-\t * to have a refcount and cannot be freed.\n--\nkernel/bpf/bpf_insn_array.c=251=void bpf_insn_array_adjust_after_remove(struct bpf_map *map, u32 off, u32 len)\n--\nkernel/bpf/bpf_insn_array.c-268-/*\nkernel/bpf/bpf_insn_array.c:269: * This function is called by JITs. The image is the real program\nkernel/bpf/bpf_insn_array.c-270- * image, the offsets array set up the xlated -\u003e jitted mapping.\n--\nkernel/bpf/bpf_iter.c=93=static ssize_t bpf_seq_read(struct file *file, char __user *buf, size_t size,\n--\nkernel/bpf/bpf_iter.c-208-\t}\nkernel/bpf/bpf_iter.c:209:\t/* bpf program called if !p */\nkernel/bpf/bpf_iter.c-210-\tseq-\u003eop-\u003estop(seq, p);\n--\nkernel/bpf/bpf_iter.c=727=BPF_CALL_4(bpf_loop, u32, nr_loops, void *, callback_fn, void *, callback_ctx,\n--\nkernel/bpf/bpf_iter.c-733-\nkernel/bpf/bpf_iter.c:734:\t/* Note: these safety checks are also verified when bpf_loop\nkernel/bpf/bpf_iter.c-735-\t * is inlined, be careful to modify this code in sync. See\n--\nkernel/bpf/bpf_local_storage.c=300=int bpf_selem_unlink(struct bpf_local_storage_elem *selem)\n--\nkernel/bpf/bpf_local_storage.c-346- * Unlink an selem from map and local storage with lockless fallback if callers\nkernel/bpf/bpf_local_storage.c:347: * are racing or rqspinlock returns error. It should only be called by\nkernel/bpf/bpf_local_storage.c-348- * bpf_local_storage_destroy() or bpf_local_storage_map_free().\n--\nkernel/bpf/bpf_lsm.c=228=bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n--\nkernel/bpf/bpf_lsm.c-287-\nkernel/bpf/bpf_lsm.c:288:/* The set of hooks which are called without pagefaults disabled and are allowed\nkernel/bpf/bpf_lsm.c-289- * to \"sleep\" and thus can be used for sleepable BPF programs.\n--\nkernel/bpf/bpf_struct_ops.c=726=static long bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,\n--\nkernel/bpf/bpf_struct_ops.c-941-\t/* Error during st_ops-\u003ereg(). Can happen if this struct_ops needs to be\nkernel/bpf/bpf_struct_ops.c:942:\t * verified as a whole, after all init_member() calls. Can also happen if\nkernel/bpf/bpf_struct_ops.c-943-\t * there was a race in registering the struct_ops (under the same name) to\n--\nkernel/bpf/bpf_struct_ops.c=1027=static void bpf_struct_ops_map_free(struct bpf_map *map)\n--\nkernel/bpf/bpf_struct_ops.c-1046-\t * setsockopt(TCP_CONGESTION, \"tcp_cc_y\").\nkernel/bpf/bpf_struct_ops.c:1047:\t * During the switch,  bpf_struct_ops_put(tcp_cc_x) is called\nkernel/bpf/bpf_struct_ops.c-1048-\t * and its refcount may reach 0 which then free its\n--\nkernel/bpf/bpf_struct_ops.c=1522=void bpf_prog_disassoc_struct_ops(struct bpf_prog *prog)\n--\nkernel/bpf/bpf_struct_ops.c-1540- * Get a reference to the struct_ops struct (i.e., kdata) associated with a\nkernel/bpf/bpf_struct_ops.c:1541: * program. Should only be called in BPF program context (e.g., in a kfunc).\nkernel/bpf/bpf_struct_ops.c-1542- *\n--\nkernel/bpf/bpf_task_storage.c=193=BPF_CALL_2(bpf_task_storage_delete, struct bpf_map *, map, struct task_struct *,\n--\nkernel/bpf/bpf_task_storage.c-199-\nkernel/bpf/bpf_task_storage.c:200:\t/* This helper must only be called from places where the lifetime of the task\nkernel/bpf/bpf_task_storage.c-201-\t * is guaranteed. Either by being refcounted or by being protected\n--\nkernel/bpf/btf.c=1782=static void btf_free_id(struct btf *btf)\n--\nkernel/bpf/btf.c-1799-\t\t * Clear the id here to make this function idempotent, since it will get\nkernel/bpf/btf.c:1800:\t\t * called a couple of times for module BTFs: on module unload, and then\nkernel/bpf/btf.c-1801-\t\t * the final btf_put(). btf_alloc_id() starts IDs with 1, so we can use\n--\nkernel/bpf/btf.c=3975=static int btf_parse_kptr(const struct btf *btf, struct btf_field *field,\n--\nkernel/bpf/btf.c-3993-\tif (id == -ENOENT) {\nkernel/bpf/btf.c:3994:\t\t/* btf_parse_kptr should only be called w/ btf = program BTF */\nkernel/bpf/btf.c-3995-\t\tWARN_ON_ONCE(btf_is_kernel(btf));\n--\nkernel/bpf/btf.c-4040-\nkernel/bpf/btf.c:4041:\t\t/* We already verified dtor_func to be btf_type_is_func\nkernel/bpf/btf.c-4042-\t\t * in register_btf_id_dtor_kfuncs.\n--\nkernel/bpf/btf.c=7956=static int btf_validate_return_type(struct bpf_verifier_env *env, struct btf *btf,\n--\nkernel/bpf/btf.c-7982-\t\t * returns to the kernel, which has no arena to cast the address\nkernel/bpf/btf.c:7983:\t\t * back into. A local function is verified inline, so its caller\nkernel/bpf/btf.c-7984-\t\t * receives the real register state and any member is fine.\n--\nkernel/bpf/btf.c=8520=static int btf_module_notify(struct notifier_block *nb, unsigned long op,\n--\nkernel/bpf/btf.c-8614-\t\t\t * btf_try_get_module() on such BTFs will fail. This may\nkernel/bpf/btf.c:8615:\t\t\t * be called again on btf_put(), but it's ok to do so.\nkernel/bpf/btf.c-8616-\t\t\t */\n--\nkernel/bpf/btf.c=8919=static int btf_populate_kfunc_set(struct btf *btf, enum btf_kfunc_hook hook,\n--\nkernel/bpf/btf.c-8965-\tset = tab-\u003esets[hook];\nkernel/bpf/btf.c:8966:\t/* Warn when register_btf_kfunc_id_set is called twice for the same hook\nkernel/bpf/btf.c-8967-\t * for module sets.\n--\nkernel/bpf/cfg.c=68=static void mark_subprog_might_throw(struct bpf_verifier_env *env, int off)\n--\nkernel/bpf/cfg.c-77- * 'w' is a callee entry point.\nkernel/bpf/cfg.c:78: * Eventually this function would be called when env-\u003ecfg.insn_state[w] == EXPLORED.\nkernel/bpf/cfg.c-79- * Rely on DFS traversal order and absence of recursive calls to guarantee that\n--\nkernel/bpf/cfg.c=451=static int visit_insn(int t, struct bpf_verifier_env *env)\n--\nkernel/bpf/cfg.c-485-\t\t/* For functions that invoke callbacks it is not known how many times\nkernel/bpf/cfg.c:486:\t\t * callback would be called. Verifier models callback calling functions\nkernel/bpf/cfg.c-487-\t\t * by repeatedly visiting callback bodies and returning to origin call\n--\nkernel/bpf/cfg.c-503-\t\t\tret = bpf_get_helper_proto(env, insn-\u003eimm, \u0026fp);\nkernel/bpf/cfg.c:504:\t\t\t/* If called in a non-sleepable context program will be\nkernel/bpf/cfg.c-505-\t\t\t * rejected anyway, so we should end up with precise\n--\nkernel/bpf/cfg.c-536-\t\t\t}\nkernel/bpf/cfg.c:537:\t\t\t/* Same as helpers, if called in a non-sleepable context\nkernel/bpf/cfg.c-538-\t\t\t * program will be rejected anyway, so we should end up\n--\nkernel/bpf/cgroup.c=771=static int insert_pl_to_hlist(struct bpf_prog_list *pl, struct hlist_head *progs,\n--\nkernel/bpf/cgroup.c-803- * Exactly one of @prog or @link can be non-null.\nkernel/bpf/cgroup.c:804: * Must be called with cgroup_mutex held.\nkernel/bpf/cgroup.c-805- */\n--\nkernel/bpf/cgroup.c=1029=static bool cgroup_bpf_storages_compatible(struct bpf_prog *old_prog,\n--\nkernel/bpf/cgroup.c-1050- *\nkernel/bpf/cgroup.c:1051: * Must be called with cgroup_mutex held.\nkernel/bpf/cgroup.c-1052- */\n--\nkernel/bpf/cgroup.c=1156=static void purge_effective_progs(struct cgroup *cgrp, struct bpf_prog_list *pl,\n--\nkernel/bpf/cgroup.c-1194- * At most one of @prog or @link can be non-NULL.\nkernel/bpf/cgroup.c:1195: * Must be called with cgroup_mutex held.\nkernel/bpf/cgroup.c-1196- */\n--\nkernel/bpf/cgroup.c=1260=static int cgroup_bpf_detach(struct cgroup *cgrp, struct bpf_prog *prog,\n--\nkernel/bpf/cgroup.c-1270-\nkernel/bpf/cgroup.c:1271:/* Must be called with cgroup_mutex held to avoid races. */\nkernel/bpf/cgroup.c-1272-static int __cgroup_bpf_query(struct cgroup *cgrp, const union bpf_attr *attr,\n--\nkernel/bpf/cgroup.c=2234=int __cgroup_bpf_run_filter_getsockopt_kern(struct sock *sk, int level,\n--\nkernel/bpf/cgroup.c-2254-\t * Here we always pass the data and memset() should\nkernel/bpf/cgroup.c:2255:\t * be called if that data shouldn't be \"exported\".\nkernel/bpf/cgroup.c-2256-\t */\n--\nkernel/bpf/cgroup_iter.c-30- *\nkernel/bpf/cgroup_iter.c:31: * Note: the iter_prog is called with cgroup_mutex held.\nkernel/bpf/cgroup_iter.c-32- *\n--\nkernel/bpf/check_btf.c=31=static int prepare_btf_func(struct bpf_verifier_env *env,\n--\nkernel/bpf/check_btf.c-116-\t\tif (unlikely(!func_proto || !btf_type_is_func_proto(func_proto)))\nkernel/bpf/check_btf.c:117:\t\t\t/* btf_func_check() already verified it during BTF load */\nkernel/bpf/check_btf.c-118-\t\t\tgoto err_free;\n--\nkernel/bpf/core.c=1243=int bpf_jit_binary_pack_finalize(struct bpf_binary_header *ro_header,\n--\nkernel/bpf/core.c-1258-\nkernel/bpf/core.c:1259:/* bpf_jit_binary_pack_free is called in two different scenarios:\nkernel/bpf/core.c-1260- *   1) when the program is freed after;\n--\nkernel/bpf/core.c=2997=int bpf_prog_array_copy_info(struct bpf_prog_array *array,\n--\nkernel/bpf/core.c-3011-\nkernel/bpf/core.c:3012:\t/* this function is called under trace/bpf_trace.c: bpf_event_mutex */\nkernel/bpf/core.c-3013-\treturn bpf_prog_array_copy_core(array, prog_ids, request_cnt) ? -ENOSPC\n--\nkernel/bpf/core.c=3123=BPF_CALL_0(bpf_user_rnd_u32)\n--\nkernel/bpf/core.c-3126-\t * of the registers passed into this function, then note that\nkernel/bpf/core.c:3127:\t * this function is called from native eBPF and classic-to-eBPF\nkernel/bpf/core.c-3128-\t * transformations. Register assignments from both sides are\n--\nkernel/bpf/devmap.c=378=static void bq_xmit_all(struct xdp_dev_bulk_queue *bq, u32 flags)\n--\nkernel/bpf/devmap.c-422-\nkernel/bpf/devmap.c:423:/* __dev_flush is called from xdp_do_flush() which _must_ be signalled from the\nkernel/bpf/devmap.c-424- * driver before returning from its napi-\u003epoll() routine. See the comment above\n--\nkernel/bpf/devmap.c=949=static long __dev_map_update_elem(struct net *net, struct bpf_map *map,\n--\nkernel/bpf/devmap.c-963-\nkernel/bpf/devmap.c:964:\t/* already verified value_size \u003c= sizeof val */\nkernel/bpf/devmap.c-965-\tmemcpy(\u0026val, value, map-\u003evalue_size);\n--\nkernel/bpf/devmap.c=998=static long __dev_map_hash_update_elem(struct net *net, struct bpf_map *map,\n--\nkernel/bpf/devmap.c-1007-\nkernel/bpf/devmap.c:1008:\t/* already verified value_size \u003c= sizeof val */\nkernel/bpf/devmap.c-1009-\tmemcpy(\u0026val, value, map-\u003evalue_size);\n--\nkernel/bpf/dispatcher.c-13- * there is available room in the dispatcher a direct call to the BPF\nkernel/bpf/dispatcher.c:14: * program will be generated. All calls to the BPF programs called via\nkernel/bpf/dispatcher.c-15- * the dispatcher will then be a direct call, instead of an\n--\nkernel/bpf/dmabuf_iter.c=9=struct dmabuf_iter_priv {\n--\nkernel/bpf/dmabuf_iter.c-12-\t * prevent destruction between stop/start. If reading is not resumed and\nkernel/bpf/dmabuf_iter.c:13:\t * start is never called again, then dmabuf_iter_seq_fini drops the\nkernel/bpf/dmabuf_iter.c-14-\t * reference when the iterator is released.\n--\nkernel/bpf/hashtab.c=273=static void htab_free_elems(struct bpf_htab *htab)\n--\nkernel/bpf/hashtab.c-295- * bpf_lru_list.c logic. For example, certain code path of\nkernel/bpf/hashtab.c:296: * bpf_lru_pop_free(), which is called by function prealloc_lru_pop(),\nkernel/bpf/hashtab.c-297- * will acquire lru_lock first followed by acquiring bucket_lock.\n--\nkernel/bpf/hashtab.c=689=static inline struct hlist_nulls_head *select_bucket(struct bpf_htab *htab, u32 hash)\n--\nkernel/bpf/hashtab.c-693-\nkernel/bpf/hashtab.c:694:/* this lookup function can only be called with bucket lock taken */\nkernel/bpf/hashtab.c-695-static struct htab_elem *lookup_elem_raw(struct hlist_nulls_head *head, u32 hash,\n--\nkernel/bpf/hashtab.c-707-\nkernel/bpf/hashtab.c:708:/* can be called without bucket lock. it will repeat the loop in\nkernel/bpf/hashtab.c-709- * the unlikely event when elements moved from one bucket into another\n--\nkernel/bpf/hashtab.c=840=static void check_and_cancel_fields(struct bpf_htab *htab,\n--\nkernel/bpf/hashtab.c-858-\nkernel/bpf/hashtab.c:859:/* It is called from the bpf_lru_list when the LRU needs to delete\nkernel/bpf/hashtab.c-860- * older elements from the htab.\n--\nkernel/bpf/hashtab.c=1571=static void delete_all_elements(struct bpf_htab *htab)\n--\nkernel/bpf/hashtab.c-1574-\nkernel/bpf/hashtab.c:1575:\t/* It's called from a worker thread and migration has been disabled,\nkernel/bpf/hashtab.c-1576-\t * therefore, it is OK to invoke bpf_mem_cache_free() directly.\n--\nkernel/bpf/hashtab.c=1626=static void htab_map_free(struct bpf_map *map)\n--\nkernel/bpf/hashtab.c-1630-\t/* bpf_free_used_maps() or close(map_fd) will trigger this map_free callback.\nkernel/bpf/hashtab.c:1631:\t * bpf_free_used_maps() is called after bpf prog is no longer executing.\nkernel/bpf/hashtab.c-1632-\t * There is no need to synchronize_rcu() here to protect map elements.\n--\nkernel/bpf/hashtab.c=2610=static void fd_htab_map_free(struct bpf_map *map)\n--\nkernel/bpf/hashtab.c-2630-\nkernel/bpf/hashtab.c:2631:/* only called from syscall */\nkernel/bpf/hashtab.c-2632-int bpf_fd_htab_map_lookup_elem(struct bpf_map *map, void *key, u32 *value)\n--\nkernel/bpf/hashtab.c-2650-\nkernel/bpf/hashtab.c:2651:/* Only called from syscall */\nkernel/bpf/hashtab.c-2652-int bpf_fd_htab_map_update_elem(struct bpf_map *map, struct file *map_file,\n--\nkernel/bpf/helpers.c=797=void bpf_bprintf_cleanup(struct bpf_bprintf_data *data)\n--\nkernel/bpf/helpers.c-815- * In argument preparation mode, if 0 is returned, safe temporary buffers are\nkernel/bpf/helpers.c:816: * allocated and bpf_bprintf_cleanup should be called to free them after use.\nkernel/bpf/helpers.c-817- */\n--\nkernel/bpf/helpers.c=1317=static int __bpf_async_init(struct bpf_async_kern *async, struct bpf_map *map, u64 flags,\n--\nkernel/bpf/helpers.c-1377-\t * when there are concurrent uref release and bpf timer init, either\nkernel/bpf/helpers.c:1378:\t * bpf_timer_cancel_and_free() called by uref release reads a no-NULL\nkernel/bpf/helpers.c-1379-\t * timer or atomic64_read() below returns a zero usercnt.\n--\nkernel/bpf/helpers.c=1683=static void bpf_async_cancel_and_free(struct bpf_async_kern *async)\n--\nkernel/bpf/helpers.c-1713-/*\nkernel/bpf/helpers.c:1714: * This function is called by map_delete/update_elem for individual element and\nkernel/bpf/helpers.c-1715- * by ops-\u003emap_release_uref when the user space reference to a map reaches zero.\n--\nkernel/bpf/helpers.c=1717=void bpf_timer_cancel_and_free(void *val)\n--\nkernel/bpf/helpers.c-1722-/*\nkernel/bpf/helpers.c:1723: * This function is called by map_delete/update_elem for individual element and\nkernel/bpf/helpers.c-1724- * by ops-\u003emap_release_uref when the user space reference to a map reaches zero.\n--\nkernel/bpf/helpers.c=2247=void bpf_list_head_free(const struct btf_field *field, void *list_head,\n--\nkernel/bpf/helpers.c-2257-\t * too long, and also prevent deadlocks if tracing programs end up\nkernel/bpf/helpers.c:2258:\t * executing on entry/exit of functions called inside the critical\nkernel/bpf/helpers.c-2259-\t * section, and end up doing map ops that call bpf_list_head_free for\n--\nkernel/bpf/helpers.c=2388=__bpf_kfunc void *bpf_percpu_obj_new_impl(u64 local_type_id__k, void *meta__ign)\n--\nkernel/bpf/helpers.c-2392-\nkernel/bpf/helpers.c:2393:/* Must be called under migrate_disable(), as required by bpf_mem_free */\nkernel/bpf/helpers.c-2394-void __bpf_obj_drop_impl(void *p, const struct btf_record *rec, bool percpu)\n--\nkernel/bpf/helpers.c=2486=static int __bpf_list_add(struct bpf_list_node_kern *node,\n--\nkernel/bpf/helpers.c-2494-\t/* If list_head was 0-initialized by map, bpf_obj_init_field wasn't\nkernel/bpf/helpers.c:2495:\t * called on its fields, so init here\nkernel/bpf/helpers.c-2496-\t */\n--\nkernel/bpf/helpers.c-2521-fail:\nkernel/bpf/helpers.c:2522:\t/* Only called from BPF prog, no need to migrate_disable */\nkernel/bpf/helpers.c-2523-\t__bpf_obj_drop_impl((void *)n - off, rec, false);\n--\nkernel/bpf/helpers.c=2597=static struct bpf_list_node *__bpf_list_del(struct bpf_list_head *head,\n--\nkernel/bpf/helpers.c-2603-\t/* If list_head was 0-initialized by map, bpf_obj_init_field wasn't\nkernel/bpf/helpers.c:2604:\t * called on its fields, so init here\nkernel/bpf/helpers.c-2605-\t */\n--\nkernel/bpf/helpers.c=2690=__bpf_kfunc bool bpf_list_empty(struct bpf_list_head *head)\n--\nkernel/bpf/helpers.c-2694-\t/* If list_head was 0-initialized by map, bpf_obj_init_field wasn't\nkernel/bpf/helpers.c:2695:\t * called on its fields, so init here\nkernel/bpf/helpers.c-2696-\t */\n--\nkernel/bpf/helpers.c=2725=static int __bpf_rbtree_add(struct bpf_rb_root *root,\n--\nkernel/bpf/helpers.c-2737-\tif (cmpxchg(\u0026node-\u003eowner, NULL, BPF_PTR_POISON)) {\nkernel/bpf/helpers.c:2738:\t\t/* Only called from BPF prog, no need to migrate_disable */\nkernel/bpf/helpers.c-2739-\t\t__bpf_obj_drop_impl((void *)n - off, rec, false);\n--\nkernel/bpf/helpers.c=4318=__bpf_kfunc int bpf_verify_pkcs7_signature(const struct bpf_dynptr *data_p,\n--\nkernel/bpf/helpers.c-4334-\t\t * A call to key_task_permission() here would be redundant, as\nkernel/bpf/helpers.c:4335:\t\t * it is already done by keyring_search() called by\nkernel/bpf/helpers.c-4336-\t\t * find_asymmetric_key().\n--\nkernel/bpf/memalloc.c=620=static void drain_mem_cache(struct bpf_mem_cache *c)\n--\nkernel/bpf/memalloc.c-623-\nkernel/bpf/memalloc.c:624:\t/* No progs are using this bpf_mem_cache, but htab_map_free() called\nkernel/bpf/memalloc.c-625-\t * bpf_mem_cache_free() for all remaining elements and they can be in\n--\nkernel/bpf/memalloc.c=776=static void notrace *unit_alloc(struct bpf_mem_cache *c)\n--\nkernel/bpf/memalloc.c-816- * add it to the free_llist of the current cpu.\nkernel/bpf/memalloc.c:817: * Let kfree() logic deal with it when it's later called from irq_work.\nkernel/bpf/memalloc.c-818- */\n--\nkernel/bpf/net_namespace.c=41=static void netns_bpf_attach_type_need(enum netns_bpf_attach_type type)\n--\nkernel/bpf/net_namespace.c-53-\nkernel/bpf/net_namespace.c:54:/* Must be called with netns_bpf_mutex held. */\nkernel/bpf/net_namespace.c-55-static void netns_bpf_run_array_detach(struct net *net,\n--\nkernel/bpf/net_namespace.c=230=static const struct bpf_link_ops bpf_netns_link_ops = {\n--\nkernel/bpf/net_namespace.c-238-\nkernel/bpf/net_namespace.c:239:/* Must be called with netns_bpf_mutex held. */\nkernel/bpf/net_namespace.c-240-static int __netns_bpf_prog_query(const union bpf_attr *attr,\n--\nkernel/bpf/net_namespace.c=291=int netns_bpf_prog_attach(const union bpf_attr *attr, struct bpf_prog *prog)\n--\nkernel/bpf/net_namespace.c-356-\nkernel/bpf/net_namespace.c:357:/* Must be called with netns_bpf_mutex held. */\nkernel/bpf/net_namespace.c-358-static int __netns_bpf_prog_detach(struct net *net,\n--\nkernel/bpf/rqspinlock.c-44- * The basic principle of a queue-based spinlock can best be understood\nkernel/bpf/rqspinlock.c:45: * by studying a classic queue-based spinlock implementation called the\nkernel/bpf/rqspinlock.c-46- * MCS lock. A copy of the original MCS lock paper (\"Algorithms for Scalable\n--\nkernel/bpf/rqspinlock.c=261=int __lockfunc resilient_tas_spin_lock(rqspinlock_t *lock)\n--\nkernel/bpf/rqspinlock.c-267-\t/*\nkernel/bpf/rqspinlock.c:268:\t * We are either called directly from res_spin_lock after grabbing the\nkernel/bpf/rqspinlock.c-269-\t * deadlock detection entry when queued spinlocks are disabled, or from\n--\nkernel/bpf/states.c=185=static int maybe_exit_scc(struct bpf_verifier_env *env, struct bpf_verifier_state *st)\n--\nkernel/bpf/states.c-197-\t\t * traversal stops when:\nkernel/bpf/states.c:198:\t\t * a. Verification error is found, maybe_exit_scc() is not called.\nkernel/bpf/states.c-199-\t\t * b. Top level BPF_EXIT is reached. Top level BPF_EXIT is not a member\n--\nkernel/bpf/states.c=869=static bool refsafe(struct bpf_verifier_state *old, struct bpf_verifier_state *cur,\n--\nkernel/bpf/states.c-923- *\nkernel/bpf/states.c:924: * this function is called when verifier exploring different branches of\nkernel/bpf/states.c-925- * execution popped from the state stack. If it sees an old state that has\n--\nkernel/bpf/states.c=1239=int bpf_is_state_visited(struct bpf_verifier_env *env, int insn_idx)\n--\nkernel/bpf/states.c-1450-\t\t\t *\nkernel/bpf/states.c:1451:\t\t\t * - When a verification path terminates, `maybe_exit_scc()` is called\nkernel/bpf/states.c-1452-\t\t\t *   from `bpf_update_branch_counts()`. For states with `branches == 0`, it\n--\nkernel/bpf/states.c-1455-\t\t\t *   this SCC visit have been explored. `propagate_backedges()` is then\nkernel/bpf/states.c:1456:\t\t\t *   called, which propagates read and precision marks through the\nkernel/bpf/states.c-1457-\t\t\t *   backedges until a fixed point is reached.\n--\nkernel/bpf/syscall.c=885=static void bpf_map_free(struct bpf_map *map)\n--\nkernel/bpf/syscall.c-914-\nkernel/bpf/syscall.c:915:/* called from workqueue */\nkernel/bpf/syscall.c-916-static void bpf_map_free_deferred(struct work_struct *work)\n--\nkernel/bpf/syscall.c=951=void bpf_map_put(struct bpf_map *map)\n--\nkernel/bpf/syscall.c-953-\tif (atomic64_dec_and_test(\u0026map-\u003erefcnt)) {\nkernel/bpf/syscall.c:954:\t\t/* bpf_map_free_id() must be called first */\nkernel/bpf/syscall.c-955-\t\tbpf_map_free_id(map);\n--\nkernel/bpf/syscall.c=1052=static ssize_t bpf_dummy_write(struct file *filp, const char __user *buf,\n--\nkernel/bpf/syscall.c-1060-\nkernel/bpf/syscall.c:1061:/* called for any extra memory-mapped regions (except initial) */\nkernel/bpf/syscall.c-1062-static void bpf_map_mmap_open(struct vm_area_struct *vma)\n--\nkernel/bpf/syscall.c-1069-\nkernel/bpf/syscall.c:1070:/* called for all unmapped memory region (including initial) */\nkernel/bpf/syscall.c-1071-static void bpf_map_mmap_close(struct vm_area_struct *vma)\n--\nkernel/bpf/syscall.c=1255=static int map_check_btf(struct bpf_map *map, struct bpf_token *token,\n--\nkernel/bpf/syscall.c-1380-#define BPF_MAP_CREATE_LAST_FIELD excl_prog_hash_size\nkernel/bpf/syscall.c:1381:/* called via syscall */\nkernel/bpf/syscall.c-1382-static int map_create_alloc(union bpf_attr *attr, bpfptr_t uattr, struct bpf_verifier_log *log,\n--\nkernel/bpf/syscall.c=2562=static void bpf_prog_show_fdinfo(struct seq_file *m, struct file *filp)\n--\nkernel/bpf/syscall.c-2578-\t\t   \"recursion_misses:\\t%llu\\n\"\nkernel/bpf/syscall.c:2579:\t\t   \"verified_insns:\\t%u\\n\",\nkernel/bpf/syscall.c-2580-\t\t   prog-\u003etype,\n--\nkernel/bpf/syscall.c-2587-\t\t   stats.misses,\nkernel/bpf/syscall.c:2588:\t\t   prog-\u003eaux-\u003everified_insns);\nkernel/bpf/syscall.c-2589-}\n--\nkernel/bpf/syscall.c=3311=static bool bpf_link_is_tracepoint(struct bpf_link *link)\n--\nkernel/bpf/syscall.c-3322-\nkernel/bpf/syscall.c:3323:/* bpf_link_free is guaranteed to be called from process context */\nkernel/bpf/syscall.c-3324-static void bpf_link_free(struct bpf_link *link)\n--\nkernel/bpf/syscall.c=3360=static void bpf_link_put_deferred(struct work_struct *work)\n--\nkernel/bpf/syscall.c-3366-\nkernel/bpf/syscall.c:3367:/* bpf_link_put might be called from atomic context. It needs to be called\nkernel/bpf/syscall.c-3368- * from sleepable context in order to acquire sleeping locks during the process.\n--\nkernel/bpf/syscall.c=3634=static int bpf_tracing_prog_attach(struct bpf_prog *prog,\n--\nkernel/bpf/syscall.c-3727-\t *\nkernel/bpf/syscall.c:3728:\t * - if tgt_prog == NULL when this function was called using the old\nkernel/bpf/syscall.c-3729-\t *   raw_tracepoint_open API, and we need a target from prog-\u003eaux\n--\nkernel/bpf/syscall.c=5099=static int bpf_prog_get_info_by_fd(struct file *file,\n--\nkernel/bpf/syscall.c-5158-\nkernel/bpf/syscall.c:5159:\tinfo.verified_insns = prog-\u003eaux-\u003everified_insns;\nkernel/bpf/syscall.c-5160-\tif (prog-\u003eaux-\u003ebtf)\n--\nkernel/bpf/tnum.c=268=struct tnum tnum_bswap64(struct tnum a)\n--\nkernel/bpf/tnum.c-282- *\nkernel/bpf/tnum.c:283: * Note: if this function is called with z \u003e= tmax, it just returns\nkernel/bpf/tnum.c:284: * early with tmax; if this function is called with z \u003c tmin, the\nkernel/bpf/tnum.c-285- * algorithm already returns tmin.\n--\nkernel/bpf/trampoline.c=98=static int bpf_tramp_ftrace_ops_func(struct ftrace_ops *ops, unsigned long ip,\n--\nkernel/bpf/trampoline.c-108-\tif (cmd == FTRACE_OPS_CMD_ENABLE_SHARE_IPMODIFY_SELF) {\nkernel/bpf/trampoline.c:109:\t\t/* This is called inside register_ftrace_direct_multi(), so\nkernel/bpf/trampoline.c-110-\t\t * trampoline's mutex is already locked.\n--\nkernel/bpf/trampoline.c-133-\t *\nkernel/bpf/trampoline.c:134:\t * The following two commands are called from\nkernel/bpf/trampoline.c-135-\t *\n--\nkernel/bpf/trampoline.c=1633=static void bpf_trampoline_multi_attach_free(struct bpf_trampoline *tr)\n--\nkernel/bpf/trampoline.c-1637-\t * When bpf_trampoline_update() fails before modify_fentry_multi()/\nkernel/bpf/trampoline.c:1638:\t * unregister_fentry_multi() is called, cur_image is unchanged\nkernel/bpf/trampoline.c-1639-\t * (cur_image == old_image) and ftrace still points to it. Freeing\n--\nkernel/bpf/verifier.c=2078=static void __update_reg64_bounds(struct bpf_reg_state *reg)\n--\nkernel/bpf/verifier.c-2096-\t\t/* The u64 range and the tnum only overlap in the maximum value\nkernel/bpf/verifier.c:2097:\t\t * represented by the tnum, called tmax.\nkernel/bpf/verifier.c-2098-\t\t * u64:  ---[xxxxxx]-----\n--\nkernel/bpf/verifier.c=4130=static int check_stack_read(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-4161-\t * ALU. See also retrieve_ptr_limit(). The check in\nkernel/bpf/verifier.c:4162:\t * check_stack_access_for_ptr_arithmetic() called by\nkernel/bpf/verifier.c-4163-\t * adjust_ptr_min_max_vals() prevents users from creating stack pointers\n\n"
}

25/3 2026/09/06 10:05 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 23002
							output: 30
							thoughts: 45
						

Thoughts:
Okay, here's my assessment of the situation:

**Checking for Initialization of `env->subprog_topo_order`**

Alright, I need to verify a crucial detail regarding the initialization of `env->subprog_topo_order`. It's essential that this data structure is always properly set up before `do_check_subprogs` is invoked. This is a common point of error in compiler backends, so I need to be sure. My next step is to initiate a search for instances of `subprog_topo_order` throughout the relevant code base. I need to be thorough and precise to confirm its proper initialization and use. This shouldn't take too long, but it needs to be done.



26/3 2026/09/06 10:06 tool grepper
0m Args:
{
  "Expression": "subprog_topo_order",
  "PathPrefix": "kernel/bpf"
}

Results:
{
  "Output": "kernel/bpf/liveness.c=1985=int bpf_compute_subprog_arg_access(struct bpf_verifier_env *env)\n--\nkernel/bpf/liveness.c-2014-\tfor (k = env-\u003esubprog_cnt - 1; k \u003e= 0; k--) {\nkernel/bpf/liveness.c:2015:\t\tint sub = env-\u003esubprog_topo_order[k];\nkernel/bpf/liveness.c-2016-\n--\nkernel/bpf/verifier.c=3135=static int sort_subprogs_topo(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-3204-\t\t\t\tcolor[cur] = 2;\nkernel/bpf/verifier.c:3205:\t\t\t\tenv-\u003esubprog_topo_order[order++] = cur;\nkernel/bpf/verifier.c-3206-\t\t\t\ttop--;\n--\nkernel/bpf/verifier.c-3213-\t\t\tverbose(env, \"topo_order[%d] = %s\\n\",\nkernel/bpf/verifier.c:3214:\t\t\t\ti, bpf_subprog_name(env, env-\u003esubprog_topo_order[i]));\nkernel/bpf/verifier.c-3215-out:\n--\nkernel/bpf/verifier.c=19755=static int do_check_subprogs(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-19778-\tfor (j = env-\u003esubprog_cnt - 1; j \u003e= 0; j--) {\nkernel/bpf/verifier.c:19779:\t\ti = env-\u003esubprog_topo_order[j];\nkernel/bpf/verifier.c-19780-\t\tif (!i)\n"
}

27/3 2026/09/06 10:06 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 23701
							output: 31
							thoughts: 29
						

Thoughts:
Here's my take on the thought process, as requested:

**Diving into `sort_subprogs_topo`**

Alright, time to dig into this `sort_subprogs_topo` function, specifically within the `bpf_verifier_env` context. My first step is to quickly refresh my memory of its purpose. It's obviously designed for topological sorting of subprograms within the BPF verifier. The next logical question is: where is this function actually *used*? I need to trace back the call chain to understand the broader context and understand why and when the verifier decides to invoke this sorting function. Knowing the calling context is key for understanding its impact and identifying potential issues. I'll need to use my usual tools to trace those calls; finding this function's callers is critical to understanding the bigger picture.



28/3 2026/09/06 10:06 tool grepper
0m Args:
{
  "Expression": "sort_subprogs_topo",
  "PathPrefix": "kernel/bpf"
}

Results:
{
  "Output": "kernel/bpf/verifier.c=3061=static int check_subprogs(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-3134- */\nkernel/bpf/verifier.c:3135:static int sort_subprogs_topo(struct bpf_verifier_env *env)\nkernel/bpf/verifier.c-3136-{\n--\nkernel/bpf/verifier.c=21306=int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\n--\nkernel/bpf/verifier.c-21475-\nkernel/bpf/verifier.c:21476:\tret = sort_subprogs_topo(env);\nkernel/bpf/verifier.c-21477-\tif (ret \u003c 0)\n"
}

29/3 2026/09/06 10:06 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 24171
							output: 38
							thoughts: 23
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Diving into the BPF Verifier**

Alright, time to dig in. The issue *should* be somewhere around line 21475 of `kernel/bpf/verifier.c`. I need to refresh my memory on the surrounding context. Specifically, I'm expecting to find some crucial logic that's leading to the unexpected behavior I'm seeing. I'll be looking for potential flaws in how the verifier handles... well, it could be any number of things. Memory access tracking? Register state management? Loop unrolling interactions? Perhaps there's an issue with how it interprets instruction semantics, or a subtle bug in the constraints it enforces. Given the nature of BPF verification, it's often a complex interplay of different factors, so I need to be methodical. Let's see what the code reveals in that area.



30/3 2026/09/06 10:06 tool read-file
0m Args:
{
  "File": "kernel/bpf/verifier.c",
  "FirstLine": 21470,
  "LineCount": 40
}

Results:
{
  "Contents": "21470:\t\t\tgoto skip_full_check;\n21471:\t\n21472:\t\tret = bpf_prune_dead_branches(env);\n21473:\t\tif (ret \u003c 0)\n21474:\t\t\tgoto skip_full_check;\n21475:\t\n21476:\t\tret = sort_subprogs_topo(env);\n21477:\t\tif (ret \u003c 0)\n21478:\t\t\tgoto skip_full_check;\n21479:\t\n21480:\t\tret = bpf_compute_scc(env);\n21481:\t\tif (ret \u003c 0)\n21482:\t\t\tgoto skip_full_check;\n21483:\t\n21484:\t\t/* must precede the first bpf_ret_reg_pair() user below */\n21485:\t\tret = bpf_compute_subprog_ret_regs(env);\n21486:\t\tif (ret \u003c 0)\n21487:\t\t\tgoto skip_full_check;\n21488:\t\n21489:\t\tret = bpf_compute_live_registers(env);\n21490:\t\tif (ret \u003c 0)\n21491:\t\t\tgoto skip_full_check;\n21492:\t\n21493:\t\tret = mark_fastcall_patterns(env);\n21494:\t\tif (ret \u003c 0)\n21495:\t\t\tgoto skip_full_check;\n21496:\t\n21497:\t\tret = do_check_main(env);\n21498:\t\tret = ret ?: do_check_subprogs(env);\n21499:\t\n21500:\t\tif (ret == 0 \u0026\u0026 bpf_prog_is_offloaded(env-\u003eprog-\u003eaux))\n21501:\t\t\tret = bpf_prog_offload_finalize(env);\n21502:\t\n21503:\tskip_full_check:\n21504:\t\tkvfree(env-\u003eexplored_states);\n21505:\t\n21506:\t\t/* might decrease stack depth, keep it before passes that\n21507:\t\t * allocate additional slots.\n21508:\t\t */\n21509:\t\tif (ret == 0)\n"
}

31/3 2026/09/06 10:06 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 25048
							output: 34
							thoughts: 70
						

Thoughts:
Here's my understanding of the situation, presented as a personal thought process, given my expertise:

**Checking Subprogram Ordering**

Okay, so `sort_subprogs_topo` needs to execute before both `do_check_main` and `do_check_subprogs`. That makes sense; we need the topological sort performed before we start validating anything against the resulting order. The critical part I'm focusing on now is how `subprog_topo_order` is being allocated. I recall that variable, `subprog_topo_order` is allocated within either `bpf_check` or `alloc_env`. I need to drill down and confirm the allocation strategy because depending on when it is set, it will impact the entire control flow and make sure all checks have a well defined starting point. I'll need to double check the calling context of those functions to be certain I have a complete picture of the setup.



32/3 2026/09/06 10:06 tool grepper
0m Args:
{
  "Expression": "subprog_topo_order",
  "PathPrefix": "kernel/bpf/verifier.c"
}

Results:
{
  "Output": "kernel/bpf/verifier.c=3135=static int sort_subprogs_topo(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-3204-\t\t\t\tcolor[cur] = 2;\nkernel/bpf/verifier.c:3205:\t\t\t\tenv-\u003esubprog_topo_order[order++] = cur;\nkernel/bpf/verifier.c-3206-\t\t\t\ttop--;\n--\nkernel/bpf/verifier.c-3213-\t\t\tverbose(env, \"topo_order[%d] = %s\\n\",\nkernel/bpf/verifier.c:3214:\t\t\t\ti, bpf_subprog_name(env, env-\u003esubprog_topo_order[i]));\nkernel/bpf/verifier.c-3215-out:\n--\nkernel/bpf/verifier.c=19755=static int do_check_subprogs(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-19778-\tfor (j = env-\u003esubprog_cnt - 1; j \u003e= 0; j--) {\nkernel/bpf/verifier.c:19779:\t\ti = env-\u003esubprog_topo_order[j];\nkernel/bpf/verifier.c-19780-\t\tif (!i)\n"
}

33/3 2026/09/06 10:06 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 25691
							output: 37
							thoughts: 29
						

Thoughts:
Here's my take on the thought process, as requested:

**Deconstructing `sort_subprogs_topo`**

Okay, so I need to understand how the `env->subprog_topo_order` structure is populated. The obvious place to start digging is the `sort_subprogs_topo` function. That's where the allocation and ordering logic likely reside. I need to trace this to understand the execution path of the subprograms from a topological point of view. Let's see how they are sorted.



34/3 2026/09/06 10:06 tool read-file
0m Args:
{
  "File": "kernel/bpf/verifier.c",
  "FirstLine": 3135,
  "LineCount": 40
}

Results:
{
  "Contents": "3135:\tstatic int sort_subprogs_topo(struct bpf_verifier_env *env)\n3136:\t{\n3137:\t\tstruct bpf_subprog_info *si = env-\u003esubprog_info;\n3138:\t\tint *insn_postorder = env-\u003ecfg.insn_postorder;\n3139:\t\tstruct bpf_insn *insn = env-\u003eprog-\u003einsnsi;\n3140:\t\tint cnt = env-\u003esubprog_cnt;\n3141:\t\tint *dfs_stack = NULL;\n3142:\t\tint top = 0, order = 0;\n3143:\t\tint i, ret = 0;\n3144:\t\tu8 *color = NULL;\n3145:\t\n3146:\t\tcolor = kvzalloc_objs(*color, cnt, GFP_KERNEL_ACCOUNT);\n3147:\t\tdfs_stack = kvmalloc_objs(*dfs_stack, cnt, GFP_KERNEL_ACCOUNT);\n3148:\t\tif (!color || !dfs_stack) {\n3149:\t\t\tret = -ENOMEM;\n3150:\t\t\tgoto out;\n3151:\t\t}\n3152:\t\n3153:\t\t/*\n3154:\t\t * DFS post-order traversal.\n3155:\t\t * Color values: 0 = unvisited, 1 = on stack, 2 = done.\n3156:\t\t */\n3157:\t\tfor (i = 0; i \u003c cnt; i++) {\n3158:\t\t\tif (color[i])\n3159:\t\t\t\tcontinue;\n3160:\t\t\tcolor[i] = 1;\n3161:\t\t\tdfs_stack[top++] = i;\n3162:\t\n3163:\t\t\twhile (top \u003e 0) {\n3164:\t\t\t\tint cur = dfs_stack[top - 1];\n3165:\t\t\t\tint po_start = si[cur].postorder_start;\n3166:\t\t\t\tint po_end = si[cur + 1].postorder_start;\n3167:\t\t\t\tbool pushed = false;\n3168:\t\t\t\tint j;\n3169:\t\n3170:\t\t\t\tfor (j = po_start; j \u003c po_end; j++) {\n3171:\t\t\t\t\tint idx = insn_postorder[j];\n3172:\t\t\t\t\tint callee;\n3173:\t\n3174:\t\t\t\t\tif (!bpf_pseudo_call(\u0026insn[idx]) \u0026\u0026 !bpf_pseudo_func(\u0026insn[idx]))\n"
}

35/3 2026/09/06 10:06 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 26530
							output: 20
							thoughts: 27
						

Thoughts:
Here's my thought process, as if I were considering this code:

**Finding the Allocation of `env->subprog_topo_order`**

Okay, so I need to track down where `env->subprog_topo_order` is being allocated. This feels like a classic debugging exercise. I'm going to need to start by doing a comprehensive search throughout the codebase. I don't want to miss anything, so I'll go wide, searching the entire project tree. Let's see what I turn up. I'm expecting this will involve looking for memory allocation calls.



36/3 2026/09/06 10:06 tool grepper
0m Args:
{
  "Expression": "subprog_topo_order"
}

Results:
{
  "Output": "include/linux/bpf_verifier.h=919=struct bpf_verifier_env {\n--\ninclude/linux/bpf_verifier.h-964-\t/* subprog indices sorted in topological order: leaves first, callers last */\ninclude/linux/bpf_verifier.h:965:\tint subprog_topo_order[BPF_MAX_SUBPROGS + 2];\ninclude/linux/bpf_verifier.h-966-\tunion {\n--\nkernel/bpf/liveness.c=1985=int bpf_compute_subprog_arg_access(struct bpf_verifier_env *env)\n--\nkernel/bpf/liveness.c-2014-\tfor (k = env-\u003esubprog_cnt - 1; k \u003e= 0; k--) {\nkernel/bpf/liveness.c:2015:\t\tint sub = env-\u003esubprog_topo_order[k];\nkernel/bpf/liveness.c-2016-\n--\nkernel/bpf/verifier.c=3135=static int sort_subprogs_topo(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-3204-\t\t\t\tcolor[cur] = 2;\nkernel/bpf/verifier.c:3205:\t\t\t\tenv-\u003esubprog_topo_order[order++] = cur;\nkernel/bpf/verifier.c-3206-\t\t\t\ttop--;\n--\nkernel/bpf/verifier.c-3213-\t\t\tverbose(env, \"topo_order[%d] = %s\\n\",\nkernel/bpf/verifier.c:3214:\t\t\t\ti, bpf_subprog_name(env, env-\u003esubprog_topo_order[i]));\nkernel/bpf/verifier.c-3215-out:\n--\nkernel/bpf/verifier.c=19755=static int do_check_subprogs(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-19778-\tfor (j = env-\u003esubprog_cnt - 1; j \u003e= 0; j--) {\nkernel/bpf/verifier.c:19779:\t\ti = env-\u003esubprog_topo_order[j];\nkernel/bpf/verifier.c-19780-\t\tif (!i)\n"
}

37/3 2026/09/06 10:06 llm kmsan-evaluator
0m Model:
gemini-3.7-flash

Args:
null

Results:
null

Tokens:
							input: 27290
							output: 275
							thoughts: 836
						

Thoughts:
**My Analysis of the BPF Verifier Patch**

Okay, let's break this down. First, I see that `subprog_topo_order` is an embedded array within `struct bpf_verifier_env`, and `env` is allocated with `kzalloc`. This is key. Now, regarding the patch itself, here's my assessment:

1.  **`include/linux/bpf.h`:** The change in `struct bpf_func_info_aux`, replacing single-bit bitfields `called` and `verified` with `bool called[2]` and `bool verified[2]`, is definitely interesting. These auxiliary structures are allocated during BTF processing. The critical thing is that they are either zero-initialized or allocated via `kzalloc_objs`. This sets the stage.

2.  **`kernel/bpf/verifier.c`:** The core logic has been modified. In `check_func_call()`, we're now tracking the invocation context explicitly with `sub_aux->called[in_sleepable_context(env)] = true` instead of a simple boolean assignment. The introduction of `in_sleepable` as a parameter to `do_check_common()` and the explicit setting of `state->in_sleepable` is important for handling sleepable and non-sleepable contexts, and it updates insn accumulation.  `do_check_subprogs()` iterates in reverse topological order, checking both contexts if marked as called.  Finally, `do_check_main()` calls `do_check_common()`. The reverse topological order and dual-context checking is a key change.

3.  **Selftests:** The addition of tests for global subprograms in both sleepable and non-sleepable contexts is a strong indication of what the developers are trying to validate, ensuring they haven't introduced any regression or new issues when handling different contexts.

Now, let's evaluate this in the context of KMSAN vs. KASAN:

*   **Kernel structures sent to userspace:** Nope. The patch is working on `struct bpf_func_info_aux`, which is entirely internal verifier metadata. No data from that struct is ever exposed to userspace.
*   **Conditional logic or branching on uninitialized data:** No.  Fields within `bpf_func_info_aux` are zero-initialized. Any local variables are explicitly initialized. There's no risk here.
*   **Complex data structure allocation/initialization:** No.  Zero-allocation via `kzalloc_objs` is very straightforward.
*   **Bounds checks & logic that could allow access to uninitialized bytes:** No.

Potential bugs, as I see it, would be related to:

*   Verifier logic bugs (rejecting valid programs, accepting invalid programs with incorrect context verification).
*   Infinite loops or ordering issues during subprogram iteration.
*   Out-of-bounds access on the `called` or `verified` arrays if `in_sleepable_context(env)` returned anything other than 0 or 1. However, since it returns a `bool` and we know the size of the array is 2, any out-of-bounds would be caught by KASAN / compiler bounds checks. None of these involve uninitialized memory.

Therefore, **NeedsKMSAN = false.**

My reasoning is straightforward:

*   **KASAN / standard sanitizers:** We're protected against verifier logic bugs, out-of-bounds access on those `called` / `verified` arrays, any potential state allocation errors, use-after-free scenarios, or infinite loops by KASAN and the standard bug detectors.
*   **KMSAN:** We're *not* dealing with uninitialized heap or stack variables, no uninitialized struct padding or fields copied to userspace, and no use of uninitialized data in conditional logic.  `struct bpf_func_info_aux` is consistently zero-initialized via `kzalloc_objs`. No risk of uninitialized memory usage or information leaks here.

Time to call `set-results`.



38/3 2026/09/06 10:06 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch modifies the BPF verifier to track and verify global subprogram calls separately for sleepable and non-sleepable/RCU contexts. It replaces the single-bit `called` and `verified` flags in `struct bpf_func_info_aux` with two-element boolean arrays indexed by `in_sleepable`.\n\n- KMSAN applicability: There is no uninitialized memory risk. The `bpf_func_info_aux` structures are zero-initialized upon allocation (via `kzalloc_objs`), and all local variables introduced or modified in `do_check_subprogs()`, `do_check_common()`, and `check_func_call()` are properly initialized. The arrays are indexed using boolean values (0 or 1), matching the array dimension. No uninitialized memory or structure padding is exposed or copied to user space.\n- KASAN / Standard Detectors: Any memory access issues (such as array out-of-bounds access, invalid pointer dereferences, state allocation issues, or logic/verifier traversal bugs) would be covered by KASAN and standard kernel debugging facilities.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch modifies the BPF verifier to track and verify global subprogram calls separately for sleepable and non-sleepable/RCU contexts. It replaces the single-bit `called` and `verified` flags in `struct bpf_func_info_aux` with two-element boolean arrays indexed by `in_sleepable`.\n\n- KMSAN applicability: There is no uninitialized memory risk. The `bpf_func_info_aux` structures are zero-initialized upon allocation (via `kzalloc_objs`), and all local variables introduced or modified in `do_check_subprogs()`, `do_check_common()`, and `check_func_call()` are properly initialized. The arrays are indexed using boolean values (0 or 1), matching the array dimension. No uninitialized memory or structure padding is exposed or copied to user space.\n- KASAN / Standard Detectors: Any memory access issues (such as array out-of-bounds access, invalid pointer dereferences, state allocation issues, or logic/verifier traversal bugs) would be covered by KASAN and standard kernel debugging facilities.\n\nTherefore, a dedicated KMSAN fuzzing session is not justified.",
  "NeedsKMSAN": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)