| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/05 05:31 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"BPF_SYSCALL",
"PERF_EVENTS"
],
"FocusSymbols": [
"check_ld_abs",
"check_max_stack_depth_subprog",
"in_rbtree_lock_required_cb",
"bpf_btf_find_by_name_kind",
"bpf_get_stack_sleepable",
"bpf_get_task_stack_sleepable"
],
"KMSANReasoning": "The patch series addresses BPF verifier logic and helper metadata:\n1. It flags helper function prototypes (`bpf_btf_find_by_name_kind_proto`, `bpf_get_stack_sleepable_proto`, and `bpf_get_task_stack_sleepable_proto`) with `.might_sleep = true` to prevent their invocation from non-sleepable contexts.\n2. It adds BPF verifier checks (`check_ld_abs` and `check_max_stack_depth_subprog`) to disallow `BPF_LD_[ABS|IND]` within callback functions and their subprograms.\n3. It updates `in_rbtree_lock_required_cb` to iterate across stack frames to properly track rbtree callback context.\n4. It adds selftests for these verifier scenarios.\n\nNone of these changes allocate, initialize, read, or copy uninitialized kernel memory, nor do they expose uninitialized data or alter buffer lengths/boundaries to user space. Locking and sleep-in-atomic issues are handled by standard tools such as LOCKDEP and KASAN. Therefore, a dedicated KMSAN fuzzing session is not needed.",
"NeedsKMSAN": false,
"Reasoning": "The patch modifies core BPF verifier validation logic (rejecting BPF_LD_[ABS|IND] within callback execution contexts and properly walking callback frames when checking rbtree lock requirements) and marks BPF helper prototypes (bpf_btf_find_by_name_kind, bpf_get_stack_sleepable, and bpf_get_task_stack_sleepable) as sleepable. These changes alter instruction verification rules and sleepable helper checks reachable via bpf() syscall program loading.",
"WorthFuzzing": true
} |
| 1/1 | 2026/09/05 05:31 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 78650c5e032c4c1186a8ae4ce98a833ebddd48f0\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Sat Sep 5 05:31:18 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c\nindex 5d93fd82e7645..9f33e95d57410 100644\n--- a/kernel/bpf/btf.c\n+++ b/kernel/bpf/btf.c\n@@ -8749,6 +8749,7 @@ BPF_CALL_4(bpf_btf_find_by_name_kind, char *, name, int, name_sz, u32, kind, int\n const struct bpf_func_proto bpf_btf_find_by_name_kind_proto = {\n \t.func\t\t= bpf_btf_find_by_name_kind,\n \t.gpl_only\t= false,\n+\t.might_sleep\t= true,\n \t.ret_type\t= RET_INTEGER,\n \t.arg1_type\t= ARG_PTR_TO_MEM | MEM_RDONLY,\n \t.arg2_type\t= ARG_MEM_SIZE,\ndiff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c\nindex a839041e0d008..d09d4c3fe547c 100644\n--- a/kernel/bpf/stackmap.c\n+++ b/kernel/bpf/stackmap.c\n@@ -875,6 +875,7 @@ BPF_CALL_4(bpf_get_stack_sleepable, struct pt_regs *, regs, void *, buf, u32, si\n const struct bpf_func_proto bpf_get_stack_sleepable_proto = {\n \t.func\t\t= bpf_get_stack_sleepable,\n \t.gpl_only\t= true,\n+\t.might_sleep\t= true,\n \t.ret_type\t= RET_INTEGER,\n \t.arg1_type\t= ARG_PTR_TO_CTX,\n \t.arg2_type\t= ARG_PTR_TO_UNINIT_MEM,\n@@ -928,6 +929,7 @@ BPF_CALL_4(bpf_get_task_stack_sleepable, struct task_struct *, task, void *, buf\n const struct bpf_func_proto bpf_get_task_stack_sleepable_proto = {\n \t.func\t\t= bpf_get_task_stack_sleepable,\n \t.gpl_only\t= false,\n+\t.might_sleep\t= true,\n \t.ret_type\t= RET_INTEGER,\n \t.arg1_type\t= ARG_PTR_TO_BTF_ID,\n \t.arg1_btf_id\t= \u0026btf_tracing_ids[BTF_TRACING_TYPE_TASK],\ndiff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c\nindex 32d31fa67036b..2cb8fba68cd15 100644\n--- a/kernel/bpf/verifier.c\n+++ b/kernel/bpf/verifier.c\n@@ -5302,6 +5302,15 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,\n \tif (!priv_stack_supported)\n \t\tsubprog[idx].priv_stack_mode = NO_PRIV_STACK;\n process_func:\n+\tif (subprog[idx].has_ld_abs) {\n+\t\tfor (tmp = idx; tmp \u003e= 0; tmp = dinfo[tmp].caller) {\n+\t\t\tif (subprog[tmp].is_cb) {\n+\t\t\t\tverbose(env, \"cannot use BPF_LD_[ABS|IND] within callback\\n\");\n+\t\t\t\treturn -EINVAL;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n \t/* protect against potential stack overflow that might happen when\n \t * bpf2bpf calls get combined with tailcalls. Limit the caller's stack\n \t * depth for such case down to 256 so that the worst case scenario\n@@ -10235,9 +10244,10 @@ static void account_current_path(struct bpf_verifier_env *env)\n \t\t\t\t\tframe ? state-\u003eframe[frame - 1] : NULL);\n }\n \n-/* Are we currently verifying the callback for a rbtree helper that must\n- * be called with lock held? If so, no need to complain about unreleased\n- * lock\n+/*\n+ * Are we currently verifying the callback for an rbtree kfunc that must\n+ * be called with a lock held, or one of that callback's subprogs? If so,\n+ * no need to complain about an unreleased lock.\n */\n static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)\n {\n@@ -10245,17 +10255,19 @@ static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)\n \tstruct bpf_insn *insn = env-\u003eprog-\u003einsnsi;\n \tstruct bpf_func_state *callee;\n \tint kfunc_btf_id;\n+\tu32 frame;\n \n-\tif (!state-\u003ecurframe)\n-\t\treturn false;\n-\n-\tcallee = state-\u003eframe[state-\u003ecurframe];\n+\tfor (frame = state-\u003ecurframe; frame; frame--) {\n+\t\tcallee = state-\u003eframe[frame];\n+\t\tif (!callee-\u003ein_callback_fn)\n+\t\t\tcontinue;\n \n-\tif (!callee-\u003ein_callback_fn)\n-\t\treturn false;\n+\t\tkfunc_btf_id = insn[callee-\u003ecallsite].imm;\n+\t\tif (is_rbtree_lock_required_kfunc(kfunc_btf_id))\n+\t\t\treturn true;\n+\t}\n \n-\tkfunc_btf_id = insn[callee-\u003ecallsite].imm;\n-\treturn is_rbtree_lock_required_kfunc(kfunc_btf_id);\n+\treturn false;\n }\n \n static bool retval_range_within(struct bpf_retval_range range, const struct bpf_reg_state *reg)\n@@ -17179,6 +17191,7 @@ static bool may_access_skb(enum bpf_prog_type type)\n */\n static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)\n {\n+\tstruct bpf_verifier_state *state = env-\u003ecur_state;\n \tstruct bpf_reg_state *regs = cur_regs(env);\n \tstatic const int ctx_reg = BPF_REG_6;\n \tu8 mode = BPF_MODE(insn-\u003ecode);\n@@ -17189,6 +17202,13 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)\n \t\treturn -EINVAL;\n \t}\n \n+\tfor (i = state-\u003ecurframe; i; i--) {\n+\t\tif (state-\u003eframe[i]-\u003ein_callback_fn) {\n+\t\t\tverbose(env, \"cannot use BPF_LD_[ABS|IND] within callback\\n\");\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t}\n+\n \tif (!env-\u003eops-\u003egen_ld_abs) {\n \t\tverifier_bug(env, \"gen_ld_abs is null\");\n \t\treturn -EFAULT;\ndiff --git a/tools/testing/selftests/bpf/progs/preempt_lock.c b/tools/testing/selftests/bpf/progs/preempt_lock.c\nindex 6d5fce7e6ffc9..81c459435680d 100644\n--- a/tools/testing/selftests/bpf/progs/preempt_lock.c\n+++ b/tools/testing/selftests/bpf/progs/preempt_lock.c\n@@ -115,6 +115,58 @@ int preempt_sleepable_helper(void *ctx)\n \treturn 0;\n }\n \n+SEC(\"?uprobe.s\")\n+__failure __msg(\"sleepable helper bpf_get_stack#\")\n+int preempt_sleepable_get_stack(struct pt_regs *ctx)\n+{\n+\tstruct bpf_stack_build_id stack;\n+\n+\tbpf_preempt_disable();\n+\tbpf_get_stack(ctx, \u0026stack, sizeof(stack),\n+\t\t BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);\n+\tbpf_preempt_enable();\n+\treturn 0;\n+}\n+\n+SEC(\"?uprobe.s\")\n+__failure __msg(\"sleepable helper bpf_get_task_stack#\")\n+int preempt_sleepable_get_task_stack(void *ctx)\n+{\n+\tstruct bpf_stack_build_id stack;\n+\tstruct task_struct *task;\n+\n+\ttask = bpf_get_current_task_btf();\n+\tbpf_preempt_disable();\n+\tbpf_get_task_stack(task, \u0026stack, sizeof(stack),\n+\t\t\t BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);\n+\tbpf_preempt_enable();\n+\treturn 0;\n+}\n+\n+SEC(\"?uprobe.s\")\n+__success\n+int sleepable_get_stack(struct pt_regs *ctx)\n+{\n+\tstruct bpf_stack_build_id stack;\n+\n+\tbpf_get_stack(ctx, \u0026stack, sizeof(stack),\n+\t\t BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);\n+\treturn 0;\n+}\n+\n+SEC(\"?uprobe.s\")\n+__success\n+int sleepable_get_task_stack(void *ctx)\n+{\n+\tstruct bpf_stack_build_id stack;\n+\tstruct task_struct *task;\n+\n+\ttask = bpf_get_current_task_btf();\n+\tbpf_get_task_stack(task, \u0026stack, sizeof(stack),\n+\t\t\t BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);\n+\treturn 0;\n+}\n+\n SEC(\"?fentry.s/\" SYS_PREFIX \"sys_getpgid\")\n __failure __msg(\"kernel func bpf_copy_from_user_str is sleepable within non-preemptible region\")\n int preempt_sleepable_kfunc(void *ctx)\ndiff --git a/tools/testing/selftests/bpf/progs/rbtree_fail.c b/tools/testing/selftests/bpf/progs/rbtree_fail.c\nindex 803419a47c626..4504608196abd 100644\n--- a/tools/testing/selftests/bpf/progs/rbtree_fail.c\n+++ b/tools/testing/selftests/bpf/progs/rbtree_fail.c\n@@ -272,6 +272,47 @@ static bool less__bad_res_spin_unlock(struct bpf_rb_node *a, const struct bpf_rb\n \treturn false;\n }\n \n+static __noinline void rbtree_cb_unlock_relock(void)\n+{\n+\tbpf_spin_unlock(\u0026glock);\n+\tbpf_spin_lock(\u0026glock);\n+}\n+\n+static __noinline void rbtree_cb_nested_unlock(void)\n+{\n+\trbtree_cb_unlock_relock();\n+\tasm volatile (\"\");\n+}\n+\n+static bool less__bad_subprog_unlock(struct bpf_rb_node *a, const struct bpf_rb_node *b)\n+{\n+\tstruct node_data *node_a;\n+\tstruct node_data *node_b;\n+\n+\tnode_a = container_of(a, struct node_data, node);\n+\tnode_b = container_of(b, struct node_data, node);\n+\trbtree_cb_nested_unlock();\n+\n+\treturn node_a-\u003ekey \u003c node_b-\u003ekey;\n+}\n+\n+static __noinline void rbtree_cb_noop(void)\n+{\n+\tasm volatile (\"\");\n+}\n+\n+static bool less__subprog_allowed(struct bpf_rb_node *a, const struct bpf_rb_node *b)\n+{\n+\tstruct node_data *node_a;\n+\tstruct node_data *node_b;\n+\n+\tnode_a = container_of(a, struct node_data, node);\n+\tnode_b = container_of(b, struct node_data, node);\n+\trbtree_cb_noop();\n+\n+\treturn node_a-\u003ekey \u003c node_b-\u003ekey;\n+}\n+\n static __always_inline\n long add_with_cb(bool (cb)(struct bpf_rb_node *a, const struct bpf_rb_node *b))\n {\n@@ -330,4 +371,18 @@ long rbtree_api_add_bad_cb_res_spin_unlock(void *ctx)\n \treturn 0;\n }\n \n+SEC(\"?tc\")\n+__failure __msg(\"can't spin_{lock,unlock} in rbtree cb\")\n+long rbtree_api_add_bad_cb_subprog_unlock(void *ctx)\n+{\n+\treturn add_with_cb(less__bad_subprog_unlock);\n+}\n+\n+SEC(\"?tc\")\n+__success\n+long rbtree_api_add_cb_subprog_allowed(void *ctx)\n+{\n+\treturn add_with_cb(less__subprog_allowed);\n+}\n+\n char _license[] SEC(\"license\") = \"GPL\";\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 a7c84d3fa4c73..e0926767bbd3c 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@@ -108,6 +108,30 @@ int timer_sys_close_prog(void *ctx)\n \treturn 0;\n }\n \n+static int timer_btf_find_cb(void *map, int *key, struct bpf_timer *timer)\n+{\n+\tchar name[] = \"task_struct\";\n+\n+\tbpf_btf_find_by_name_kind(name, sizeof(name), BTF_KIND_STRUCT, 0);\n+\treturn 0;\n+}\n+\n+SEC(\"syscall\")\n+__failure __msg(\"sleepable helper bpf_btf_find_by_name_kind#{{[0-9]+}} in non-sleepable prog\")\n+int timer_btf_find_prog(void *ctx)\n+{\n+\tstruct timer_elem *val;\n+\tint key = 0;\n+\n+\tval = bpf_map_lookup_elem(\u0026timer_map, \u0026key);\n+\tif (!val)\n+\t\treturn 0;\n+\n+\tbpf_timer_init(\u0026val-\u003et, \u0026timer_map, 0);\n+\tbpf_timer_set_callback(\u0026val-\u003et, timer_btf_find_cb);\n+\treturn 0;\n+}\n+\n SEC(\"syscall\")\n __success\n int syscall_sys_bpf_prog(void *ctx)\n@@ -126,6 +150,16 @@ int syscall_sys_close_prog(void *ctx)\n \treturn 0;\n }\n \n+SEC(\"syscall\")\n+__success\n+int syscall_btf_find_prog(void *ctx)\n+{\n+\tchar name[] = \"task_struct\";\n+\n+\tbpf_btf_find_by_name_kind(name, sizeof(name), BTF_KIND_STRUCT, 0);\n+\treturn 0;\n+}\n+\n /* Workqueue tests */\n \n struct wq_elem {\ndiff --git a/tools/testing/selftests/bpf/progs/verifier_ld_ind.c b/tools/testing/selftests/bpf/progs/verifier_ld_ind.c\nindex 09e81b99eecb4..32989f981fb69 100644\n--- a/tools/testing/selftests/bpf/progs/verifier_ld_ind.c\n+++ b/tools/testing/selftests/bpf/progs/verifier_ld_ind.c\n@@ -194,6 +194,102 @@ __naked void ld_ind_subprog_both_paths_safe(void)\n \t::: __clobber_all);\n }\n \n+__naked __noinline __used\n+static int ld_abs_callback(void)\n+{\n+\tasm volatile (\n+\t\"r6 = *(u64 *)(r2 + 0);\"\n+\t\".8byte %[ld_abs];\"\n+\t\"r0 = 0;\"\n+\t\"exit;\"\n+\t:\n+\t: __imm_insn(ld_abs, BPF_LD_ABS(BPF_W, 0))\n+\t: __clobber_all);\n+}\n+\n+SEC(\"socket\")\n+__description(\"ld_abs: reject in callback\")\n+__failure __msg(\"cannot use BPF_LD_[ABS|IND] within callback\")\n+int ld_abs_callback_reject(struct __sk_buff *skb)\n+{\n+\tbpf_loop(1, ld_abs_callback, \u0026skb, 0);\n+\treturn 0;\n+}\n+\n+__naked __noinline __used\n+static int ld_ind_callback_subprog(void)\n+{\n+\tasm volatile (\n+\t\"r6 = r1;\"\n+\t\"r7 = 0;\"\n+\t\".8byte %[ld_ind];\"\n+\t\"r0 = 0;\"\n+\t\"exit;\"\n+\t:\n+\t: __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0))\n+\t: __clobber_all);\n+}\n+\n+__naked __noinline __used\n+static int ld_ind_callback(void)\n+{\n+\tasm volatile (\n+\t\"r1 = *(u64 *)(r2 + 0);\"\n+\t\"call ld_ind_callback_subprog;\"\n+\t\"exit;\"\n+\t::: __clobber_all);\n+}\n+\n+SEC(\"socket\")\n+__description(\"ld_ind: reject in callback subprog\")\n+__failure __msg(\"cannot use BPF_LD_[ABS|IND] within callback\")\n+int ld_ind_callback_subprog_reject(struct __sk_buff *skb)\n+{\n+\tbpf_loop(1, ld_ind_callback, \u0026skb, 0);\n+\treturn 0;\n+}\n+\n+static __noinline int ld_ind_global_static(struct __sk_buff *skb)\n+{\n+\tasm volatile (\n+\t\"r6 = %[skb];\"\n+\t\"r7 = 0;\"\n+\t\".8byte %[ld_ind];\"\n+\t:\n+\t: [skb] \"r\"(skb),\n+\t __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0))\n+\t: __clobber_common, \"r6\", \"r7\");\n+\treturn skb-\u003emark;\n+}\n+\n+__noinline int ld_ind_global(struct __sk_buff *skb)\n+{\n+\treturn ld_ind_global_static(skb);\n+}\n+\n+static int ld_ind_global_callback(__u32 index, struct __sk_buff **ctx)\n+{\n+\tld_ind_global(*ctx);\n+\treturn 0;\n+}\n+\n+SEC(\"socket\")\n+__description(\"ld_ind: reject in callback global subprog\")\n+__failure __msg(\"cannot use BPF_LD_[ABS|IND] within callback\")\n+int ld_ind_global_callback_reject(struct __sk_buff *skb)\n+{\n+\tbpf_loop(1, ld_ind_global_callback, \u0026skb, 0);\n+\treturn 0;\n+}\n+\n+SEC(\"socket\")\n+__description(\"ld_ind: allow in non-callback global subprog\")\n+__success\n+int ld_ind_global_subprog_ok(struct __sk_buff *skb)\n+{\n+\treturn ld_ind_global(skb);\n+}\n+\n /*\n * ld_{abs,ind} in subprogs require scalar (int) return type in BTF.\n * A test with void return must be rejected.\n"
} |
| 2/1 | 2026/09/05 05:31 | agent | patch-evaluator |
0mModel:core Args: null Results: {
"EnableConfigs": [
"BPF_SYSCALL",
"PERF_EVENTS"
],
"FocusSymbols": [
"check_ld_abs",
"check_max_stack_depth_subprog",
"in_rbtree_lock_required_cb",
"bpf_btf_find_by_name_kind",
"bpf_get_stack_sleepable",
"bpf_get_task_stack_sleepable"
],
"Reasoning": "The patch modifies core BPF verifier validation logic (rejecting BPF_LD_[ABS|IND] within callback execution contexts and properly walking callback frames when checking rbtree lock requirements) and marks BPF helper prototypes (bpf_btf_find_by_name_kind, bpf_get_stack_sleepable, and bpf_get_task_stack_sleepable) as sleepable. These changes alter instruction verification rules and sleepable helper checks reachable via bpf() syscall program loading.",
"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 78650c5e032c4c1186a8ae4ce98a833ebddd48f0
Author: syz-cluster <triage@syzkaller.com>
Date: Sat Sep 5 05:31:18 2026 +0000
syz-cluster: applied patch under review
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 5d93fd82e7645..9f33e95d57410 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -8749,6 +8749,7 @@ BPF_CALL_4(bpf_btf_find_by_name_kind, char *, name, int, name_sz, u32, kind, int
const struct bpf_func_proto bpf_btf_find_by_name_kind_proto = {
.func = bpf_btf_find_by_name_kind,
.gpl_only = false,
+ .might_sleep = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg2_type = ARG_MEM_SIZE,
diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
index a839041e0d008..d09d4c3fe547c 100644
--- a/kernel/bpf/stackmap.c
+++ b/kernel/bpf/stackmap.c
@@ -875,6 +875,7 @@ BPF_CALL_4(bpf_get_stack_sleepable, struct pt_regs *, regs, void *, buf, u32, si
const struct bpf_func_proto bpf_get_stack_sleepable_proto = {
.func = bpf_get_stack_sleepable,
.gpl_only = true,
+ .might_sleep = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
@@ -928,6 +929,7 @@ BPF_CALL_4(bpf_get_task_stack_sleepable, struct task_struct *, task, void *, buf
const struct bpf_func_proto bpf_get_task_stack_sleepable_proto = {
.func = bpf_get_task_stack_sleepable,
.gpl_only = false,
+ .might_sleep = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK],
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 32d31fa67036b..2cb8fba68cd15 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5302,6 +5302,15 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
if (!priv_stack_supported)
subprog[idx].priv_stack_mode = NO_PRIV_STACK;
process_func:
+ if (subprog[idx].has_ld_abs) {
+ for (tmp = idx; tmp >= 0; tmp = dinfo[tmp].caller) {
+ if (subprog[tmp].is_cb) {
+ verbose(env, "cannot use BPF_LD_[ABS|IND] within callback\n");
+ return -EINVAL;
+ }
+ }
+ }
+
/* protect against potential stack overflow that might happen when
* bpf2bpf calls get combined with tailcalls. Limit the caller's stack
* depth for such case down to 256 so that the worst case scenario
@@ -10235,9 +10244,10 @@ static void account_current_path(struct bpf_verifier_env *env)
frame ? state->frame[frame - 1] : NULL);
}
-/* Are we currently verifying the callback for a rbtree helper that must
- * be called with lock held? If so, no need to complain about unreleased
- * lock
+/*
+ * Are we currently verifying the callback for an rbtree kfunc that must
+ * be called with a lock held, or one of that callback's subprogs? If so,
+ * no need to complain about an unreleased lock.
*/
static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)
{
@@ -10245,17 +10255,19 @@ static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)
struct bpf_insn *insn = env->prog->insnsi;
struct bpf_func_state *callee;
int kfunc_btf_id;
+ u32 frame;
- if (!state->curframe)
- return false;
-
- callee = state->frame[state->curframe];
+ for (frame = state->curframe; frame; frame--) {
+ callee = state->frame[frame];
+ if (!callee->in_callback_fn)
+ continue;
- if (!callee->in_callback_fn)
- return false;
+ kfunc_btf_id = insn[callee->callsite].imm;
+ if (is_rbtree_lock_required_kfunc(kfunc_btf_id))
+ return true;
+ }
- kfunc_btf_id = insn[callee->callsite].imm;
- return is_rbtree_lock_required_kfunc(kfunc_btf_id);
+ return false;
}
static bool retval_range_within(struct bpf_retval_range range, const struct bpf_reg_state *reg)
@@ -17179,6 +17191,7 @@ static bool may_access_skb(enum bpf_prog_type type)
*/
static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
{
+ struct bpf_verifier_state *state = env->cur_state;
struct bpf_reg_state *regs = cur_regs(env);
static const int ctx_reg = BPF_REG_6;
u8 mode = BPF_MODE(insn->code);
@@ -17189,6 +17202,13 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
return -EINVAL;
}
+ for (i = state->curframe; i; i--) {
+ if (state->frame[i]->in_callback_fn) {
+ verbose(env, "cannot use BPF_LD_[ABS|IND] within callback\n");
+ return -EINVAL;
+ }
+ }
+
if (!env->ops->gen_ld_abs) {
verifier_bug(env, "gen_ld_abs is null");
return -EFAULT;
diff --git a/tools/testing/selftests/bpf/progs/preempt_lock.c b/tools/testing/selftests/bpf/progs/preempt_lock.c
index 6d5fce7e6ffc9..81c459435680d 100644
--- a/tools/testing/selftests/bpf/progs/preempt_lock.c
+++ b/tools/testing/selftests/bpf/progs/preempt_lock.c
@@ -115,6 +115,58 @@ int preempt_sleepable_helper(void *ctx)
return 0;
}
+SEC("?uprobe.s")
+__failure __msg("sleepable helper bpf_get_stack#")
+int preempt_sleepable_get_stack(struct pt_regs *ctx)
+{
+ struct bpf_stack_build_id stack;
+
+ bpf_preempt_disable();
+ bpf_get_stack(ctx, &stack, sizeof(stack),
+ BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);
+ bpf_preempt_enable();
+ return 0;
+}
+
+SEC("?uprobe.s")
+__failure __msg("sleepable helper bpf_get_task_stack#")
+int preempt_sleepable_get_task_stack(void *ctx)
+{
+ struct bpf_stack_build_id stack;
+ struct task_struct *task;
+
+ task = bpf_get_current_task_btf();
+ bpf_preempt_disable();
+ bpf_get_task_stack(task, &stack, sizeof(stack),
+ BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);
+ bpf_preempt_enable();
+ return 0;
+}
+
+SEC("?uprobe.s")
+__success
+int sleepable_get_stack(struct pt_regs *ctx)
+{
+ struct bpf_stack_build_id stack;
+
+ bpf_get_stack(ctx, &stack, sizeof(stack),
+ BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);
+ return 0;
+}
+
+SEC("?uprobe.s")
+__success
+int sleepable_get_task_stack(void *ctx)
+{
+ struct bpf_stack_build_id stack;
+ struct task_struct *task;
+
+ task = bpf_get_current_task_btf();
+ bpf_get_task_stack(task, &stack, sizeof(stack),
+ BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);
+ return 0;
+}
+
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
__failure __msg("kernel func bpf_copy_from_user_str is sleepable within non-preemptible region")
int preempt_sleepable_kfunc(void *ctx)
diff --git a/tools/testing/selftests/bpf/progs/rbtree_fail.c b/tools/testing/selftests/bpf/progs/rbtree_fail.c
index 803419a47c626..4504608196abd 100644
--- a/tools/testing/selftests/bpf/progs/rbtree_fail.c
+++ b/tools/testing/selftests/bpf/progs/rbtree_fail.c
@@ -272,6 +272,47 @@ static bool less__bad_res_spin_unlock(struct bpf_rb_node *a, const struct bpf_rb
return false;
}
+static __noinline void rbtree_cb_unlock_relock(void)
+{
+ bpf_spin_unlock(&glock);
+ bpf_spin_lock(&glock);
+}
+
+static __noinline void rbtree_cb_nested_unlock(void)
+{
+ rbtree_cb_unlock_relock();
+ asm volatile ("");
+}
+
+static bool less__bad_subprog_unlock(struct bpf_rb_node *a, const struct bpf_rb_node *b)
+{
+ struct node_data *node_a;
+ struct node_data *node_b;
+
+ node_a = container_of(a, struct node_data, node);
+ node_b = container_of(b, struct node_data, node);
+ rbtree_cb_nested_unlock();
+
+ return node_a->key < node_b->key;
+}
+
+static __noinline void rbtree_cb_noop(void)
+{
+ asm volatile ("");
+}
+
+static bool less__subprog_allowed(struct bpf_rb_node *a, const struct bpf_rb_node *b)
+{
+ struct node_data *node_a;
+ struct node_data *node_b;
+
+ node_a = container_of(a, struct node_data, node);
+ node_b = container_of(b, struct node_data, node);
+ rbtree_cb_noop();
+
+ return node_a->key < node_b->key;
+}
+
static __always_inline
long add_with_cb(bool (cb)(struct bpf_rb_node *a, const struct bpf_rb_node *b))
{
@@ -330,4 +371,18 @@ long rbtree_api_add_bad_cb_res_spin_unlock(void *ctx)
return 0;
}
+SEC("?tc")
+__failure __msg("can't spin_{lock,unlock} in rbtree cb")
+long rbtree_api_add_bad_cb_subprog_unlock(void *ctx)
+{
+ return add_with_cb(less__bad_subprog_unlock);
+}
+
+SEC("?tc")
+__success
+long rbtree_api_add_cb_subprog_allowed(void *ctx)
+{
+ return add_with_cb(less__subprog_allowed);
+}
+
char _license[] SEC("license") = "GPL";
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 a7c84d3fa4c73..e0926767bbd3c 100644
--- a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
+++ b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
@@ -108,6 +108,30 @@ int timer_sys_close_prog(void *ctx)
return 0;
}
+static int timer_btf_find_cb(void *map, int *key, struct bpf_timer *timer)
+{
+ char name[] = "task_struct";
+
+ bpf_btf_find_by_name_kind(name, sizeof(name), BTF_KIND_STRUCT, 0);
+ return 0;
+}
+
+SEC("syscall")
+__failure __msg("sleepable helper bpf_btf_find_by_name_kind#{{[0-9]+}} in non-sleepable prog")
+int timer_btf_find_prog(void *ctx)
+{
+ struct timer_elem *val;
+ int key = 0;
+
+ val = bpf_map_lookup_elem(&timer_map, &key);
+ if (!val)
+ return 0;
+
+ bpf_timer_init(&val->t, &timer_map, 0);
+ bpf_timer_set_callback(&val->t, timer_btf_find_cb);
+ return 0;
+}
+
SEC("syscall")
__success
int syscall_sys_bpf_prog(void *ctx)
@@ -126,6 +150,16 @@ int syscall_sys_close_prog(void *ctx)
return 0;
}
+SEC("syscall")
+__success
+int syscall_btf_find_prog(void *ctx)
+{
+ char name[] = "task_struct";
+
+ bpf_btf_find_by_name_kind(name, sizeof(name), BTF_KIND_STRUCT, 0);
+ return 0;
+}
+
/* Workqueue tests */
struct wq_elem {
diff --git a/tools/testing/selftests/bpf/progs/verifier_ld_ind.c b/tools/testing/selftests/bpf/progs/verifier_ld_ind.c
index 09e81b99eecb4..32989f981fb69 100644
--- a/tools/testing/selftests/bpf/progs/verifier_ld_ind.c
+++ b/tools/testing/selftests/bpf/progs/verifier_ld_ind.c
@@ -194,6 +194,102 @@ __naked void ld_ind_subprog_both_paths_safe(void)
::: __clobber_all);
}
+__naked __noinline __used
+static int ld_abs_callback(void)
+{
+ asm volatile (
+ "r6 = *(u64 *)(r2 + 0);"
+ ".8byte %[ld_abs];"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm_insn(ld_abs, BPF_LD_ABS(BPF_W, 0))
+ : __clobber_all);
+}
+
+SEC("socket")
+__description("ld_abs: reject in callback")
+__failure __msg("cannot use BPF_LD_[ABS|IND] within callback")
+int ld_abs_callback_reject(struct __sk_buff *skb)
+{
+ bpf_loop(1, ld_abs_callback, &skb, 0);
+ return 0;
+}
+
+__naked __noinline __used
+static int ld_ind_callback_subprog(void)
+{
+ asm volatile (
+ "r6 = r1;"
+ "r7 = 0;"
+ ".8byte %[ld_ind];"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0))
+ : __clobber_all);
+}
+
+__naked __noinline __used
+static int ld_ind_callback(void)
+{
+ asm volatile (
+ "r1 = *(u64 *)(r2 + 0);"
+ "call ld_ind_callback_subprog;"
+ "exit;"
+ ::: __clobber_all);
+}
+
+SEC("socket")
+__description("ld_ind: reject in callback subprog")
+__failure __msg("cannot use BPF_LD_[ABS|IND] within callback")
+int ld_ind_callback_subprog_reject(struct __sk_buff *skb)
+{
+ bpf_loop(1, ld_ind_callback, &skb, 0);
+ return 0;
+}
+
+static __noinline int ld_ind_global_static(struct __sk_buff *skb)
+{
+ asm volatile (
+ "r6 = %[skb];"
+ "r7 = 0;"
+ ".8byte %[ld_ind];"
+ :
+ : [skb] "r"(skb),
+ __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0))
+ : __clobber_common, "r6", "r7");
+ return skb->mark;
+}
+
+__noinline int ld_ind_global(struct __sk_buff *skb)
+{
+ return ld_ind_global_static(skb);
+}
+
+static int ld_ind_global_callback(__u32 index, struct __sk_buff **ctx)
+{
+ ld_ind_global(*ctx);
+ return 0;
+}
+
+SEC("socket")
+__description("ld_ind: reject in callback global subprog")
+__failure __msg("cannot use BPF_LD_[ABS|IND] within callback")
+int ld_ind_global_callback_reject(struct __sk_buff *skb)
+{
+ bpf_loop(1, ld_ind_global_callback, &skb, 0);
+ return 0;
+}
+
+SEC("socket")
+__description("ld_ind: allow in non-callback global subprog")
+__success
+int ld_ind_global_subprog_ok(struct __sk_buff *skb)
+{
+ return ld_ind_global(skb);
+}
+
/*
* ld_{abs,ind} in subprogs require scalar (int) return type in BTF.
* A test with void return must be rejected.
|
| 3/2 | 2026/09/05 05:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 7904 output: 68 thoughts: 343 Thoughts: **Analyzing This BPF Patch** Alright, let's break this down. My first instinct is always to look at the diff and understand the scope of the changes. So, I see that this patch touches a few key areas within the BPF subsystem: `kernel/bpf/btf.c`, `kernel/bpf/stackmap.c`, `kernel/bpf/verifier.c`, and a set of selftests. Immediately, I notice the addition of `.might_sleep = true` in the `.proto` structures for several functions: `bpf_btf_find_by_name_kind_proto`, `bpf_get_stack_sleepable_proto`, and `bpf_get_task_stack_sleepable_proto`. That's interesting; it suggests that these functions might now be able to sleep, which could have implications for locking and preemption. I'll need to dig into the context of those functions to fully understand the impact. The meat of the changes, however, seems to be in the verifier. Specifically, `kernel/bpf/verifier.c`. I see new checks in `check_max_stack_depth_subprog`, `in_rbtree_lock_required_cb`, and `check_ld_abs`. The verifier is clearly adding or refining restrictions around the use of `BPF_LD_[ABS|IND]` within callback functions. I see it rejecting code with `"cannot use BPF_LD_[ABS|IND] within callback\n"` if it finds calls that use these. I also see code that walks through the caller chain (the stack frame), checking to see if code might be in a callback. This signals that these calls within callbacks are the thing we're trying to prevent. I need to understand what's triggering these checks in the first place, and why these restrictions are being put in place now. Are there security implications, or is this related to stability and preventing hangs or deadlocks? I will need to understand the relationship between `has_ld_abs`, `is_cb`, `in_callback_fn`, and `is_rbtree_lock_required_kfunc` and how they interrelate. Finally, the selftests are a good sign. They're probably designed to validate these new restrictions and ensure that things are working as intended, and don't regress old functionality. I'll definitely want to take a look at those to see how this is being tested. Time to dive into the code and grep around those files to connect the dots. |
| 4/2 | 2026/09/05 05:31 | tool | grepper |
0mArgs:{
"Expression": "bpf_btf_find_by_name_kind_proto|in_rbtree_lock_required_cb|check_ld_abs|check_max_stack_depth_subprog",
"PathPrefix": "kernel/bpf/"
}Results: {
"Output": "kernel/bpf/btf.c=8723=BPF_CALL_4(bpf_btf_find_by_name_kind, char *, name, int, name_sz, u32, kind, int, flags)\n--\nkernel/bpf/btf.c-8748-\nkernel/bpf/btf.c:8749:const struct bpf_func_proto bpf_btf_find_by_name_kind_proto = {\nkernel/bpf/btf.c-8750-\t.func\t\t= bpf_btf_find_by_name_kind,\n--\nkernel/bpf/syscall.c=6630=syscall_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n--\nkernel/bpf/syscall.c-6636-\tcase BPF_FUNC_btf_find_by_name_kind:\nkernel/bpf/syscall.c:6637:\t\treturn \u0026bpf_btf_find_by_name_kind_proto;\nkernel/bpf/syscall.c-6638-\tcase BPF_FUNC_sys_close:\n--\nkernel/bpf/verifier.c=212=static void invalidate_rcu_protected_refs(struct bpf_verifier_env *env);\nkernel/bpf/verifier.c:213:static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env);\nkernel/bpf/verifier.c-214-static bool is_tracing_prog_type(enum bpf_prog_type type);\n--\nkernel/bpf/verifier.c=5277=struct bpf_subprog_call_depth_info {\n--\nkernel/bpf/verifier.c-5286- */\nkernel/bpf/verifier.c:5287:static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,\nkernel/bpf/verifier.c-5288-\t\t\t\t\t struct bpf_subprog_call_depth_info *dinfo,\n--\nkernel/bpf/verifier.c=5489=static int check_max_stack_depth(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-5521-\t\t\tpriv_stack_supported = !i \u0026\u0026 priv_stack_mode == PRIV_STACK_ADAPTIVE;\nkernel/bpf/verifier.c:5522:\t\t\tret = check_max_stack_depth_subprog(env, i, dinfo,\nkernel/bpf/verifier.c-5523-\t\t\t\t\tpriv_stack_supported);\n--\nkernel/bpf/verifier.c=8689=static int check_func_arg(struct bpf_verifier_env *env, u32 arg,\n--\nkernel/bpf/verifier.c-8836-\tcase ARG_PTR_TO_SPIN_LOCK:\nkernel/bpf/verifier.c:8837:\t\tif (in_rbtree_lock_required_cb(env)) {\nkernel/bpf/verifier.c-8838-\t\t\tverbose(env, \"can't spin_{lock,unlock} in rbtree cb\\n\");\n--\nkernel/bpf/verifier.c=10237=static void account_current_path(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-10251- */\nkernel/bpf/verifier.c:10252:static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)\nkernel/bpf/verifier.c-10253-{\n--\nkernel/bpf/verifier.c=12589=static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta,\n--\nkernel/bpf/verifier.c-12968-\t\t\t\t}\nkernel/bpf/verifier.c:12969:\t\t\t\tif (in_rbtree_lock_required_cb(env)) {\nkernel/bpf/verifier.c-12970-\t\t\t\t\tverbose(env, \"%s not allowed in rbtree cb\\n\", func_name);\n--\nkernel/bpf/verifier.c-13255-\nkernel/bpf/verifier.c:13256:\t\t\tif (in_rbtree_lock_required_cb(env)) {\nkernel/bpf/verifier.c-13257-\t\t\t\tverbose(env, \"can't res_spin_{lock,unlock} in rbtree cb\\n\");\n--\nkernel/bpf/verifier.c=13681=static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n--\nkernel/bpf/verifier.c-13876-\nkernel/bpf/verifier.c:13877:\tif (in_rbtree_lock_required_cb(env) \u0026\u0026 (rcu_lock || rcu_unlock)) {\nkernel/bpf/verifier.c-13878-\t\tverbose(env, \"Calling bpf_rcu_read_{lock,unlock} in unnecessary rbtree callback\\n\");\n--\nkernel/bpf/verifier.c=17165=static bool may_access_skb(enum bpf_prog_type type)\n--\nkernel/bpf/verifier.c-17191- */\nkernel/bpf/verifier.c:17192:static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)\nkernel/bpf/verifier.c-17193-{\n--\nkernel/bpf/verifier.c=18123=static int do_check_insn(struct bpf_verifier_env *env, bool *do_print_state)\n--\nkernel/bpf/verifier.c-18215-\t\tif (mode == BPF_ABS || mode == BPF_IND)\nkernel/bpf/verifier.c:18216:\t\t\treturn check_ld_abs(env, insn);\nkernel/bpf/verifier.c-18217-\n"
} |
| 5/2 | 2026/09/05 05:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 10397 output: 37 thoughts: 833 Thoughts: **BPF Verifier and Helper Functions: Fuzzing Target Identification** Okay, so I'm looking at this patch and immediately the BPF verifier and helper functions jump out. These changes touch code in `kernel/bpf/verifier.c`, `kernel/bpf/btf.c`, and `kernel/bpf/stackmap.c` – all core components of the BPF subsystem, which is, of course, accessible from userspace via the `bpf(2)` syscall. Given our fuzzing triage guidelines, this is a clear "WorthFuzzing=true" situation. The changes affect executable code within a core subsystem. Now, let's drill down to identify specific focus symbols for fuzzing. The patch modifies several key functions: `check_max_stack_depth_subprog`, `in_rbtree_lock_required_cb`, `check_ld_abs`, `bpf_btf_find_by_name_kind`, `bpf_get_stack_sleepable`, and `bpf_get_task_stack_sleepable`. The question now is, are these static, exported, or inlined? Let's see: `check_max_stack_depth_subprog`, `in_rbtree_lock_required_cb`, and `check_ld_abs` are all static within `verifier.c`. `bpf_btf_find_by_name_kind`, `bpf_get_stack_sleepable`, and `bpf_get_task_stack_sleepable` are *not* static; they're BPF_CALLs in `btf.c` and `stackmap.c` respectively, which is good for our purposes. And `bpf_check` is the main verifier entrypoint. Now, let's delve into the structure and call-flow of the static functions to understand how they're used within `verifier.c`. We'll need to figure out how these static verifier functions are used to get a better attack surface for fuzzing. |
| 6/2 | 2026/09/05 05:31 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/verifier.c",
"FirstLine": 5280,
"LineCount": 40
}Results: {
"Contents": "5280:\t\tint frame; /* # of consecutive static call stack frames on top of stack */\n5281:\t};\n5282:\t\n5283:\t/* starting from main bpf function walk all instructions of the function\n5284:\t * and recursively walk all callees that given function can call.\n5285:\t * Ignore jump and exit insns.\n5286:\t */\n5287:\tstatic int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,\n5288:\t\t\t\t\t\t struct bpf_subprog_call_depth_info *dinfo,\n5289:\t\t\t\t\t\t bool priv_stack_supported)\n5290:\t{\n5291:\t\tstruct bpf_subprog_info *subprog = env-\u003esubprog_info;\n5292:\t\tstruct bpf_insn *insn = env-\u003eprog-\u003einsnsi;\n5293:\t\tint depth = 0, frame = 0, i, subprog_end, subprog_depth;\n5294:\t\tbool tail_call_reachable = false;\n5295:\t\tint total;\n5296:\t\tint tmp;\n5297:\t\n5298:\t\t/* no caller idx */\n5299:\t\tdinfo[idx].caller = -1;\n5300:\t\n5301:\t\ti = subprog[idx].start;\n5302:\t\tif (!priv_stack_supported)\n5303:\t\t\tsubprog[idx].priv_stack_mode = NO_PRIV_STACK;\n5304:\tprocess_func:\n5305:\t\tif (subprog[idx].has_ld_abs) {\n5306:\t\t\tfor (tmp = idx; tmp \u003e= 0; tmp = dinfo[tmp].caller) {\n5307:\t\t\t\tif (subprog[tmp].is_cb) {\n5308:\t\t\t\t\tverbose(env, \"cannot use BPF_LD_[ABS|IND] within callback\\n\");\n5309:\t\t\t\t\treturn -EINVAL;\n5310:\t\t\t\t}\n5311:\t\t\t}\n5312:\t\t}\n5313:\t\n5314:\t\t/* protect against potential stack overflow that might happen when\n5315:\t\t * bpf2bpf calls get combined with tailcalls. Limit the caller's stack\n5316:\t\t * depth for such case down to 256 so that the worst case scenario\n5317:\t\t * would result in 8k stack size (32 which is tailcall limit * 256 =\n5318:\t\t * 8k).\n5319:\t\t *\n"
} |
| 7/2 | 2026/09/05 05:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 12387 output: 38 thoughts: 55 Thoughts: Here's my thinking process: **Diving into BTF and Stackmap Implementation** Okay, so I need to understand how BTF and stack maps are working, specifically regarding finding BTF data by name and checking for sleepability on the stack. My initial approach will be to examine the code directly. I'll need to look closely at the `bpf_btf_find_by_name_kind` and `bpf_get_stack_sleepable` functions. Given that they're core BPF functionalities, they're probably residing within the kernel source. The most logical locations to start are `kernel/bpf/btf.c` and `kernel/bpf/stackmap.c`. This should give me a good handle on the actual implementation details and the logic flow of these key functions, which will hopefully illuminate the areas I need to focus on. |
| 8/2 | 2026/09/05 05:31 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/btf.c",
"FirstLine": 8740,
"LineCount": 25
}Results: {
"Contents": "8740:\t\t\t\treturn btf_obj_fd;\n8741:\t\t\t}\n8742:\t\t\treturn ret | (((u64)btf_obj_fd) \u003c\u003c 32);\n8743:\t\t}\n8744:\t\tif (ret \u003e 0)\n8745:\t\t\tbtf_put(btf);\n8746:\t\treturn ret;\n8747:\t}\n8748:\t\n8749:\tconst struct bpf_func_proto bpf_btf_find_by_name_kind_proto = {\n8750:\t\t.func\t\t= bpf_btf_find_by_name_kind,\n8751:\t\t.gpl_only\t= false,\n8752:\t\t.might_sleep\t= true,\n8753:\t\t.ret_type\t= RET_INTEGER,\n8754:\t\t.arg1_type\t= ARG_PTR_TO_MEM | MEM_RDONLY,\n8755:\t\t.arg2_type\t= ARG_MEM_SIZE,\n8756:\t\t.arg3_type\t= ARG_ANYTHING,\n8757:\t\t.arg4_type\t= ARG_ANYTHING,\n8758:\t};\n8759:\t\n8760:\tBTF_ID_LIST_GLOBAL(btf_tracing_ids, MAX_BTF_TRACING_TYPE)\n8761:\t#define BTF_TRACING_TYPE(name, type) BTF_ID(struct, type)\n8762:\tBTF_TRACING_TYPE_xxx\n8763:\t#undef BTF_TRACING_TYPE\n8764:\t\n"
} |
| 9/2 | 2026/09/05 05:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 13098 output: 37 thoughts: 15 Thoughts: Here's a summary of my thought process, as requested: **Diving into the BPF Stack Map Implementation** Okay, time to dig into the details. I need to understand how stack maps work within the BPF framework. My instinct is to start by examining the core implementation, so let's navigate directly to `kernel/bpf/stackmap.c`. This file should hold the key logic for allocating, managing, and interacting with BPF stack maps. I'm hoping to find details on how stack space is reserved, how data is written and read, and how these maps integrate with the broader BPF verifier and execution environment. I'm particularly interested in any synchronization mechanisms they employ, potential performance bottlenecks, and any clever optimizations used to make stack maps efficient in the context of BPF programs. This should be a good starting point for a deeper dive. |
| 10/2 | 2026/09/05 05:31 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/stackmap.c",
"FirstLine": 870,
"LineCount": 70
}Results: {
"Contents": " 870:\t\t u64, flags)\n 871:\t{\n 872:\t\treturn __bpf_get_stack(regs, NULL, buf, size, flags, true /* may_fault */);\n 873:\t}\n 874:\t\n 875:\tconst struct bpf_func_proto bpf_get_stack_sleepable_proto = {\n 876:\t\t.func\t\t= bpf_get_stack_sleepable,\n 877:\t\t.gpl_only\t= true,\n 878:\t\t.might_sleep\t= true,\n 879:\t\t.ret_type\t= RET_INTEGER,\n 880:\t\t.arg1_type\t= ARG_PTR_TO_CTX,\n 881:\t\t.arg2_type\t= ARG_PTR_TO_UNINIT_MEM,\n 882:\t\t.arg3_type\t= ARG_MEM_SIZE_OR_ZERO,\n 883:\t\t.arg4_type\t= ARG_ANYTHING,\n 884:\t};\n 885:\t\n 886:\tstatic long __bpf_get_task_stack(struct task_struct *task, void *buf, u32 size,\n 887:\t\t\t\t\t u64 flags, bool may_fault)\n 888:\t{\n 889:\t\tstruct pt_regs *regs;\n 890:\t\tlong res = -EINVAL;\n 891:\t\n 892:\t\tif (!try_get_task_stack(task)) {\n 893:\t\t\tmemset(buf, 0, size);\n 894:\t\t\treturn -EFAULT;\n 895:\t\t}\n 896:\t\n 897:\t\tregs = task_pt_regs(task);\n 898:\t\tif (regs)\n 899:\t\t\tres = __bpf_get_stack(regs, task, buf, size, flags, may_fault);\n 900:\t\telse\n 901:\t\t\tmemset(buf, 0, size);\n 902:\t\tput_task_stack(task);\n 903:\t\treturn res;\n 904:\t}\n 905:\t\n 906:\tBPF_CALL_4(bpf_get_task_stack, struct task_struct *, task, void *, buf,\n 907:\t\t u32, size, u64, flags)\n 908:\t{\n 909:\t\treturn __bpf_get_task_stack(task, buf, size, flags, false /* !may_fault */);\n 910:\t}\n 911:\t\n 912:\tconst struct bpf_func_proto bpf_get_task_stack_proto = {\n 913:\t\t.func\t\t= bpf_get_task_stack,\n 914:\t\t.gpl_only\t= false,\n 915:\t\t.ret_type\t= RET_INTEGER,\n 916:\t\t.arg1_type\t= ARG_PTR_TO_BTF_ID,\n 917:\t\t.arg1_btf_id\t= \u0026btf_tracing_ids[BTF_TRACING_TYPE_TASK],\n 918:\t\t.arg2_type\t= ARG_PTR_TO_UNINIT_MEM,\n 919:\t\t.arg3_type\t= ARG_MEM_SIZE_OR_ZERO,\n 920:\t\t.arg4_type\t= ARG_ANYTHING,\n 921:\t};\n 922:\t\n 923:\tBPF_CALL_4(bpf_get_task_stack_sleepable, struct task_struct *, task, void *, buf,\n 924:\t\t u32, size, u64, flags)\n 925:\t{\n 926:\t\treturn __bpf_get_task_stack(task, buf, size, flags, true /* !may_fault */);\n 927:\t}\n 928:\t\n 929:\tconst struct bpf_func_proto bpf_get_task_stack_sleepable_proto = {\n 930:\t\t.func\t\t= bpf_get_task_stack_sleepable,\n 931:\t\t.gpl_only\t= false,\n 932:\t\t.might_sleep\t= true,\n 933:\t\t.ret_type\t= RET_INTEGER,\n 934:\t\t.arg1_type\t= ARG_PTR_TO_BTF_ID,\n 935:\t\t.arg1_btf_id\t= \u0026btf_tracing_ids[BTF_TRACING_TYPE_TASK],\n 936:\t\t.arg2_type\t= ARG_PTR_TO_UNINIT_MEM,\n 937:\t\t.arg3_type\t= ARG_MEM_SIZE_OR_ZERO,\n 938:\t\t.arg4_type\t= ARG_ANYTHING,\n 939:\t};\n"
} |
| 11/2 | 2026/09/05 05:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 14583 output: 29 thoughts: 42 Thoughts: Okay, here's my take on those thoughts, framed as my own mental process, given my expertise: **Checking BPF Verifier and Helpers Kconfig Dependencies** Alright, time to get to the core of this. I need to make sure the BPF verifier and BPF helpers are properly configured. That means digging into the Kconfig dependencies. Where's the first place I'd expect to find these settings? My instinct is to start by looking in the places that define the core BPF features. Likely candidates for Kconfig files would be `kernel/bpf/Kconfig` or possibly even `init/Kconfig` to see if there are global options or initialization related settings that would affect this. I'll need to meticulously go through those files and trace out the dependency chains to ensure nothing’s broken or unintentionally disabled. It's a quick and efficient way to verify everything is in place for optimal performance. |
| 12/2 | 2026/09/05 05:31 | tool | grepper |
0mError:no matches Args: {
"Expression": "config BPF_SYSCALL",
"PathPrefix": "init/"
}Results: {
"error": "no matches"
} |
| 13/2 | 2026/09/05 05:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 14862 output: 32 thoughts: 24 Thoughts: Here's my thought process, as it went down: **BPF Configuration Deep Dive** Okay, so I need to get a handle on the BPF configuration. My first move is to head straight to the source. Specifically, I know that the `kernel/bpf/Makefile` is where a lot of the build-time configurations related to BPF will reside. I'll need to check the dependencies and see how it builds, and I'll also check any conditional compilation logic it might contain. Then, to get the user-facing configuration options, `init/Kconfig` is the place to look for the kernel build configuration options that end up in `.config`. It's pretty standard, but I want to verify if there's anything new or any changes to what I already know about BPF configs. This should give me a solid foundation for understanding how BPF is enabled and controlled in this kernel build. |
| 14/2 | 2026/09/05 05:31 | tool | grepper |
0mArgs:{
"Expression": "BPF_SYSCALL",
"PathPrefix": "kernel/bpf/Makefile"
}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\nkernel/bpf/Makefile:11:obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o bloom_filter.o\nkernel/bpf/Makefile:12:obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o ringbuf.o bpf_insn_array.o\nkernel/bpf/Makefile:13:obj-$(CONFIG_BPF_SYSCALL) += bpf_local_storage.o bpf_task_storage.o\nkernel/bpf/Makefile:14:obj-$(CONFIG_BPF_SYSCALL) += fixups.o cfg.o states.o backtrack.o check_btf.o\nkernel/bpf/Makefile-15-obj-${CONFIG_BPF_LSM}\t += bpf_inode_storage.o\nkernel/bpf/Makefile:16:obj-$(CONFIG_BPF_SYSCALL) += disasm.o mprog.o\nkernel/bpf/Makefile-17-obj-$(CONFIG_BPF_JIT) += trampoline.o\nkernel/bpf/Makefile:18:obj-$(CONFIG_BPF_SYSCALL) += btf.o memalloc.o rqspinlock.o stream.o\nkernel/bpf/Makefile-19-ifeq ($(CONFIG_MMU)$(CONFIG_64BIT),yy)\nkernel/bpf/Makefile:20:obj-$(CONFIG_BPF_SYSCALL) += arena.o range_tree.o\nkernel/bpf/Makefile-21-endif\n--\nkernel/bpf/Makefile=23=ifeq ($(CONFIG_NET),y)\nkernel/bpf/Makefile:24:obj-$(CONFIG_BPF_SYSCALL) += devmap.o\nkernel/bpf/Makefile:25:obj-$(CONFIG_BPF_SYSCALL) += cpumap.o\nkernel/bpf/Makefile:26:obj-$(CONFIG_BPF_SYSCALL) += offload.o\nkernel/bpf/Makefile:27:obj-$(CONFIG_BPF_SYSCALL) += net_namespace.o\nkernel/bpf/Makefile:28:obj-$(CONFIG_BPF_SYSCALL) += tcx.o\nkernel/bpf/Makefile-29-endif\nkernel/bpf/Makefile=30=ifeq ($(CONFIG_PERF_EVENTS),y)\nkernel/bpf/Makefile:31:obj-$(CONFIG_BPF_SYSCALL) += stackmap.o\nkernel/bpf/Makefile-32-endif\nkernel/bpf/Makefile=33=ifeq ($(CONFIG_CGROUPS),y)\nkernel/bpf/Makefile:34:obj-$(CONFIG_BPF_SYSCALL) += cgroup_iter.o bpf_cgrp_storage.o\nkernel/bpf/Makefile-35-endif\n--\nkernel/bpf/Makefile=37=ifeq ($(CONFIG_INET),y)\nkernel/bpf/Makefile:38:obj-$(CONFIG_BPF_SYSCALL) += reuseport_array.o\nkernel/bpf/Makefile-39-endif\n--\nkernel/bpf/Makefile=43=ifeq ($(CONFIG_BPF_JIT),y)\nkernel/bpf/Makefile:44:obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o\nkernel/bpf/Makefile:45:obj-$(CONFIG_BPF_SYSCALL) += cpumask.o\nkernel/bpf/Makefile-46-# bpf_lsm_proto.o must precede bpf_lsm.o. The current pahole logic\n--\nkernel/bpf/Makefile=58=ifneq ($(CONFIG_CRYPTO),)\nkernel/bpf/Makefile:59:obj-$(CONFIG_BPF_SYSCALL) += crypto.o\nkernel/bpf/Makefile-60-endif\nkernel/bpf/Makefile=61=obj-$(CONFIG_BPF_PRELOAD) += preload/\nkernel/bpf/Makefile-62-\nkernel/bpf/Makefile:63:obj-$(CONFIG_BPF_SYSCALL) += relo_core.o\nkernel/bpf/Makefile:64:obj-$(CONFIG_BPF_SYSCALL) += btf_iter.o\nkernel/bpf/Makefile:65:obj-$(CONFIG_BPF_SYSCALL) += btf_relocate.o\nkernel/bpf/Makefile:66:obj-$(CONFIG_BPF_SYSCALL) += kmem_cache_iter.o\nkernel/bpf/Makefile-67-ifeq ($(CONFIG_DMA_SHARED_BUFFER),y)\nkernel/bpf/Makefile:68:obj-$(CONFIG_BPF_SYSCALL) += dmabuf_iter.o\nkernel/bpf/Makefile-69-endif\n"
} |
| 15/2 | 2026/09/05 05:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 16460 output: 37 thoughts: 20 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Examining Kconfig for BPF and Perf Event Configurations** Alright, time to get my hands dirty. I'm going to start by diving straight into the Kconfig definitions. I need to be absolutely sure about how `BPF_SYSCALL` and `PERF_EVENTS` are configured within the kernel. I can't afford to make assumptions here. I need to understand the dependencies, the relationships between these features, and any potential side effects of their enablement or disablement. This means looking beyond just the immediate definitions, tracing through the conditional logic, and seeing how they interact with other subsystems. I want to know everything about their configuration, how they tie into the overall kernel build, and if there are any subtle nuances in their behavior based on different build configurations. This level of detail is critical for any serious analysis or development involving BPF and performance monitoring. My goal is a rock-solid understanding of their configuration options. |
| 16/2 | 2026/09/05 05:31 | tool | grepper |
0mArgs:{
"Expression": "config (BPF_SYSCALL|BPF)",
"PathPrefix": "kernel/bpf/"
}Results: {
"Output": "kernel/bpf/Kconfig-3-# BPF interpreter that, for example, classic socket filters depend on.\nkernel/bpf/Kconfig:4:config BPF\nkernel/bpf/Kconfig-5-\tbool\n--\nkernel/bpf/Kconfig=25=menu \"BPF subsystem\"\nkernel/bpf/Kconfig-26-\nkernel/bpf/Kconfig:27:config BPF_SYSCALL\nkernel/bpf/Kconfig-28-\tbool \"Enable bpf() system call\"\n--\nkernel/bpf/Kconfig-41-\nkernel/bpf/Kconfig:42:config BPF_JIT\nkernel/bpf/Kconfig-43-\tbool \"Enable BPF Just In Time compiler\"\n--\nkernel/bpf/Kconfig-57-\nkernel/bpf/Kconfig:58:config BPF_JIT_ALWAYS_ON\nkernel/bpf/Kconfig-59-\tbool \"Permanently enable BPF JIT and remove BPF interpreter\"\n--\nkernel/bpf/Kconfig-68-\nkernel/bpf/Kconfig:69:config BPF_JIT_DEFAULT_ON\nkernel/bpf/Kconfig-70-\tdef_bool ARCH_WANT_DEFAULT_BPF_JIT || BPF_JIT_ALWAYS_ON\n--\nkernel/bpf/Kconfig-72-\nkernel/bpf/Kconfig:73:config BPF_UNPRIV_DEFAULT_OFF\nkernel/bpf/Kconfig-74-\tbool \"Disable unprivileged BPF by default\"\n--\nkernel/bpf/Kconfig=90=source \"kernel/bpf/preload/Kconfig\"\nkernel/bpf/Kconfig-91-\nkernel/bpf/Kconfig:92:config BPF_LSM\nkernel/bpf/Kconfig-93-\tbool \"Enable BPF LSM Instrumentation\"\n--\nkernel/bpf/preload/Kconfig-1-# SPDX-License-Identifier: GPL-2.0-only\nkernel/bpf/preload/Kconfig:2:menuconfig BPF_PRELOAD\nkernel/bpf/preload/Kconfig-3-\tbool \"Preload BPF file system with kernel specific program and map iterators\"\n--\nkernel/bpf/preload/Kconfig=14=if BPF_PRELOAD\nkernel/bpf/preload/Kconfig:15:config BPF_PRELOAD_UMD\nkernel/bpf/preload/Kconfig-16-\ttristate \"bpf_preload kernel module\"\n"
} |
| 17/2 | 2026/09/05 05:31 | llm | patch-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 17344 output: 227 thoughts: 772 Thoughts: **My Analysis of this BPF Patch** Okay, so I've got this patch in front of me, and my initial thought process is to break down its implications within the BPF subsystem. First, I see the conditional compilation: `verifier.c` and `btf.c` are compiled under `CONFIG_BPF_SYSCALL`, and `stackmap.c` requires both `CONFIG_BPF_SYSCALL` and `CONFIG_PERF_EVENTS`. Naturally, `CONFIG_BPF` is the baseline. Now, let's look at what's being modified. I'm focusing on these symbols: `check_ld_abs`, `check_max_stack_depth_subprog`, `in_rbtree_lock_required_cb`, `bpf_btf_find_by_name_kind`, `bpf_get_stack_sleepable`, and `bpf_get_task_stack_sleepable`. Quick check, and I see that the first three (`check_ld_abs`, `check_max_stack_depth_subprog`, `in_rbtree_lock_required_cb`) are static functions within `verifier.c`, which makes them particularly interesting in the context of verification. The last three (`bpf_btf_find_by_name_kind`, `bpf_get_stack_sleepable`, `bpf_get_task_stack_sleepable`) are functions in `btf.c` and `stackmap.c`, respectively. My next step is to consider `bpf_check`, the core verification entry point. This makes sense to me to consider those symbols I noted earlier. The patch indicates that `check_ld_abs`, `check_max_stack_depth_subprog`, `in_rbtree_lock_required_cb`, `bpf_btf_find_by_name_kind`, `bpf_get_stack_sleepable`, `bpf_get_task_stack_sleepable` should all be considered. Alright, the core of the patch involves verifier logic changes in `verifier.c` (disabling `BPF_LD_ABS/IND` inside subprogram callbacks, updating rbtree lock verifier checks) and adding `.might_sleep = true` flags to several BPF helper function definitions. These are definitely core changes, and directly touchable via BPF program loading and verification. That makes this patch a high priority for fuzzing. Therefore, the `EnableConfigs` are: `BPF_SYSCALL` and `PERF_EVENTS`. The `FocusSymbols` are the ones mentioned earlier: `check_ld_abs`, `check_max_stack_depth_subprog`, `in_rbtree_lock_required_cb`, `bpf_btf_find_by_name_kind`, `bpf_get_stack_sleepable`, and `bpf_get_task_stack_sleepable`. Let's set the results and move forward. |
| 18/2 | 2026/09/05 05:32 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"BPF_SYSCALL",
"PERF_EVENTS"
],
"FocusSymbols": [
"check_ld_abs",
"check_max_stack_depth_subprog",
"in_rbtree_lock_required_cb",
"bpf_btf_find_by_name_kind",
"bpf_get_stack_sleepable",
"bpf_get_task_stack_sleepable"
],
"Reasoning": "The patch modifies core BPF verifier validation logic (rejecting BPF_LD_[ABS|IND] within callback execution contexts and properly walking callback frames when checking rbtree lock requirements) and marks BPF helper prototypes (bpf_btf_find_by_name_kind, bpf_get_stack_sleepable, and bpf_get_task_stack_sleepable) as sleepable. These changes alter instruction verification rules and sleepable helper checks reachable via bpf() syscall program loading.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"BPF_SYSCALL",
"PERF_EVENTS"
],
"FocusSymbols": [
"check_ld_abs",
"check_max_stack_depth_subprog",
"in_rbtree_lock_required_cb",
"bpf_btf_find_by_name_kind",
"bpf_get_stack_sleepable",
"bpf_get_task_stack_sleepable"
],
"Reasoning": "The patch modifies core BPF verifier validation logic (rejecting BPF_LD_[ABS|IND] within callback execution contexts and properly walking callback frames when checking rbtree lock requirements) and marks BPF helper prototypes (bpf_btf_find_by_name_kind, bpf_get_stack_sleepable, and bpf_get_task_stack_sleepable) as sleepable. These changes alter instruction verification rules and sleepable helper checks reachable via bpf() syscall program loading.",
"WorthFuzzing": true
} |
| 19/1 | 2026/09/05 05:32 | action | If |
0mArgs:{
"WorthFuzzing": true
}Results: null |
| 20/2 | 2026/09/05 05:32 | agent | kmsan-evaluator |
0mModel:core Args: null Results: {
"KMSANReasoning": "The patch series addresses BPF verifier logic and helper metadata:\n1. It flags helper function prototypes (`bpf_btf_find_by_name_kind_proto`, `bpf_get_stack_sleepable_proto`, and `bpf_get_task_stack_sleepable_proto`) with `.might_sleep = true` to prevent their invocation from non-sleepable contexts.\n2. It adds BPF verifier checks (`check_ld_abs` and `check_max_stack_depth_subprog`) to disallow `BPF_LD_[ABS|IND]` within callback functions and their subprograms.\n3. It updates `in_rbtree_lock_required_cb` to iterate across stack frames to properly track rbtree callback context.\n4. It adds selftests for these verifier scenarios.\n\nNone of these changes allocate, initialize, read, or copy uninitialized kernel memory, nor do they expose uninitialized data or alter buffer lengths/boundaries to user space. Locking and sleep-in-atomic issues are handled by standard tools such as LOCKDEP and KASAN. Therefore, a dedicated KMSAN fuzzing session is not needed.",
"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 78650c5e032c4c1186a8ae4ce98a833ebddd48f0
Author: syz-cluster <triage@syzkaller.com>
Date: Sat Sep 5 05:31:18 2026 +0000
syz-cluster: applied patch under review
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 5d93fd82e7645..9f33e95d57410 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -8749,6 +8749,7 @@ BPF_CALL_4(bpf_btf_find_by_name_kind, char *, name, int, name_sz, u32, kind, int
const struct bpf_func_proto bpf_btf_find_by_name_kind_proto = {
.func = bpf_btf_find_by_name_kind,
.gpl_only = false,
+ .might_sleep = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_MEM | MEM_RDONLY,
.arg2_type = ARG_MEM_SIZE,
diff --git a/kernel/bpf/stackmap.c b/kernel/bpf/stackmap.c
index a839041e0d008..d09d4c3fe547c 100644
--- a/kernel/bpf/stackmap.c
+++ b/kernel/bpf/stackmap.c
@@ -875,6 +875,7 @@ BPF_CALL_4(bpf_get_stack_sleepable, struct pt_regs *, regs, void *, buf, u32, si
const struct bpf_func_proto bpf_get_stack_sleepable_proto = {
.func = bpf_get_stack_sleepable,
.gpl_only = true,
+ .might_sleep = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_CTX,
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
@@ -928,6 +929,7 @@ BPF_CALL_4(bpf_get_task_stack_sleepable, struct task_struct *, task, void *, buf
const struct bpf_func_proto bpf_get_task_stack_sleepable_proto = {
.func = bpf_get_task_stack_sleepable,
.gpl_only = false,
+ .might_sleep = true,
.ret_type = RET_INTEGER,
.arg1_type = ARG_PTR_TO_BTF_ID,
.arg1_btf_id = &btf_tracing_ids[BTF_TRACING_TYPE_TASK],
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 32d31fa67036b..2cb8fba68cd15 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5302,6 +5302,15 @@ static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx,
if (!priv_stack_supported)
subprog[idx].priv_stack_mode = NO_PRIV_STACK;
process_func:
+ if (subprog[idx].has_ld_abs) {
+ for (tmp = idx; tmp >= 0; tmp = dinfo[tmp].caller) {
+ if (subprog[tmp].is_cb) {
+ verbose(env, "cannot use BPF_LD_[ABS|IND] within callback\n");
+ return -EINVAL;
+ }
+ }
+ }
+
/* protect against potential stack overflow that might happen when
* bpf2bpf calls get combined with tailcalls. Limit the caller's stack
* depth for such case down to 256 so that the worst case scenario
@@ -10235,9 +10244,10 @@ static void account_current_path(struct bpf_verifier_env *env)
frame ? state->frame[frame - 1] : NULL);
}
-/* Are we currently verifying the callback for a rbtree helper that must
- * be called with lock held? If so, no need to complain about unreleased
- * lock
+/*
+ * Are we currently verifying the callback for an rbtree kfunc that must
+ * be called with a lock held, or one of that callback's subprogs? If so,
+ * no need to complain about an unreleased lock.
*/
static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)
{
@@ -10245,17 +10255,19 @@ static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)
struct bpf_insn *insn = env->prog->insnsi;
struct bpf_func_state *callee;
int kfunc_btf_id;
+ u32 frame;
- if (!state->curframe)
- return false;
-
- callee = state->frame[state->curframe];
+ for (frame = state->curframe; frame; frame--) {
+ callee = state->frame[frame];
+ if (!callee->in_callback_fn)
+ continue;
- if (!callee->in_callback_fn)
- return false;
+ kfunc_btf_id = insn[callee->callsite].imm;
+ if (is_rbtree_lock_required_kfunc(kfunc_btf_id))
+ return true;
+ }
- kfunc_btf_id = insn[callee->callsite].imm;
- return is_rbtree_lock_required_kfunc(kfunc_btf_id);
+ return false;
}
static bool retval_range_within(struct bpf_retval_range range, const struct bpf_reg_state *reg)
@@ -17179,6 +17191,7 @@ static bool may_access_skb(enum bpf_prog_type type)
*/
static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
{
+ struct bpf_verifier_state *state = env->cur_state;
struct bpf_reg_state *regs = cur_regs(env);
static const int ctx_reg = BPF_REG_6;
u8 mode = BPF_MODE(insn->code);
@@ -17189,6 +17202,13 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
return -EINVAL;
}
+ for (i = state->curframe; i; i--) {
+ if (state->frame[i]->in_callback_fn) {
+ verbose(env, "cannot use BPF_LD_[ABS|IND] within callback\n");
+ return -EINVAL;
+ }
+ }
+
if (!env->ops->gen_ld_abs) {
verifier_bug(env, "gen_ld_abs is null");
return -EFAULT;
diff --git a/tools/testing/selftests/bpf/progs/preempt_lock.c b/tools/testing/selftests/bpf/progs/preempt_lock.c
index 6d5fce7e6ffc9..81c459435680d 100644
--- a/tools/testing/selftests/bpf/progs/preempt_lock.c
+++ b/tools/testing/selftests/bpf/progs/preempt_lock.c
@@ -115,6 +115,58 @@ int preempt_sleepable_helper(void *ctx)
return 0;
}
+SEC("?uprobe.s")
+__failure __msg("sleepable helper bpf_get_stack#")
+int preempt_sleepable_get_stack(struct pt_regs *ctx)
+{
+ struct bpf_stack_build_id stack;
+
+ bpf_preempt_disable();
+ bpf_get_stack(ctx, &stack, sizeof(stack),
+ BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);
+ bpf_preempt_enable();
+ return 0;
+}
+
+SEC("?uprobe.s")
+__failure __msg("sleepable helper bpf_get_task_stack#")
+int preempt_sleepable_get_task_stack(void *ctx)
+{
+ struct bpf_stack_build_id stack;
+ struct task_struct *task;
+
+ task = bpf_get_current_task_btf();
+ bpf_preempt_disable();
+ bpf_get_task_stack(task, &stack, sizeof(stack),
+ BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);
+ bpf_preempt_enable();
+ return 0;
+}
+
+SEC("?uprobe.s")
+__success
+int sleepable_get_stack(struct pt_regs *ctx)
+{
+ struct bpf_stack_build_id stack;
+
+ bpf_get_stack(ctx, &stack, sizeof(stack),
+ BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);
+ return 0;
+}
+
+SEC("?uprobe.s")
+__success
+int sleepable_get_task_stack(void *ctx)
+{
+ struct bpf_stack_build_id stack;
+ struct task_struct *task;
+
+ task = bpf_get_current_task_btf();
+ bpf_get_task_stack(task, &stack, sizeof(stack),
+ BPF_F_USER_STACK | BPF_F_USER_BUILD_ID);
+ return 0;
+}
+
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
__failure __msg("kernel func bpf_copy_from_user_str is sleepable within non-preemptible region")
int preempt_sleepable_kfunc(void *ctx)
diff --git a/tools/testing/selftests/bpf/progs/rbtree_fail.c b/tools/testing/selftests/bpf/progs/rbtree_fail.c
index 803419a47c626..4504608196abd 100644
--- a/tools/testing/selftests/bpf/progs/rbtree_fail.c
+++ b/tools/testing/selftests/bpf/progs/rbtree_fail.c
@@ -272,6 +272,47 @@ static bool less__bad_res_spin_unlock(struct bpf_rb_node *a, const struct bpf_rb
return false;
}
+static __noinline void rbtree_cb_unlock_relock(void)
+{
+ bpf_spin_unlock(&glock);
+ bpf_spin_lock(&glock);
+}
+
+static __noinline void rbtree_cb_nested_unlock(void)
+{
+ rbtree_cb_unlock_relock();
+ asm volatile ("");
+}
+
+static bool less__bad_subprog_unlock(struct bpf_rb_node *a, const struct bpf_rb_node *b)
+{
+ struct node_data *node_a;
+ struct node_data *node_b;
+
+ node_a = container_of(a, struct node_data, node);
+ node_b = container_of(b, struct node_data, node);
+ rbtree_cb_nested_unlock();
+
+ return node_a->key < node_b->key;
+}
+
+static __noinline void rbtree_cb_noop(void)
+{
+ asm volatile ("");
+}
+
+static bool less__subprog_allowed(struct bpf_rb_node *a, const struct bpf_rb_node *b)
+{
+ struct node_data *node_a;
+ struct node_data *node_b;
+
+ node_a = container_of(a, struct node_data, node);
+ node_b = container_of(b, struct node_data, node);
+ rbtree_cb_noop();
+
+ return node_a->key < node_b->key;
+}
+
static __always_inline
long add_with_cb(bool (cb)(struct bpf_rb_node *a, const struct bpf_rb_node *b))
{
@@ -330,4 +371,18 @@ long rbtree_api_add_bad_cb_res_spin_unlock(void *ctx)
return 0;
}
+SEC("?tc")
+__failure __msg("can't spin_{lock,unlock} in rbtree cb")
+long rbtree_api_add_bad_cb_subprog_unlock(void *ctx)
+{
+ return add_with_cb(less__bad_subprog_unlock);
+}
+
+SEC("?tc")
+__success
+long rbtree_api_add_cb_subprog_allowed(void *ctx)
+{
+ return add_with_cb(less__subprog_allowed);
+}
+
char _license[] SEC("license") = "GPL";
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 a7c84d3fa4c73..e0926767bbd3c 100644
--- a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
+++ b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
@@ -108,6 +108,30 @@ int timer_sys_close_prog(void *ctx)
return 0;
}
+static int timer_btf_find_cb(void *map, int *key, struct bpf_timer *timer)
+{
+ char name[] = "task_struct";
+
+ bpf_btf_find_by_name_kind(name, sizeof(name), BTF_KIND_STRUCT, 0);
+ return 0;
+}
+
+SEC("syscall")
+__failure __msg("sleepable helper bpf_btf_find_by_name_kind#{{[0-9]+}} in non-sleepable prog")
+int timer_btf_find_prog(void *ctx)
+{
+ struct timer_elem *val;
+ int key = 0;
+
+ val = bpf_map_lookup_elem(&timer_map, &key);
+ if (!val)
+ return 0;
+
+ bpf_timer_init(&val->t, &timer_map, 0);
+ bpf_timer_set_callback(&val->t, timer_btf_find_cb);
+ return 0;
+}
+
SEC("syscall")
__success
int syscall_sys_bpf_prog(void *ctx)
@@ -126,6 +150,16 @@ int syscall_sys_close_prog(void *ctx)
return 0;
}
+SEC("syscall")
+__success
+int syscall_btf_find_prog(void *ctx)
+{
+ char name[] = "task_struct";
+
+ bpf_btf_find_by_name_kind(name, sizeof(name), BTF_KIND_STRUCT, 0);
+ return 0;
+}
+
/* Workqueue tests */
struct wq_elem {
diff --git a/tools/testing/selftests/bpf/progs/verifier_ld_ind.c b/tools/testing/selftests/bpf/progs/verifier_ld_ind.c
index 09e81b99eecb4..32989f981fb69 100644
--- a/tools/testing/selftests/bpf/progs/verifier_ld_ind.c
+++ b/tools/testing/selftests/bpf/progs/verifier_ld_ind.c
@@ -194,6 +194,102 @@ __naked void ld_ind_subprog_both_paths_safe(void)
::: __clobber_all);
}
+__naked __noinline __used
+static int ld_abs_callback(void)
+{
+ asm volatile (
+ "r6 = *(u64 *)(r2 + 0);"
+ ".8byte %[ld_abs];"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm_insn(ld_abs, BPF_LD_ABS(BPF_W, 0))
+ : __clobber_all);
+}
+
+SEC("socket")
+__description("ld_abs: reject in callback")
+__failure __msg("cannot use BPF_LD_[ABS|IND] within callback")
+int ld_abs_callback_reject(struct __sk_buff *skb)
+{
+ bpf_loop(1, ld_abs_callback, &skb, 0);
+ return 0;
+}
+
+__naked __noinline __used
+static int ld_ind_callback_subprog(void)
+{
+ asm volatile (
+ "r6 = r1;"
+ "r7 = 0;"
+ ".8byte %[ld_ind];"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0))
+ : __clobber_all);
+}
+
+__naked __noinline __used
+static int ld_ind_callback(void)
+{
+ asm volatile (
+ "r1 = *(u64 *)(r2 + 0);"
+ "call ld_ind_callback_subprog;"
+ "exit;"
+ ::: __clobber_all);
+}
+
+SEC("socket")
+__description("ld_ind: reject in callback subprog")
+__failure __msg("cannot use BPF_LD_[ABS|IND] within callback")
+int ld_ind_callback_subprog_reject(struct __sk_buff *skb)
+{
+ bpf_loop(1, ld_ind_callback, &skb, 0);
+ return 0;
+}
+
+static __noinline int ld_ind_global_static(struct __sk_buff *skb)
+{
+ asm volatile (
+ "r6 = %[skb];"
+ "r7 = 0;"
+ ".8byte %[ld_ind];"
+ :
+ : [skb] "r"(skb),
+ __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0))
+ : __clobber_common, "r6", "r7");
+ return skb->mark;
+}
+
+__noinline int ld_ind_global(struct __sk_buff *skb)
+{
+ return ld_ind_global_static(skb);
+}
+
+static int ld_ind_global_callback(__u32 index, struct __sk_buff **ctx)
+{
+ ld_ind_global(*ctx);
+ return 0;
+}
+
+SEC("socket")
+__description("ld_ind: reject in callback global subprog")
+__failure __msg("cannot use BPF_LD_[ABS|IND] within callback")
+int ld_ind_global_callback_reject(struct __sk_buff *skb)
+{
+ bpf_loop(1, ld_ind_global_callback, &skb, 0);
+ return 0;
+}
+
+SEC("socket")
+__description("ld_ind: allow in non-callback global subprog")
+__success
+int ld_ind_global_subprog_ok(struct __sk_buff *skb)
+{
+ return ld_ind_global(skb);
+}
+
/*
* ld_{abs,ind} in subprogs require scalar (int) return type in BTF.
* A test with void return must be rejected.
|
| 21/3 | 2026/09/05 05:32 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 7116 output: 49 thoughts: 953 Thoughts: **KMSAN Fuzzing Session Justification: A BPF Patch Series Analysis** Alright, let's break this down. The question is, does this patch series warrant a dedicated KMSAN fuzzing session? Time to think like an expert. My initial impression is, probably not. Let's delve in. First, I see changes primarily focused within the BPF subsystem: specifically, in the verifier and helper function attributes. The patch modifies `kernel/bpf/btf.c`, `kernel/bpf/stackmap.c`, and `kernel/bpf/verifier.c`, along with associated self-tests. The core of it boils down to three key areas. 1. We've got `might_sleep = true` being added to `bpf_btf_find_by_name_kind_proto`, `bpf_get_stack_sleepable_proto`, and `bpf_get_task_stack_sleepable_proto`. This is about the verifier enforcing that sleepable helpers are used correctly – i.e., only in sleepable contexts, not when preemption is disabled or in RCU critical sections. Standard stuff. 2. The verifier now explicitly disallows `BPF_LD_ABS` and `BPF_LD_IND` within BPF callbacks and subprograms that those callbacks call. This is expected since these instructions involve special JIT transformations or re-entry that doesn't gel with callback execution. It makes sense to forbid them. 3. The `in_rbtree_lock_required_cb` verifier check has been updated to handle subprograms called from rbtree callbacks. It looks like it allows the verifier to properly understand the context, thereby avoiding false positives about unreleased locks if the subprogram is correctly handling lock acquisition/release inside the rbtree callback. Now, let's switch gears and approach this from a KMSAN perspective. What kind of uninitialized memory issues might this introduce or expose? I have to be systematic. 1. **Kernel-to-User Space Information Leak:** This patch series doesn't touch any of the typical culprits - no `copy_to_user`, no netlink messages, no ioctls. No data exposure is added or altered. I don't see any risk here. 2. **Uninitialized Variable Usage in Conditional Logic:** The verifier's internal state variables look to be well-defined. No weird conditional logic depending on uninitialized values. I see no concerns. 3. **Data Structure Allocation/Initialization Issues:** No new allocations are added by these changes. 4. **Bounds Checks/Uninitialized Bytes:** The changes restrict instructions in callbacks, modify sleepable helper attributes, and improve verifier logic related to rbtree callbacks. No chance here. So, where's the KMSAN hook? Honestly, I don't see any. KMSAN is all about detecting reads of uninitialized memory and info leaks to userspace. This patch series is all about BPF verifier logic and helper attributes. Any sleepable or locking bugs or verifier issues that creep up should be caught by the verifier itself, LOCKDEP, or those standard kernel runtime checks. Just to be thorough, I might want to quickly double-check `kernel/bpf/btf.c` and `kernel/bpf/stackmap.c` around `bpf_get_stack_sleepable`, `bpf_get_task_stack_sleepable`, and `bpf_btf_find_by_name_kind` but my conclusion is that it's probably not worth a full KMSAN fuzzing session. |
| 22/3 | 2026/09/05 05:32 | tool | grepper |
0mArgs:{
"Expression": "BPF_CALL_4\\(bpf_btf_find_by_name_kind",
"PathPrefix": "kernel/bpf/btf.c"
}Results: {
"Output": "kernel/bpf/btf.c=8712=static int check_btf_kconfigs(const struct module *module, const char *feature)\n--\nkernel/bpf/btf.c-8722-\nkernel/bpf/btf.c:8723:BPF_CALL_4(bpf_btf_find_by_name_kind, char *, name, int, name_sz, u32, kind, int, flags)\nkernel/bpf/btf.c-8724-{\n"
} |
| 23/3 | 2026/09/05 05:32 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 9039 output: 38 thoughts: 22 Thoughts: Here's a summary of my thought process, as requested: **Diving into BTF Internals** Okay, time to get my hands dirty. I need to understand what's happening within the BPF type format (BTF) code, specifically, where the action is taking place. My intuition is telling me to zero in on `kernel/bpf/btf.c` because that's where the core BTF implementation likely resides. I'm going to start by taking a close look around line 8723. It's not a random number; it's a strategic starting point based on my past experience with similar codebases. Hopefully, this area will shed some light on the behavior I'm trying to diagnose or understand better. Let's see what's happening there and then use that as a jumping-off point. |
| 24/3 | 2026/09/05 05:32 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/btf.c",
"FirstLine": 8720,
"LineCount": 40
}Results: {
"Contents": "8720:\t\treturn 0;\n8721:\t}\n8722:\t\n8723:\tBPF_CALL_4(bpf_btf_find_by_name_kind, char *, name, int, name_sz, u32, kind, int, flags)\n8724:\t{\n8725:\t\tstruct btf *btf = NULL;\n8726:\t\tint btf_obj_fd = 0;\n8727:\t\tlong ret;\n8728:\t\n8729:\t\tif (flags)\n8730:\t\t\treturn -EINVAL;\n8731:\t\n8732:\t\tif (name_sz \u003c= 1 || name[name_sz - 1])\n8733:\t\t\treturn -EINVAL;\n8734:\t\n8735:\t\tret = bpf_find_btf_id(name, kind, \u0026btf);\n8736:\t\tif (ret \u003e 0 \u0026\u0026 btf_is_module(btf)) {\n8737:\t\t\tbtf_obj_fd = __btf_new_fd(btf);\n8738:\t\t\tif (btf_obj_fd \u003c 0) {\n8739:\t\t\t\tbtf_put(btf);\n8740:\t\t\t\treturn btf_obj_fd;\n8741:\t\t\t}\n8742:\t\t\treturn ret | (((u64)btf_obj_fd) \u003c\u003c 32);\n8743:\t\t}\n8744:\t\tif (ret \u003e 0)\n8745:\t\t\tbtf_put(btf);\n8746:\t\treturn ret;\n8747:\t}\n8748:\t\n8749:\tconst struct bpf_func_proto bpf_btf_find_by_name_kind_proto = {\n8750:\t\t.func\t\t= bpf_btf_find_by_name_kind,\n8751:\t\t.gpl_only\t= false,\n8752:\t\t.might_sleep\t= true,\n8753:\t\t.ret_type\t= RET_INTEGER,\n8754:\t\t.arg1_type\t= ARG_PTR_TO_MEM | MEM_RDONLY,\n8755:\t\t.arg2_type\t= ARG_MEM_SIZE,\n8756:\t\t.arg3_type\t= ARG_ANYTHING,\n8757:\t\t.arg4_type\t= ARG_ANYTHING,\n8758:\t};\n8759:\t\n"
} |
| 25/3 | 2026/09/05 05:32 | llm | kmsan-evaluator |
0mModel:gemini-3.7-flash Args: null Results: null Tokens: input: 9929 output: 277 thoughts: 223 Thoughts: **My Assessment of the Kernel Patch** Okay, this looks pretty clean-cut. I've reviewed the proposed changes, and it's all rather direct. The core of this patch is about adding a `.might_sleep = true` flag to some BPF helper function definitions: `bpf_btf_find_by_name_kind_proto`, `bpf_get_stack_sleepable_proto`, and `bpf_get_task_stack_sleepable_proto`. This signifies that these functions *can* potentially sleep, which is important for the verifier to understand. Beyond that, we have verifier validation additions. Specifically, the patch adds checks to disallow `BPF_LD_[ABS|IND]` in callbacks and their subprograms. This means we're tightening the verification rules regarding direct memory access within these contexts, using `check_max_stack_depth_subprog` and `check_ld_abs`. We also have a fix for `in_rbtree_lock_required_cb` to correctly analyze the caller frames for rbtree callback context. Finally, some new selftests are included to cover these new verifier rules and sleepable helpers, which is good to see for ongoing reliability. Importantly, based on my analysis, *none* of these changes involve memory allocation, copying, or expose uninitialized kernel memory or change memory safety semantics in a way that requires KMSAN. I'm not seeing anything here that would necessitate KMSAN to catch potential issues. Therefore, the `NeedsKMSAN` flag can be set to `false`. Now, I'll go ahead and use `set-results` to record this assessment. When setting the results I'll provide a clear explanation contrasting KASAN/LOCKDEP with KMSAN. It is important to remember what each tool is intended for, and while KASAN/LOCKDEP are critical for detecting concurrency and memory safety issues, KMSAN is tailored for uninitialized memory, which is not pertinent here. |
| 26/3 | 2026/09/05 05:32 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series addresses BPF verifier logic and helper metadata:\n1. It flags helper function prototypes (`bpf_btf_find_by_name_kind_proto`, `bpf_get_stack_sleepable_proto`, and `bpf_get_task_stack_sleepable_proto`) with `.might_sleep = true` to prevent their invocation from non-sleepable contexts.\n2. It adds BPF verifier checks (`check_ld_abs` and `check_max_stack_depth_subprog`) to disallow `BPF_LD_[ABS|IND]` within callback functions and their subprograms.\n3. It updates `in_rbtree_lock_required_cb` to iterate across stack frames to properly track rbtree callback context.\n4. It adds selftests for these verifier scenarios.\n\nNone of these changes allocate, initialize, read, or copy uninitialized kernel memory, nor do they expose uninitialized data or alter buffer lengths/boundaries to user space. Locking and sleep-in-atomic issues are handled by standard tools such as LOCKDEP and KASAN. Therefore, a dedicated KMSAN fuzzing session is not needed.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series addresses BPF verifier logic and helper metadata:\n1. It flags helper function prototypes (`bpf_btf_find_by_name_kind_proto`, `bpf_get_stack_sleepable_proto`, and `bpf_get_task_stack_sleepable_proto`) with `.might_sleep = true` to prevent their invocation from non-sleepable contexts.\n2. It adds BPF verifier checks (`check_ld_abs` and `check_max_stack_depth_subprog`) to disallow `BPF_LD_[ABS|IND]` within callback functions and their subprograms.\n3. It updates `in_rbtree_lock_required_cb` to iterate across stack frames to properly track rbtree callback context.\n4. It adds selftests for these verifier scenarios.\n\nNone of these changes allocate, initialize, read, or copy uninitialized kernel memory, nor do they expose uninitialized data or alter buffer lengths/boundaries to user space. Locking and sleep-in-atomic issues are handled by standard tools such as LOCKDEP and KASAN. Therefore, a dedicated KMSAN fuzzing session is not needed.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|