| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/16 02:13 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "The patch is entirely focused on improving BPF verifier diagnostics. It updates error messages, adds hints, and reorders the BTF validation and subprogram layout checks to report BTF/CO-RE errors before subprogram layout errors. These are purely decorative and diagnostic changes that do not alter the set of accepted BPF programs or the execution flow of valid programs.",
"WorthFuzzing": false
} |
| 1/1 | 2026/08/16 02:13 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit a4c88729b476347cbbc1f15e4c82945b417658f9\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Sun Aug 16 02:13:14 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c\nindex 5b9d767895c91..6967d48bba49d 100644\n--- a/kernel/bpf/btf.c\n+++ b/kernel/bpf/btf.c\n@@ -1169,19 +1169,19 @@ static const char *btf_show_name(struct btf_show *show)\n \t\t\tid = t-\u003etype;\n \t\t\tbreak;\n \t\tdefault:\n-\t\t\tid = 0;\n-\t\t\tbreak;\n+\t\t\tgoto resolved;\n \t\t}\n+\t\tt = btf_type_skip_qualifiers(show-\u003ebtf, id);\n \t\tif (!id)\n \t\t\tbreak;\n-\t\tt = btf_type_skip_qualifiers(show-\u003ebtf, id);\n \t}\n \t/* We may not be able to represent this type; bail to be safe */\n \tif (i == BTF_SHOW_MAX_ITER)\n \t\treturn \"\";\n \n+resolved:\n \tif (!name)\n-\t\tname = btf_name_by_offset(show-\u003ebtf, t-\u003ename_off);\n+\t\tname = btf_type_is_void(t) ? \"void\" : btf_name_by_offset(show-\u003ebtf, t-\u003ename_off);\n \n \tswitch (BTF_INFO_KIND(t-\u003einfo)) {\n \tcase BTF_KIND_STRUCT:\ndiff --git a/kernel/bpf/cfg.c b/kernel/bpf/cfg.c\nindex 0f13c13f41337..95c59f6cf70ae 100644\n--- a/kernel/bpf/cfg.c\n+++ b/kernel/bpf/cfg.c\n@@ -287,7 +287,7 @@ static struct bpf_iarray *jt_from_map(struct bpf_map *map)\n * combined jump table in jt-\u003eitems (allocated with kvcalloc)\n */\n static struct bpf_iarray *jt_from_subprog(struct bpf_verifier_env *env,\n-\t\t\t\t\t int subprog_start, int subprog_end)\n+\t\t\t\t\t int insn_idx, int subprog_start, int subprog_end)\n {\n \tstruct bpf_iarray *jt = NULL;\n \tstruct bpf_map *map;\n@@ -327,7 +327,7 @@ static struct bpf_iarray *jt_from_subprog(struct bpf_verifier_env *env,\n \tif (!jt) {\n \t\tverbose(env, \"no jump tables found for subprog starting at %u\\n\", subprog_start);\n \t\tbpf_diag_program_structure(\n-\t\t\tenv, subprog_start, \"missing jump table\",\n+\t\t\tenv, insn_idx, \"missing jump table\",\n \t\t\t\"Make sure subprograms containing gotox instructions are accompanied by jump tables referencing these subprograms.\",\n \t\t\t\"No jump table was found for the subprogram that starts at instruction %u.\",\n \t\t\tsubprog_start);\n@@ -349,7 +349,7 @@ create_jt(int t, struct bpf_verifier_env *env)\n \tsubprog = bpf_find_containing_subprog(env, t);\n \tsubprog_start = subprog-\u003estart;\n \tsubprog_end = (subprog + 1)-\u003estart;\n-\tjt = jt_from_subprog(env, subprog_start, subprog_end);\n+\tjt = jt_from_subprog(env, t, subprog_start, subprog_end);\n \tif (IS_ERR(jt))\n \t\treturn jt;\n \ndiff --git a/kernel/bpf/diagnostics.c b/kernel/bpf/diagnostics.c\nindex 33b7d9e8e2c3a..44d0b7c5f05f5 100644\n--- a/kernel/bpf/diagnostics.c\n+++ b/kernel/bpf/diagnostics.c\n@@ -833,11 +833,9 @@ static void bpf_diag_source(struct bpf_verifier_env *env, u32 insn_idx, const ch\n \tlinfo = bpf_find_linfo(env-\u003eprog, insn_idx);\n \tif (btf \u0026\u0026 linfo)\n \t\tbpf_get_linfo_source(btf, linfo, \u0026src);\n-\tif (!src.file || !*src.file || !src.line || !*src.line) {\n+\tif (!src.file || !*src.file) {\n \t\tdiag_write(env, \" insn %u\\n\", insn_idx);\n-\t\tdiag_print_source_annotation(env, 0, 0, label, msg);\n-\t\tdiag_print_insn_context(env, insn_idx, disasm_lines);\n-\t\tgoto out_restore;\n+\t\tgoto out_annotation;\n \t}\n \n \tsubprog = bpf_find_containing_subprog(env, insn_idx);\n@@ -847,6 +845,8 @@ static void bpf_diag_source(struct bpf_verifier_env *env, u32 insn_idx, const ch\n \t\tdiag_write(env, \" %s @ %s:%d:%d\\n\", func, src.file, src.line_num, src.line_col);\n \telse\n \t\tdiag_write(env, \" %s:%d:%d\\n\", src.file, src.line_num, src.line_col);\n+\tif (!src.line || !*src.line)\n+\t\tgoto out_annotation;\n \n \tstart_line = src.line_num - BPF_DIAG_CONTEXT;\n \tend_line = src.line_num + BPF_DIAG_CONTEXT;\n@@ -889,7 +889,11 @@ static void bpf_diag_source(struct bpf_verifier_env *env, u32 insn_idx, const ch\n \t\t\tdiag_print_source_annotation(env, width, indent, label, msg);\n \t}\n \tdiag_print_insn_context(env, insn_idx, disasm_lines);\n+\tgoto out_restore;\n \n+out_annotation:\n+\tdiag_print_source_annotation(env, 0, 0, label, msg);\n+\tdiag_print_insn_context(env, insn_idx, disasm_lines);\n out_restore:\n \tdiag_fmt_restore(env, mark);\n }\n@@ -1083,7 +1087,9 @@ void bpf_diag_ctx_forbidden(struct bpf_verifier_env *env, u32 insn_idx,\n \tconst char *constraint, *context;\n \tu32 depth;\n \n-\tif (env-\u003ecur_state-\u003eactive_rcu_locks)\n+\tif (!env-\u003ecur_state-\u003ein_sleepable)\n+\t\tctx_kind = BPF_DIAG_CONTEXT_NONE;\n+\telse if (env-\u003ecur_state-\u003eactive_rcu_locks)\n \t\tctx_kind = BPF_DIAG_CONTEXT_RCU;\n \telse if (env-\u003ecur_state-\u003eactive_preempt_locks)\n \t\tctx_kind = BPF_DIAG_CONTEXT_PREEMPT;\n@@ -1531,8 +1537,7 @@ static void diag_record_mod(struct bpf_verifier_env *env, u32 insn_idx,\n \t} else if (diag_mod_insn_origin(env, insn_idx, \u0026target, \u0026event.mod.origin)) {\n \t\tevent.mod.origin_valid = true;\n \t}\n-\tif (old_reg \u0026\u0026 new_reg \u0026\u0026\n-\t (reason == BPF_DIAG_MOD_WRITE || reason == BPF_DIAG_MOD_SPILL) \u0026\u0026\n+\tif (old_reg \u0026\u0026 new_reg \u0026\u0026 reason == BPF_DIAG_MOD_WRITE \u0026\u0026\n \t !memcmp(\u0026event.mod.old, \u0026event.mod.new, sizeof(event.mod.old)) \u0026\u0026\n \t !event.mod.origin_valid \u0026\u0026\n \t diag_mod_keeps_lineage(env, \u0026event))\n@@ -2164,7 +2169,7 @@ static const char *diag_mod_target_desc(struct bpf_verifier_env *env,\n \tcase BPF_DIAG_MOD_TARGET_REG:\n \t\treturn bpf_diag_fmt(env, \"R%u\", target-\u003eregno);\n \tcase BPF_DIAG_MOD_TARGET_STACK_ARG:\n-\t\treturn bpf_diag_fmt(env, \"stack arg%d\", diag_stack_argno(target-\u003estack_arg));\n+\t\treturn bpf_diag_fmt(env, \"*(R11-%u)\", (target-\u003estack_arg + 1) * BPF_REG_SIZE);\n \tcase BPF_DIAG_MOD_TARGET_STACK_SLOT:\n \t\treturn bpf_diag_fmt(env, \"stack slot fp%d\", -(target-\u003espi + 1) * BPF_REG_SIZE);\n \tdefault:\ndiff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c\nindex 0bb7ee95c8bd2..77033cf3734a5 100644\n--- a/kernel/bpf/verifier.c\n+++ b/kernel/bpf/verifier.c\n@@ -2912,6 +2912,7 @@ static int add_subprogs(struct bpf_verifier_env *env)\n \tstruct bpf_subprog_info *subprog = env-\u003esubprog_info;\n \tint i, ret, insn_cnt = env-\u003eprog-\u003elen, ex_cb_insn;\n \tstruct bpf_insn *insn = env-\u003eprog-\u003einsnsi;\n+\tconst char *operation, *suggestion;\n \n \t/* Add entry function. */\n \tret = add_subprog(env, 0);\n@@ -2923,11 +2924,18 @@ static int add_subprogs(struct bpf_verifier_env *env)\n \t\t\tcontinue;\n \n \t\tif (!env-\u003ebpf_capable) {\n+\t\t\tif (bpf_pseudo_func(insn)) {\n+\t\t\t\toperation = \"BPF function reference\";\n+\t\t\t\tsuggestion = \"Load this program with the required capability, or avoid BPF function references in unprivileged programs.\";\n+\t\t\t} else {\n+\t\t\t\toperation = \"BPF-to-BPF function call\";\n+\t\t\t\tsuggestion = \"Load this program with the required capability, or avoid BPF-to-BPF function calls in unprivileged programs.\";\n+\t\t\t}\n \t\t\tverbose(env, \"loading/calling other bpf or kernel functions are allowed for CAP_BPF and CAP_SYS_ADMIN\\n\");\n \t\t\tbpf_diag_policy(\n-\t\t\t\tenv, i, \"BPF-to-BPF function call\",\n+\t\t\t\tenv, i, operation,\n \t\t\t\t\"loading or calling other BPF functions requires CAP_BPF or CAP_SYS_ADMIN\",\n-\t\t\t\t\"Load this program with the required capability, or avoid BPF-to-BPF function calls in unprivileged programs.\");\n+\t\t\t\tsuggestion);\n \t\t\treturn -EPERM;\n \t\t}\n \n@@ -2995,15 +3003,13 @@ static int add_kfuncs(struct bpf_verifier_env *env)\n \treturn 0;\n }\n \n-static int check_subprogs(struct bpf_verifier_env *env)\n+static void find_subprog_properties(struct bpf_verifier_env *env)\n {\n-\tint i, subprog_start, subprog_end, off, cur_subprog = 0;\n+\tint i, subprog_end, cur_subprog = 0;\n \tstruct bpf_subprog_info *subprog = env-\u003esubprog_info;\n \tstruct bpf_insn *insn = env-\u003eprog-\u003einsnsi;\n \tint insn_cnt = env-\u003eprog-\u003elen;\n \n-\t/* now check that all jumps are within the same subprog */\n-\tsubprog_start = subprog[cur_subprog].start;\n \tsubprog_end = subprog[cur_subprog + 1].start;\n \tfor (i = 0; i \u003c insn_cnt; i++) {\n \t\tu8 code = insn[i].code;\n@@ -3017,6 +3023,27 @@ static int check_subprogs(struct bpf_verifier_env *env)\n \t\tif (BPF_CLASS(code) == BPF_LD \u0026\u0026\n \t\t (BPF_MODE(code) == BPF_ABS || BPF_MODE(code) == BPF_IND))\n \t\t\tsubprog[cur_subprog].has_ld_abs = true;\n+\t\tif (i == subprog_end - 1) {\n+\t\t\tcur_subprog++;\n+\t\t\tif (cur_subprog \u003c env-\u003esubprog_cnt)\n+\t\t\t\tsubprog_end = subprog[cur_subprog + 1].start;\n+\t\t}\n+\t}\n+}\n+\n+static int check_subprogs(struct bpf_verifier_env *env)\n+{\n+\tint i, subprog_start, subprog_end, off, cur_subprog = 0;\n+\tstruct bpf_subprog_info *subprog = env-\u003esubprog_info;\n+\tstruct bpf_insn *insn = env-\u003eprog-\u003einsnsi;\n+\tint insn_cnt = env-\u003eprog-\u003elen;\n+\n+\t/* now check that all jumps are within the same subprog */\n+\tsubprog_start = subprog[cur_subprog].start;\n+\tsubprog_end = subprog[cur_subprog + 1].start;\n+\tfor (i = 0; i \u003c insn_cnt; i++) {\n+\t\tu8 code = insn[i].code;\n+\n \t\tif (BPF_CLASS(code) != BPF_JMP \u0026\u0026 BPF_CLASS(code) != BPF_JMP32)\n \t\t\tgoto next;\n \t\tif (BPF_OP(code) == BPF_CALL)\n@@ -3038,9 +3065,10 @@ static int check_subprogs(struct bpf_verifier_env *env)\n \t\t}\n next:\n \t\tif (i == subprog_end - 1) {\n-\t\t\t/* to avoid fall-through from one subprog into another\n+\t\t\t/*\n+\t\t\t * To avoid fall-through from one subprog into another,\n \t\t\t * the last insn of the subprog should be either exit\n-\t\t\t * or unconditional jump back or bpf_throw call\n+\t\t\t * or unconditional jump back or bpf_throw call.\n \t\t\t */\n \t\t\tif (code != (BPF_JMP | BPF_EXIT) \u0026\u0026\n \t\t\t code != (BPF_JMP32 | BPF_JA) \u0026\u0026\n@@ -3126,8 +3154,9 @@ static int sort_subprogs_topo(struct bpf_verifier_env *env)\n \t\t\t\t\tbpf_diag_program_structure(\n \t\t\t\t\t\tenv, idx, \"recursive subprogram call\",\n \t\t\t\t\t\t\"Rewrite the recursion as an explicit bounded loop, or split the logic so subprogram calls do not form a cycle.\",\n-\t\t\t\t\t\t\"This bpf2bpf call would make the subprogram call graph recursive. \"\n-\t\t\t\t\t\t\"The verifier requires a finite, acyclic call graph so it can bound stack depth and analysis.\");\n+\t\t\t\t\t\t\"The call from %s() to %s() would make the subprogram call graph recursive. \"\n+\t\t\t\t\t\t\"The verifier requires a finite, acyclic call graph so it can bound stack depth and analysis.\",\n+\t\t\t\t\t\tbpf_subprog_name(env, cur), bpf_subprog_name(env, callee));\n \t\t\t\t\tret = -EINVAL;\n \t\t\t\t\tgoto out;\n \t\t\t\t}\n@@ -3817,19 +3846,46 @@ static int mark_reg_stack_read(struct bpf_verifier_env *env,\n \treturn 0;\n }\n \n-static void bpf_diag_stack_read_uninit(struct bpf_verifier_env *env, int off, int i,\n-\t\t\t\t int size)\n+static void bpf_diag_stack_read_invalid(struct bpf_verifier_env *env, int off, int i, int size,\n+\t\t\t\t\tenum bpf_stack_slot_type type)\n {\n-\tconst char *reason;\n+\tconst char *problem, *reason, *suggestion, *kind;\n+\n+\tif (type == STACK_INVALID) {\n+\t\treason = bpf_diag_fmt(\n+\t\t\tenv, \"This rejected read uses %d bytes at stack offset %d, but byte %d in that range is uninitialized on this path. \"\n+\t\t\t\"Programs loaded with CAP_PERFMON can be allowed to read uninitialized stack bytes, but this program is being rejected without that allowance.\",\n+\t\t\tsize, off, i);\n+\t\tbpf_diag_memory(\n+\t\t\tenv, env-\u003einsn_idx, \"uninitialized stack read\", reason,\n+\t\t\t\"Initialize every byte in the stack range before reading it, adjust the offset and size so the read covers only initialized bytes, \"\n+\t\t\t\"or load with CAP_PERFMON if uninitialized stack reads are intended.\");\n+\t\treturn;\n+\t}\n \n-\treason = bpf_diag_fmt(env,\n-\t\t\t \"This rejected read uses %d bytes at stack offset %d, but byte %d in that range is uninitialized on this path. \"\n-\t\t\"Programs loaded with CAP_PERFMON can be allowed to read uninitialized stack bytes, but this program is being rejected without that allowance.\",\n-\t\tsize, off, i);\n-\tbpf_diag_memory(\n-\t\tenv, env-\u003einsn_idx, \"uninitialized stack read\", reason,\n-\t\t\"Initialize every byte in the stack range before reading it, adjust the offset and size so the read covers only initialized bytes, \"\n-\t\t\"or load with CAP_PERFMON if uninitialized stack reads are intended.\");\n+\tswitch (type) {\n+\tcase STACK_DYNPTR:\n+\t\tkind = \"dynptr\";\n+\t\tsuggestion = \"Use dynptr helpers or kfuncs to access the object represented by the dynptr instead of reading the dynptr state directly.\";\n+\t\tbreak;\n+\tcase STACK_ITER:\n+\t\tkind = \"iterator\";\n+\t\tsuggestion = \"Use iterator kfuncs to advance or destroy the iterator instead of reading its state directly.\";\n+\t\tbreak;\n+\tcase STACK_IRQ_FLAG:\n+\t\tkind = \"IRQ flag\";\n+\t\tsuggestion = \"Pass the saved IRQ flag to the matching restore kfunc instead of reading its state directly.\";\n+\t\tbreak;\n+\tdefault:\n+\t\treturn;\n+\t}\n+\n+\tproblem = bpf_diag_fmt(env, \"direct read of %s stack state\", kind);\n+\treason = bpf_diag_fmt(\n+\t\tenv, \"This rejected read uses %d bytes at stack offset %d, but byte %d in that range belongs to verifier-managed %s state. \"\n+\t\t\"This state has an opaque representation that BPF programs cannot read directly.\",\n+\t\tsize, off, i, kind);\n+\tbpf_diag_memory(env, env-\u003einsn_idx, problem, reason, suggestion);\n }\n \n /* Read the stack at 'off' and put the results into the register indicated by\n@@ -3921,7 +3977,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,\n \t\t\t\t\t} else {\n \t\t\t\t\t\tverbose(env, \"invalid read from stack off %d+%d size %d\\n\",\n \t\t\t\t\t\t\toff, i, size);\n-\t\t\t\t\t\tbpf_diag_stack_read_uninit(env, off, i, size);\n+\t\t\t\t\t\tbpf_diag_stack_read_invalid(env, off, i, size, type);\n \t\t\t\t\t}\n \t\t\t\t\treturn -EACCES;\n \t\t\t\t}\n@@ -3980,7 +4036,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,\n \t\t\t} else {\n \t\t\t\tverbose(env, \"invalid read from stack off %d+%d size %d\\n\",\n \t\t\t\t\toff, i, size);\n-\t\t\t\tbpf_diag_stack_read_uninit(env, off, i, size);\n+\t\t\t\tbpf_diag_stack_read_invalid(env, off, i, size, type);\n \t\t\t}\n \t\t\treturn -EACCES;\n \t\t}\n@@ -4079,13 +4135,13 @@ static int check_stack_read(struct bpf_verifier_env *env,\n \t\ttnum_strn(tn_buf, sizeof(tn_buf), reg-\u003evar_off);\n \t\tverbose(env, \"variable offset stack pointer cannot be passed into helper function; var_off=%s off=%d size=%d\\n\",\n \t\t\ttn_buf, off, size);\n-\t\treason = bpf_diag_fmt(env,\n-\t\t\t\t \"The helper would access the stack through variable offset %s plus fixed offset %d and size %d. \"\n-\t\t\t\"Helper stack memory arguments require a constant stack offset and a precise initialized range.\",\n+\t\treason = bpf_diag_fmt(\n+\t\t\tenv, \"The atomic operation would access the stack through variable offset %s plus fixed offset %d and size %d. \"\n+\t\t\t\"Atomic stack operations require a constant stack offset and a precise initialized range.\",\n \t\t\ttn_buf, off, size);\n \t\tbpf_diag_memory(\n-\t\t\tenv, env-\u003einsn_idx, \"variable stack access\", reason,\n-\t\t\t\"Use a fixed stack offset for helper memory arguments, or copy the needed bytes into a fixed stack slot first.\");\n+\t\t\tenv, env-\u003einsn_idx, \"variable-offset atomic stack access\", reason,\n+\t\t\t\"Use a fixed stack offset for the atomic operation, selecting the target stack slot on separate control-flow paths if necessary.\");\n \t\treturn -EACCES;\n \t}\n \t/* Variable offset is prohibited for unprivileged mode for simplicity\n@@ -9882,7 +9938,7 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n \t\t\t\tsub_name, non_sleepable_context_description(env));\n \t\t\toperation = bpf_diag_fmt(env, \"sleepable global function %s()\", sub_name);\n \t\t\tbpf_diag_ctx_forbidden(env, *insn_idx, operation,\n-\t\t\t\t\"Move the call outside the critical section, or use a non-sleepable function.\");\n+\t\t\t\t\"Call the function from a sleepable program outside any critical section, or use a non-sleepable function.\");\n \t\t\treturn -EINVAL;\n \t\t}\n \n@@ -10701,6 +10757,8 @@ static inline bool in_sleepable_context(struct bpf_verifier_env *env)\n \n static const char *non_sleepable_context_description(struct bpf_verifier_env *env)\n {\n+\tif (!in_sleepable(env))\n+\t\treturn \"non-sleepable prog\";\n \tif (env-\u003ecur_state-\u003eactive_rcu_locks)\n \t\treturn \"rcu_read_lock region\";\n \tif (env-\u003ecur_state-\u003eactive_preempt_locks)\n@@ -10709,7 +10767,7 @@ static const char *non_sleepable_context_description(struct bpf_verifier_env *en\n \t\treturn \"IRQ-disabled region\";\n \tif (env-\u003ecur_state-\u003eactive_locks)\n \t\treturn \"lock region\";\n-\treturn \"non-sleepable prog\";\n+\treturn \"non-sleepable context\";\n }\n \n static int release_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,\n@@ -10805,7 +10863,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn\n \t\toperation = bpf_diag_fmt(env, \"sleepable helper %s#%d\",\n \t\t\t\t\t func_id_name(func_id), func_id);\n \t\tbpf_diag_ctx_forbidden(env, insn_idx, operation,\n-\t\t\t\"Move the helper call outside the critical section, or use a non-sleepable helper.\");\n+\t\t\t\"Call the helper from a sleepable program outside any critical section, or use a non-sleepable helper.\");\n \t\treturn -EINVAL;\n \t}\n \n@@ -10994,13 +11052,13 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn\n \n \t/* reset caller saved regs */\n \tbpf_diag_record_caller_saved(env, regs);\n+\tbpf_diag_mod_begin(env, \u0026regs[BPF_REG_0], NULL, BPF_DIAG_MOD_WRITE);\n \tfor (i = 0; i \u003c CALLER_SAVED_REGS; i++) {\n \t\tbpf_mark_reg_not_init(env, \u0026regs[caller_saved[i]]);\n \t\tcheck_reg_arg(env, caller_saved[i], DST_OP_NO_MARK);\n \t}\n \tinvalidate_outgoing_stack_args(env, cur_func(env));\n \n-\tbpf_diag_mod_begin(env, \u0026regs[BPF_REG_0], NULL, BPF_DIAG_MOD_WRITE);\n \t/* update return register (already marked as written above) */\n \tret_type = fn-\u003eret_type;\n \tret_flag = type_flag(ret_type);\n@@ -12623,12 +12681,12 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me\n \t\t !type_may_be_null(kf_arg_type)) {\n \t\t\tconst char *expected_type;\n \n-\t\t\texpected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);\n+\t\t\texpected_type = bpf_diag_fmt_btf_type(env, btf, args[i].type);\n \t\t\tverbose(env, \"Possibly NULL pointer passed to trusted %s\\n\",\n \t\t\t\treg_arg_name(env, argno));\n \t\t\tbpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,\n \t\t\t\t\t \"Add a NULL check and call the kfunc only on the non-NULL path.\",\n-\t\t\t\t\t \"the pointer may be NULL, but this kfunc requires a non-NULL pointer to %s\",\n+\t\t\t\t\t \"the pointer may be NULL, but this kfunc requires a non-NULL value of type %s\",\n \t\t\t\t\t expected_type);\n \t\t\treturn -EACCES;\n \t\t}\n@@ -13065,8 +13123,14 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me\n \t\t\tbreak;\n \t\tcase KF_ARG_CONST_MEM_SIZE:\n \t\t\tret = process_const_arg(env, reg, argno, meta);\n-\t\t\tif (ret \u003c 0)\n+\t\t\tif (ret \u003c 0) {\n+\t\t\t\tif (ret == -EINVAL)\n+\t\t\t\t\tbpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,\n+\t\t\t\t\t\t\t \"Pass a compile-time constant or a value the verifier can prove is constant at this call.\",\n+\t\t\t\t\t\t\t \"the kfunc requires this memory size to be a verifier-known constant, but %s is variable on this path\",\n+\t\t\t\t\t\t\t reg_arg_name(env, argno));\n \t\t\t\treturn ret;\n+\t\t\t}\n \t\t\tfallthrough;\n \t\tcase KF_ARG_MEM_SIZE:\n \t\t{\n@@ -13130,15 +13194,13 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me\n \t\t\tbreak;\n \t\tcase KF_ARG_PTR_TO_REFCOUNTED_KPTR:\n \t\t\tif (!type_is_ptr_alloc_obj(reg-\u003etype)) {\n-\t\t\t\tconst char *expected_type;\n-\n-\t\t\t\texpected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);\n \t\t\t\tverbose(env, \"%s is neither owning or non-owning ref\\n\",\n \t\t\t\t\treg_arg_name(env, argno));\n \t\t\t\tbpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,\n-\t\t\t\t\t\t \"Pass a pointer returned by the matching BPF object allocation or lookup operation for this kfunc.\",\n-\t\t\t\t\t\t \"the kfunc expects a pointer to BPF-managed refcounted object type %s, but this argument is not such an object pointer\",\n-\t\t\t\t\t\t expected_type);\n+\t\t\t\t\t\t \"Pass an owning or non-owning pointer to a BPF-managed object containing a bpf_refcount field.\",\n+\t\t\t\t\t\t \"the kfunc expects a pointer to a BPF-managed refcounted object, but %s is %s\",\n+\t\t\t\t\t\t reg_arg_name(env, argno),\n+\t\t\t\t\t\t bpf_diag_reg_type_plain(env, reg-\u003etype));\n \t\t\t\treturn -EINVAL;\n \t\t\t}\n \t\t\tif (!type_is_non_owning_ref(reg-\u003etype))\n@@ -13726,11 +13788,20 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n \t}\n \n \tsleepable = bpf_is_kfunc_sleepable(\u0026meta);\n-\tif (sleepable \u0026\u0026 !in_sleepable(env)) {\n-\t\tverbose(env, \"program must be sleepable to call sleepable kfunc %s\\n\", func_name);\n+\tif (sleepable \u0026\u0026 !in_sleepable_context(env)) {\n+\t\tconst char *suggestion;\n+\n+\t\tif (in_sleepable(env)) {\n+\t\t\tverbose(env, \"kernel func %s is sleepable within %s\\n\",\n+\t\t\t\tfunc_name, non_sleepable_context_description(env));\n+\t\t\tsuggestion = \"Move the kfunc call outside the critical section, or use a non-sleepable kfunc.\";\n+\t\t} else {\n+\t\t\tverbose(env, \"program must be sleepable to call sleepable kfunc %s\\n\",\n+\t\t\t\tfunc_name);\n+\t\t\tsuggestion = \"Mark the program sleepable if the program type allows it, or use a non-sleepable kfunc.\";\n+\t\t}\n \t\toperation = bpf_diag_fmt(env, \"sleepable kfunc %s\", func_name);\n-\t\tbpf_diag_ctx_forbidden(env, insn_idx, operation,\n-\t\t\t\"Mark the program sleepable if the program type allows it, or use a non-sleepable kfunc.\");\n+\t\tbpf_diag_ctx_forbidden(env, insn_idx, operation, suggestion);\n \t\treturn -EACCES;\n \t}\n \n@@ -13830,15 +13901,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n \t\t\tinvalidate_rcu_protected_refs(env);\n \t}\n \n-\tif (sleepable \u0026\u0026 !in_sleepable_context(env)) {\n-\t\tverbose(env, \"kernel func %s is sleepable within %s\\n\",\n-\t\t\tfunc_name, non_sleepable_context_description(env));\n-\t\toperation = bpf_diag_fmt(env, \"sleepable kfunc %s\", func_name);\n-\t\tbpf_diag_ctx_forbidden(env, insn_idx, operation,\n-\t\t\t\"Move the kfunc call outside the critical section, or use a non-sleepable kfunc.\");\n-\t\treturn -EACCES;\n-\t}\n-\n \tif (in_rbtree_lock_required_cb(env) \u0026\u0026 (rcu_lock || rcu_unlock)) {\n \t\tverbose(env, \"Calling bpf_rcu_read_{lock,unlock} in unnecessary rbtree callback\\n\");\n \t\treturn -EACCES;\n@@ -21088,17 +21150,19 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\n \tif (ret \u003c 0)\n \t\tgoto skip_full_check;\n \n-\t/* Discover all subprograms before validating their layout and BTF. */\n+\t/* Discover all subprograms and collect the properties needed by BTF validation. */\n \tret = add_subprogs(env);\n \tif (ret \u003c 0)\n \t\tgoto skip_full_check;\n \n-\tret = check_subprogs(env);\n+\tfind_subprog_properties(env);\n+\n+\t/* Validate BTF and apply CO-RE before reporting subprogram layout errors. */\n+\tret = bpf_check_btf_info(env, attr, uattr);\n \tif (ret \u003c 0)\n \t\tgoto skip_full_check;\n \n-\t/* Validate BTF against the complete subprogram layout and apply CO-RE. */\n-\tret = bpf_check_btf_info(env, attr, uattr);\n+\tret = check_subprogs(env);\n \tif (ret \u003c 0)\n \t\tgoto skip_full_check;\n \ndiff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c\nindex beaa73dc35f5d..9418dfe4d7b7f 100644\n--- a/tools/testing/selftests/bpf/progs/dynptr_fail.c\n+++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c\n@@ -560,6 +560,9 @@ int global(void *ctx)\n /* A direct read should fail */\n SEC(\"?raw_tp\")\n __failure __msg(\"invalid read from stack\")\n+__msg(\"Verification failed: Memory Safety: Direct read of dynptr stack state\")\n+__msg(\"verifier-managed dynptr state\")\n+__msg(\"Use dynptr helpers or kfuncs\")\n int invalid_read1(void *ctx)\n {\n \tstruct bpf_dynptr ptr;\n@@ -1590,6 +1593,7 @@ __u32 hdr_size = sizeof(struct ethhdr);\n /* Can't pass in variable-sized len to bpf_dynptr_slice */\n SEC(\"?tc\")\n __failure __msg(\"must be a known constant\")\n+__msg(\"requires this memory size to be a verifier-known constant\")\n int dynptr_slice_var_len1(struct __sk_buff *skb)\n {\n \tstruct bpf_dynptr ptr;\n@@ -1609,6 +1613,7 @@ int dynptr_slice_var_len1(struct __sk_buff *skb)\n /* Can't pass in variable-sized len to bpf_dynptr_slice */\n SEC(\"?tc\")\n __failure __msg(\"must be a known constant\")\n+__msg(\"requires this memory size to be a verifier-known constant\")\n int dynptr_slice_var_len2(struct __sk_buff *skb)\n {\n \tchar buffer[sizeof(struct ethhdr)] = {};\ndiff --git a/tools/testing/selftests/bpf/progs/irq.c b/tools/testing/selftests/bpf/progs/irq.c\nindex a4a007866a332..8d2b50d11fa4e 100644\n--- a/tools/testing/selftests/bpf/progs/irq.c\n+++ b/tools/testing/selftests/bpf/progs/irq.c\n@@ -14,6 +14,19 @@ extern int bpf_copy_from_user_str(void *dst, u32 dst__sz, const void *unsafe_ptr\n struct bpf_res_spin_lock lockA __hidden SEC(\".data.A\");\n struct bpf_res_spin_lock lockB __hidden SEC(\".data.B\");\n \n+SEC(\"?tc\")\n+__failure __msg(\"invalid read from stack\")\n+__msg(\"Verification failed: Memory Safety: Direct read of IRQ flag stack state\")\n+__msg(\"verifier-managed IRQ flag state\")\n+__msg(\"Pass the saved IRQ flag to the matching restore kfunc\")\n+int irq_flag_direct_read(struct __sk_buff *ctx)\n+{\n+\tunsigned long flags;\n+\n+\tbpf_local_irq_save(\u0026flags);\n+\treturn flags;\n+}\n+\n SEC(\"?tc\")\n __failure __msg(\"R1 doesn't point to an irq flag on stack\")\n int irq_save_bad_arg(struct __sk_buff *ctx)\ndiff --git a/tools/testing/selftests/bpf/progs/iters_state_safety.c b/tools/testing/selftests/bpf/progs/iters_state_safety.c\nindex 646026430e9b5..4723ae578e534 100644\n--- a/tools/testing/selftests/bpf/progs/iters_state_safety.c\n+++ b/tools/testing/selftests/bpf/progs/iters_state_safety.c\n@@ -332,6 +332,9 @@ int next_after_destroy_fail(void *ctx)\n \n SEC(\"?raw_tp\")\n __failure __msg(\"invalid read from stack\")\n+__msg(\"Verification failed: Memory Safety: Direct read of iterator stack state\")\n+__msg(\"verifier-managed iterator state\")\n+__msg(\"Use iterator kfuncs\")\n int __naked read_from_iter_slot_fail(void)\n {\n \tasm volatile (\ndiff --git a/tools/testing/selftests/bpf/progs/preempt_lock.c b/tools/testing/selftests/bpf/progs/preempt_lock.c\nindex 6d5fce7e6ffc9..83c911494a34c 100644\n--- a/tools/testing/selftests/bpf/progs/preempt_lock.c\n+++ b/tools/testing/selftests/bpf/progs/preempt_lock.c\n@@ -6,6 +6,8 @@\n #include \"bpf_experimental.h\"\n \n extern int bpf_copy_from_user_str(void *dst, u32 dst__sz, const void *unsafe_ptr__ign, u64 flags) __weak __ksym;\n+extern void bpf_rcu_read_lock(void) __ksym;\n+extern void bpf_rcu_read_unlock(void) __ksym;\n \n SEC(\"?tc\")\n __failure __msg(\"BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region\")\n@@ -127,6 +129,19 @@ int preempt_sleepable_kfunc(void *ctx)\n \treturn 0;\n }\n \n+SEC(\"?fentry/\" SYS_PREFIX \"sys_getpgid\")\n+__failure __msg(\"program must be sleepable to call sleepable kfunc bpf_copy_from_user_str\")\n+__msg(\"cannot be used in non-sleepable program\")\n+int non_sleepable_kfunc_in_rcu(void *ctx)\n+{\n+\tu32 data;\n+\n+\tbpf_rcu_read_lock();\n+\tbpf_copy_from_user_str(\u0026data, sizeof(data), NULL, 0);\n+\tbpf_rcu_read_unlock();\n+\treturn 0;\n+}\n+\n int __noinline preempt_global_subprog(void)\n {\n \tpreempt_balance_subprog();\ndiff --git a/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c b/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c\nindex 024ef2aae2008..eaaed0859f946 100644\n--- a/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c\n+++ b/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c\n@@ -63,6 +63,7 @@ long rbtree_refcounted_node_ref_escapes(void *ctx)\n \n SEC(\"?tc\")\n __failure __msg(\"Possibly NULL pointer passed to trusted R1\")\n+__msg(\"requires a non-NULL value of type (void *)\")\n long refcount_acquire_maybe_null(void *ctx)\n {\n \tstruct node_acquire *n, *m;\n@@ -80,6 +81,14 @@ long refcount_acquire_maybe_null(void *ctx)\n \treturn 0;\n }\n \n+SEC(\"?tc\")\n+__failure __msg(\"R1 is neither owning or non-owning ref\")\n+__msg(\"expects a pointer to a BPF-managed refcounted object, but R1 is a context pointer\")\n+long refcount_acquire_non_object(void *ctx)\n+{\n+\treturn bpf_refcount_acquire(ctx) != NULL;\n+}\n+\n SEC(\"?tc\")\n __failure __msg(\"Unreleased reference id=3 alloc_insn={{[0-9]+}}\")\n long rbtree_refcounted_node_ref_escapes_owning_input(void *ctx)\ndiff --git a/tools/testing/selftests/bpf/progs/verifier_cfg.c b/tools/testing/selftests/bpf/progs/verifier_cfg.c\nindex c1f55e1d80a42..4416d915cdf38 100644\n--- a/tools/testing/selftests/bpf/progs/verifier_cfg.c\n+++ b/tools/testing/selftests/bpf/progs/verifier_cfg.c\n@@ -55,6 +55,46 @@ __naked void out_of_range_jump2(void)\n \"\t::: __clobber_all);\n }\n \n+static __naked __noinline __used int cross_subprog_target(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tr0 = 0;\t\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t::: __clobber_all);\n+}\n+\n+SEC(\"socket\")\n+__description(\"jump across subprogram boundary\")\n+__failure __msg(\"jump out of range from insn 1\")\n+__msg(\"jump_across_subprog_boundary @ verifier_cfg.c\")\n+__naked void jump_across_subprog_boundary(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tcall cross_subprog_target;\t\t\t\\\n+\tgoto +1;\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t::: __clobber_all);\n+}\n+\n+static __naked __noinline __used int fallthrough_subprog(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tr0 = 0;\t\t\t\t\t\t\\\n+\"\t::: __clobber_all);\n+}\n+\n+SEC(\"socket\")\n+__description(\"subprogram fallthrough\")\n+__failure __msg(\"last insn is not an exit or jmp\")\n+__msg(\"fallthrough_subprog @ verifier_cfg.c\")\n+__naked void subprog_fallthrough(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tcall fallthrough_subprog;\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t::: __clobber_all);\n+}\n+\n SEC(\"socket\")\n __description(\"loop (back-edge)\")\n __failure __msg(\"unreachable insn 1\")\ndiff --git a/tools/testing/selftests/bpf/progs/verifier_gotox.c b/tools/testing/selftests/bpf/progs/verifier_gotox.c\nindex 5b18c9a27717b..a835a48710213 100644\n--- a/tools/testing/selftests/bpf/progs/verifier_gotox.c\n+++ b/tools/testing/selftests/bpf/progs/verifier_gotox.c\n@@ -53,9 +53,11 @@ DEFINE_SIMPLE_JUMP_TABLE_PROG(reserved_field_non_zero_imm, BPF_REG_0, 0, 1, __fa\n */\n SEC(\"socket\")\n __failure __msg(\"no jump tables found for subprog starting at 0\")\n+__msg(\"\u003e\u003e\u003e 1 | (0d) gotox r0\")\n __naked void jump_table_no_jump_table(void)\n {\n \tasm volatile (\"\t\t\t\t\t\t\\\n+\tr0 = 0;\t\t\t\t\t\t\t\\\n \t.8byte %[gotox_r0];\t\t\t\t\t\\\n \tr0 = 1;\t\t\t\t\t\t\t\\\n \texit;\t\t\t\t\t\t\t\\\ndiff --git a/tools/testing/selftests/bpf/progs/verifier_loops1.c b/tools/testing/selftests/bpf/progs/verifier_loops1.c\nindex d248ce877f14e..d1ec19b7f66b2 100644\n--- a/tools/testing/selftests/bpf/progs/verifier_loops1.c\n+++ b/tools/testing/selftests/bpf/progs/verifier_loops1.c\n@@ -139,6 +139,7 @@ SEC(\"tracepoint\")\n __description(\"bounded recursion\")\n __failure\n __msg(\"recursive call from\")\n+__msg(\"The call from bounded_recursion__1() to bounded_recursion__1() would make\")\n __naked void bounded_recursion(void)\n {\n \tasm volatile (\"\t\t\t\t\t\\\ndiff --git a/tools/testing/selftests/bpf/progs/verifier_unpriv.c b/tools/testing/selftests/bpf/progs/verifier_unpriv.c\nindex 42de5cff7e52a..3069e70fbcbdc 100644\n--- a/tools/testing/selftests/bpf/progs/verifier_unpriv.c\n+++ b/tools/testing/selftests/bpf/progs/verifier_unpriv.c\n@@ -96,6 +96,24 @@ __naked void pseudo_btf_id_log_masks_address(void)\n \t: __clobber_all);\n }\n \n+static int pseudo_func_callback(__u32 index, void *ctx)\n+{\n+\treturn 0;\n+}\n+\n+SEC(\"socket\")\n+__description(\"unpriv: pseudo function policy diagnostic\")\n+__success __failure_unpriv\n+__msg_unpriv(\"loading/calling other bpf or kernel functions\")\n+__not_msg_unpriv(\"BPF-to-BPF function call\")\n+__msg_unpriv(\"policy check failed for BPF function reference\")\n+__msg_unpriv(\"avoid BPF function references in unprivileged\")\n+int unpriv_pseudo_func_policy(void *ctx)\n+{\n+\tbpf_loop(1, pseudo_func_callback, NULL, 0);\n+\treturn 0;\n+}\n+\n SEC(\"socket\")\n __description(\"unpriv: return pointer\")\n __success __failure_unpriv __msg_unpriv(\"R0 leaks addr\")\ndiff --git a/tools/testing/selftests/bpf/progs/verifier_xadd.c b/tools/testing/selftests/bpf/progs/verifier_xadd.c\nindex 05a0a55adb451..f2430b9a02184 100644\n--- a/tools/testing/selftests/bpf/progs/verifier_xadd.c\n+++ b/tools/testing/selftests/bpf/progs/verifier_xadd.c\n@@ -121,4 +121,31 @@ l0_%=:\tr0 = 42;\t\t\t\t\t\\\n \"\t::: __clobber_all);\n }\n \n+SEC(\"tc\")\n+__description(\"xadd with variable stack offset\")\n+__failure\n+__msg(\"variable offset stack pointer cannot be passed into helper function\")\n+__msg(\"Verification failed: Memory Safety: Variable-offset atomic stack access\")\n+__msg(\"The atomic operation would access the stack\")\n+__msg(\"Use a fixed stack offset for the atomic operation\")\n+__naked void xadd_variable_stack_offset(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tr1 = 0;\t\t\t\t\t\t\\\n+\t*(u64 *)(r10 - 16) = r1;\t\t\t\\\n+\t*(u64 *)(r10 - 8) = r1;\t\t\t\t\\\n+\tcall %[bpf_get_prandom_u32];\t\t\t\\\n+\tr0 \u0026= 8;\t\t\t\t\t\\\n+\tr1 = r10;\t\t\t\t\t\\\n+\tr1 += -16;\t\t\t\t\t\\\n+\tr1 += r0;\t\t\t\t\t\\\n+\tr2 = 1;\t\t\t\t\t\t\\\n+\tlock *(u64 *)(r1 + 0) += r2;\t\t\t\\\n+\tr0 = 0;\t\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t:\n+\t: __imm(bpf_get_prandom_u32)\n+\t: __clobber_all);\n+}\n+\n char _license[] SEC(\"license\") = \"GPL\";\n"
} |
| 2/1 | 2026/08/16 02:13 | agent | patch-evaluator |
1mModel:best-expensive Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch is entirely focused on improving BPF verifier diagnostics. It updates error messages, adds hints, and reorders the BTF validation and subprogram layout checks to report BTF/CO-RE errors before subprogram layout errors. These are purely decorative and diagnostic changes that do not alter the set of accepted BPF programs or the execution flow of valid programs.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a '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: For your convenience, here is the diff of the changes:
commit a4c88729b476347cbbc1f15e4c82945b417658f9
Author: syz-cluster <triage@syzkaller.com>
Date: Sun Aug 16 02:13:14 2026 +0000
syz-cluster: applied patch under review
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 5b9d767895c91..6967d48bba49d 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -1169,19 +1169,19 @@ static const char *btf_show_name(struct btf_show *show)
id = t->type;
break;
default:
- id = 0;
- break;
+ goto resolved;
}
+ t = btf_type_skip_qualifiers(show->btf, id);
if (!id)
break;
- t = btf_type_skip_qualifiers(show->btf, id);
}
/* We may not be able to represent this type; bail to be safe */
if (i == BTF_SHOW_MAX_ITER)
return "";
+resolved:
if (!name)
- name = btf_name_by_offset(show->btf, t->name_off);
+ name = btf_type_is_void(t) ? "void" : btf_name_by_offset(show->btf, t->name_off);
switch (BTF_INFO_KIND(t->info)) {
case BTF_KIND_STRUCT:
diff --git a/kernel/bpf/cfg.c b/kernel/bpf/cfg.c
index 0f13c13f41337..95c59f6cf70ae 100644
--- a/kernel/bpf/cfg.c
+++ b/kernel/bpf/cfg.c
@@ -287,7 +287,7 @@ static struct bpf_iarray *jt_from_map(struct bpf_map *map)
* combined jump table in jt->items (allocated with kvcalloc)
*/
static struct bpf_iarray *jt_from_subprog(struct bpf_verifier_env *env,
- int subprog_start, int subprog_end)
+ int insn_idx, int subprog_start, int subprog_end)
{
struct bpf_iarray *jt = NULL;
struct bpf_map *map;
@@ -327,7 +327,7 @@ static struct bpf_iarray *jt_from_subprog(struct bpf_verifier_env *env,
if (!jt) {
verbose(env, "no jump tables found for subprog starting at %u\n", subprog_start);
bpf_diag_program_structure(
- env, subprog_start, "missing jump table",
+ env, insn_idx, "missing jump table",
"Make sure subprograms containing gotox instructions are accompanied by jump tables referencing these subprograms.",
"No jump table was found for the subprogram that starts at instruction %u.",
subprog_start);
@@ -349,7 +349,7 @@ create_jt(int t, struct bpf_verifier_env *env)
subprog = bpf_find_containing_subprog(env, t);
subprog_start = subprog->start;
subprog_end = (subprog + 1)->start;
- jt = jt_from_subprog(env, subprog_start, subprog_end);
+ jt = jt_from_subprog(env, t, subprog_start, subprog_end);
if (IS_ERR(jt))
return jt;
diff --git a/kernel/bpf/diagnostics.c b/kernel/bpf/diagnostics.c
index 33b7d9e8e2c3a..44d0b7c5f05f5 100644
--- a/kernel/bpf/diagnostics.c
+++ b/kernel/bpf/diagnostics.c
@@ -833,11 +833,9 @@ static void bpf_diag_source(struct bpf_verifier_env *env, u32 insn_idx, const ch
linfo = bpf_find_linfo(env->prog, insn_idx);
if (btf && linfo)
bpf_get_linfo_source(btf, linfo, &src);
- if (!src.file || !*src.file || !src.line || !*src.line) {
+ if (!src.file || !*src.file) {
diag_write(env, " insn %u\n", insn_idx);
- diag_print_source_annotation(env, 0, 0, label, msg);
- diag_print_insn_context(env, insn_idx, disasm_lines);
- goto out_restore;
+ goto out_annotation;
}
subprog = bpf_find_containing_subprog(env, insn_idx);
@@ -847,6 +845,8 @@ static void bpf_diag_source(struct bpf_verifier_env *env, u32 insn_idx, const ch
diag_write(env, " %s @ %s:%d:%d\n", func, src.file, src.line_num, src.line_col);
else
diag_write(env, " %s:%d:%d\n", src.file, src.line_num, src.line_col);
+ if (!src.line || !*src.line)
+ goto out_annotation;
start_line = src.line_num - BPF_DIAG_CONTEXT;
end_line = src.line_num + BPF_DIAG_CONTEXT;
@@ -889,7 +889,11 @@ static void bpf_diag_source(struct bpf_verifier_env *env, u32 insn_idx, const ch
diag_print_source_annotation(env, width, indent, label, msg);
}
diag_print_insn_context(env, insn_idx, disasm_lines);
+ goto out_restore;
+out_annotation:
+ diag_print_source_annotation(env, 0, 0, label, msg);
+ diag_print_insn_context(env, insn_idx, disasm_lines);
out_restore:
diag_fmt_restore(env, mark);
}
@@ -1083,7 +1087,9 @@ void bpf_diag_ctx_forbidden(struct bpf_verifier_env *env, u32 insn_idx,
const char *constraint, *context;
u32 depth;
- if (env->cur_state->active_rcu_locks)
+ if (!env->cur_state->in_sleepable)
+ ctx_kind = BPF_DIAG_CONTEXT_NONE;
+ else if (env->cur_state->active_rcu_locks)
ctx_kind = BPF_DIAG_CONTEXT_RCU;
else if (env->cur_state->active_preempt_locks)
ctx_kind = BPF_DIAG_CONTEXT_PREEMPT;
@@ -1531,8 +1537,7 @@ static void diag_record_mod(struct bpf_verifier_env *env, u32 insn_idx,
} else if (diag_mod_insn_origin(env, insn_idx, &target, &event.mod.origin)) {
event.mod.origin_valid = true;
}
- if (old_reg && new_reg &&
- (reason == BPF_DIAG_MOD_WRITE || reason == BPF_DIAG_MOD_SPILL) &&
+ if (old_reg && new_reg && reason == BPF_DIAG_MOD_WRITE &&
!memcmp(&event.mod.old, &event.mod.new, sizeof(event.mod.old)) &&
!event.mod.origin_valid &&
diag_mod_keeps_lineage(env, &event))
@@ -2164,7 +2169,7 @@ static const char *diag_mod_target_desc(struct bpf_verifier_env *env,
case BPF_DIAG_MOD_TARGET_REG:
return bpf_diag_fmt(env, "R%u", target->regno);
case BPF_DIAG_MOD_TARGET_STACK_ARG:
- return bpf_diag_fmt(env, "stack arg%d", diag_stack_argno(target->stack_arg));
+ return bpf_diag_fmt(env, "*(R11-%u)", (target->stack_arg + 1) * BPF_REG_SIZE);
case BPF_DIAG_MOD_TARGET_STACK_SLOT:
return bpf_diag_fmt(env, "stack slot fp%d", -(target->spi + 1) * BPF_REG_SIZE);
default:
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 0bb7ee95c8bd2..77033cf3734a5 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -2912,6 +2912,7 @@ static int add_subprogs(struct bpf_verifier_env *env)
struct bpf_subprog_info *subprog = env->subprog_info;
int i, ret, insn_cnt = env->prog->len, ex_cb_insn;
struct bpf_insn *insn = env->prog->insnsi;
+ const char *operation, *suggestion;
/* Add entry function. */
ret = add_subprog(env, 0);
@@ -2923,11 +2924,18 @@ static int add_subprogs(struct bpf_verifier_env *env)
continue;
if (!env->bpf_capable) {
+ if (bpf_pseudo_func(insn)) {
+ operation = "BPF function reference";
+ suggestion = "Load this program with the required capability, or avoid BPF function references in unprivileged programs.";
+ } else {
+ operation = "BPF-to-BPF function call";
+ suggestion = "Load this program with the required capability, or avoid BPF-to-BPF function calls in unprivileged programs.";
+ }
verbose(env, "loading/calling other bpf or kernel functions are allowed for CAP_BPF and CAP_SYS_ADMIN\n");
bpf_diag_policy(
- env, i, "BPF-to-BPF function call",
+ env, i, operation,
"loading or calling other BPF functions requires CAP_BPF or CAP_SYS_ADMIN",
- "Load this program with the required capability, or avoid BPF-to-BPF function calls in unprivileged programs.");
+ suggestion);
return -EPERM;
}
@@ -2995,15 +3003,13 @@ static int add_kfuncs(struct bpf_verifier_env *env)
return 0;
}
-static int check_subprogs(struct bpf_verifier_env *env)
+static void find_subprog_properties(struct bpf_verifier_env *env)
{
- int i, subprog_start, subprog_end, off, cur_subprog = 0;
+ int i, subprog_end, cur_subprog = 0;
struct bpf_subprog_info *subprog = env->subprog_info;
struct bpf_insn *insn = env->prog->insnsi;
int insn_cnt = env->prog->len;
- /* now check that all jumps are within the same subprog */
- subprog_start = subprog[cur_subprog].start;
subprog_end = subprog[cur_subprog + 1].start;
for (i = 0; i < insn_cnt; i++) {
u8 code = insn[i].code;
@@ -3017,6 +3023,27 @@ static int check_subprogs(struct bpf_verifier_env *env)
if (BPF_CLASS(code) == BPF_LD &&
(BPF_MODE(code) == BPF_ABS || BPF_MODE(code) == BPF_IND))
subprog[cur_subprog].has_ld_abs = true;
+ if (i == subprog_end - 1) {
+ cur_subprog++;
+ if (cur_subprog < env->subprog_cnt)
+ subprog_end = subprog[cur_subprog + 1].start;
+ }
+ }
+}
+
+static int check_subprogs(struct bpf_verifier_env *env)
+{
+ int i, subprog_start, subprog_end, off, cur_subprog = 0;
+ struct bpf_subprog_info *subprog = env->subprog_info;
+ struct bpf_insn *insn = env->prog->insnsi;
+ int insn_cnt = env->prog->len;
+
+ /* now check that all jumps are within the same subprog */
+ subprog_start = subprog[cur_subprog].start;
+ subprog_end = subprog[cur_subprog + 1].start;
+ for (i = 0; i < insn_cnt; i++) {
+ u8 code = insn[i].code;
+
if (BPF_CLASS(code) != BPF_JMP && BPF_CLASS(code) != BPF_JMP32)
goto next;
if (BPF_OP(code) == BPF_CALL)
@@ -3038,9 +3065,10 @@ static int check_subprogs(struct bpf_verifier_env *env)
}
next:
if (i == subprog_end - 1) {
- /* to avoid fall-through from one subprog into another
+ /*
+ * To avoid fall-through from one subprog into another,
* the last insn of the subprog should be either exit
- * or unconditional jump back or bpf_throw call
+ * or unconditional jump back or bpf_throw call.
*/
if (code != (BPF_JMP | BPF_EXIT) &&
code != (BPF_JMP32 | BPF_JA) &&
@@ -3126,8 +3154,9 @@ static int sort_subprogs_topo(struct bpf_verifier_env *env)
bpf_diag_program_structure(
env, idx, "recursive subprogram call",
"Rewrite the recursion as an explicit bounded loop, or split the logic so subprogram calls do not form a cycle.",
- "This bpf2bpf call would make the subprogram call graph recursive. "
- "The verifier requires a finite, acyclic call graph so it can bound stack depth and analysis.");
+ "The call from %s() to %s() would make the subprogram call graph recursive. "
+ "The verifier requires a finite, acyclic call graph so it can bound stack depth and analysis.",
+ bpf_subprog_name(env, cur), bpf_subprog_name(env, callee));
ret = -EINVAL;
goto out;
}
@@ -3817,19 +3846,46 @@ static int mark_reg_stack_read(struct bpf_verifier_env *env,
return 0;
}
-static void bpf_diag_stack_read_uninit(struct bpf_verifier_env *env, int off, int i,
- int size)
+static void bpf_diag_stack_read_invalid(struct bpf_verifier_env *env, int off, int i, int size,
+ enum bpf_stack_slot_type type)
{
- const char *reason;
+ const char *problem, *reason, *suggestion, *kind;
+
+ if (type == STACK_INVALID) {
+ reason = bpf_diag_fmt(
+ env, "This rejected read uses %d bytes at stack offset %d, but byte %d in that range is uninitialized on this path. "
+ "Programs loaded with CAP_PERFMON can be allowed to read uninitialized stack bytes, but this program is being rejected without that allowance.",
+ size, off, i);
+ bpf_diag_memory(
+ env, env->insn_idx, "uninitialized stack read", reason,
+ "Initialize every byte in the stack range before reading it, adjust the offset and size so the read covers only initialized bytes, "
+ "or load with CAP_PERFMON if uninitialized stack reads are intended.");
+ return;
+ }
- reason = bpf_diag_fmt(env,
- "This rejected read uses %d bytes at stack offset %d, but byte %d in that range is uninitialized on this path. "
- "Programs loaded with CAP_PERFMON can be allowed to read uninitialized stack bytes, but this program is being rejected without that allowance.",
- size, off, i);
- bpf_diag_memory(
- env, env->insn_idx, "uninitialized stack read", reason,
- "Initialize every byte in the stack range before reading it, adjust the offset and size so the read covers only initialized bytes, "
- "or load with CAP_PERFMON if uninitialized stack reads are intended.");
+ switch (type) {
+ case STACK_DYNPTR:
+ kind = "dynptr";
+ suggestion = "Use dynptr helpers or kfuncs to access the object represented by the dynptr instead of reading the dynptr state directly.";
+ break;
+ case STACK_ITER:
+ kind = "iterator";
+ suggestion = "Use iterator kfuncs to advance or destroy the iterator instead of reading its state directly.";
+ break;
+ case STACK_IRQ_FLAG:
+ kind = "IRQ flag";
+ suggestion = "Pass the saved IRQ flag to the matching restore kfunc instead of reading its state directly.";
+ break;
+ default:
+ return;
+ }
+
+ problem = bpf_diag_fmt(env, "direct read of %s stack state", kind);
+ reason = bpf_diag_fmt(
+ env, "This rejected read uses %d bytes at stack offset %d, but byte %d in that range belongs to verifier-managed %s state. "
+ "This state has an opaque representation that BPF programs cannot read directly.",
+ size, off, i, kind);
+ bpf_diag_memory(env, env->insn_idx, problem, reason, suggestion);
}
/* Read the stack at 'off' and put the results into the register indicated by
@@ -3921,7 +3977,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
} else {
verbose(env, "invalid read from stack off %d+%d size %d\n",
off, i, size);
- bpf_diag_stack_read_uninit(env, off, i, size);
+ bpf_diag_stack_read_invalid(env, off, i, size, type);
}
return -EACCES;
}
@@ -3980,7 +4036,7 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
} else {
verbose(env, "invalid read from stack off %d+%d size %d\n",
off, i, size);
- bpf_diag_stack_read_uninit(env, off, i, size);
+ bpf_diag_stack_read_invalid(env, off, i, size, type);
}
return -EACCES;
}
@@ -4079,13 +4135,13 @@ static int check_stack_read(struct bpf_verifier_env *env,
tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
verbose(env, "variable offset stack pointer cannot be passed into helper function; var_off=%s off=%d size=%d\n",
tn_buf, off, size);
- reason = bpf_diag_fmt(env,
- "The helper would access the stack through variable offset %s plus fixed offset %d and size %d. "
- "Helper stack memory arguments require a constant stack offset and a precise initialized range.",
+ reason = bpf_diag_fmt(
+ env, "The atomic operation would access the stack through variable offset %s plus fixed offset %d and size %d. "
+ "Atomic stack operations require a constant stack offset and a precise initialized range.",
tn_buf, off, size);
bpf_diag_memory(
- env, env->insn_idx, "variable stack access", reason,
- "Use a fixed stack offset for helper memory arguments, or copy the needed bytes into a fixed stack slot first.");
+ env, env->insn_idx, "variable-offset atomic stack access", reason,
+ "Use a fixed stack offset for the atomic operation, selecting the target stack slot on separate control-flow paths if necessary.");
return -EACCES;
}
/* Variable offset is prohibited for unprivileged mode for simplicity
@@ -9882,7 +9938,7 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
sub_name, non_sleepable_context_description(env));
operation = bpf_diag_fmt(env, "sleepable global function %s()", sub_name);
bpf_diag_ctx_forbidden(env, *insn_idx, operation,
- "Move the call outside the critical section, or use a non-sleepable function.");
+ "Call the function from a sleepable program outside any critical section, or use a non-sleepable function.");
return -EINVAL;
}
@@ -10701,6 +10757,8 @@ static inline bool in_sleepable_context(struct bpf_verifier_env *env)
static const char *non_sleepable_context_description(struct bpf_verifier_env *env)
{
+ if (!in_sleepable(env))
+ return "non-sleepable prog";
if (env->cur_state->active_rcu_locks)
return "rcu_read_lock region";
if (env->cur_state->active_preempt_locks)
@@ -10709,7 +10767,7 @@ static const char *non_sleepable_context_description(struct bpf_verifier_env *en
return "IRQ-disabled region";
if (env->cur_state->active_locks)
return "lock region";
- return "non-sleepable prog";
+ return "non-sleepable context";
}
static int release_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg,
@@ -10805,7 +10863,7 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
operation = bpf_diag_fmt(env, "sleepable helper %s#%d",
func_id_name(func_id), func_id);
bpf_diag_ctx_forbidden(env, insn_idx, operation,
- "Move the helper call outside the critical section, or use a non-sleepable helper.");
+ "Call the helper from a sleepable program outside any critical section, or use a non-sleepable helper.");
return -EINVAL;
}
@@ -10994,13 +11052,13 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
/* reset caller saved regs */
bpf_diag_record_caller_saved(env, regs);
+ bpf_diag_mod_begin(env, ®s[BPF_REG_0], NULL, BPF_DIAG_MOD_WRITE);
for (i = 0; i < CALLER_SAVED_REGS; i++) {
bpf_mark_reg_not_init(env, ®s[caller_saved[i]]);
check_reg_arg(env, caller_saved[i], DST_OP_NO_MARK);
}
invalidate_outgoing_stack_args(env, cur_func(env));
- bpf_diag_mod_begin(env, ®s[BPF_REG_0], NULL, BPF_DIAG_MOD_WRITE);
/* update return register (already marked as written above) */
ret_type = fn->ret_type;
ret_flag = type_flag(ret_type);
@@ -12623,12 +12681,12 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
!type_may_be_null(kf_arg_type)) {
const char *expected_type;
- expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
+ expected_type = bpf_diag_fmt_btf_type(env, btf, args[i].type);
verbose(env, "Possibly NULL pointer passed to trusted %s\n",
reg_arg_name(env, argno));
bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
"Add a NULL check and call the kfunc only on the non-NULL path.",
- "the pointer may be NULL, but this kfunc requires a non-NULL pointer to %s",
+ "the pointer may be NULL, but this kfunc requires a non-NULL value of type %s",
expected_type);
return -EACCES;
}
@@ -13065,8 +13123,14 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
break;
case KF_ARG_CONST_MEM_SIZE:
ret = process_const_arg(env, reg, argno, meta);
- if (ret < 0)
+ if (ret < 0) {
+ if (ret == -EINVAL)
+ bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
+ "Pass a compile-time constant or a value the verifier can prove is constant at this call.",
+ "the kfunc requires this memory size to be a verifier-known constant, but %s is variable on this path",
+ reg_arg_name(env, argno));
return ret;
+ }
fallthrough;
case KF_ARG_MEM_SIZE:
{
@@ -13130,15 +13194,13 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_call_arg_me
break;
case KF_ARG_PTR_TO_REFCOUNTED_KPTR:
if (!type_is_ptr_alloc_obj(reg->type)) {
- const char *expected_type;
-
- expected_type = bpf_diag_fmt_btf_type(env, btf, ref_id);
verbose(env, "%s is neither owning or non-owning ref\n",
reg_arg_name(env, argno));
bpf_diag_call_arg_fmt(env, insn_idx, argno, func_name,
- "Pass a pointer returned by the matching BPF object allocation or lookup operation for this kfunc.",
- "the kfunc expects a pointer to BPF-managed refcounted object type %s, but this argument is not such an object pointer",
- expected_type);
+ "Pass an owning or non-owning pointer to a BPF-managed object containing a bpf_refcount field.",
+ "the kfunc expects a pointer to a BPF-managed refcounted object, but %s is %s",
+ reg_arg_name(env, argno),
+ bpf_diag_reg_type_plain(env, reg->type));
return -EINVAL;
}
if (!type_is_non_owning_ref(reg->type))
@@ -13726,11 +13788,20 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
}
sleepable = bpf_is_kfunc_sleepable(&meta);
- if (sleepable && !in_sleepable(env)) {
- verbose(env, "program must be sleepable to call sleepable kfunc %s\n", func_name);
+ if (sleepable && !in_sleepable_context(env)) {
+ const char *suggestion;
+
+ if (in_sleepable(env)) {
+ verbose(env, "kernel func %s is sleepable within %s\n",
+ func_name, non_sleepable_context_description(env));
+ suggestion = "Move the kfunc call outside the critical section, or use a non-sleepable kfunc.";
+ } else {
+ verbose(env, "program must be sleepable to call sleepable kfunc %s\n",
+ func_name);
+ suggestion = "Mark the program sleepable if the program type allows it, or use a non-sleepable kfunc.";
+ }
operation = bpf_diag_fmt(env, "sleepable kfunc %s", func_name);
- bpf_diag_ctx_forbidden(env, insn_idx, operation,
- "Mark the program sleepable if the program type allows it, or use a non-sleepable kfunc.");
+ bpf_diag_ctx_forbidden(env, insn_idx, operation, suggestion);
return -EACCES;
}
@@ -13830,15 +13901,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
invalidate_rcu_protected_refs(env);
}
- if (sleepable && !in_sleepable_context(env)) {
- verbose(env, "kernel func %s is sleepable within %s\n",
- func_name, non_sleepable_context_description(env));
- operation = bpf_diag_fmt(env, "sleepable kfunc %s", func_name);
- bpf_diag_ctx_forbidden(env, insn_idx, operation,
- "Move the kfunc call outside the critical section, or use a non-sleepable kfunc.");
- return -EACCES;
- }
-
if (in_rbtree_lock_required_cb(env) && (rcu_lock || rcu_unlock)) {
verbose(env, "Calling bpf_rcu_read_{lock,unlock} in unnecessary rbtree callback\n");
return -EACCES;
@@ -21088,17 +21150,19 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
if (ret < 0)
goto skip_full_check;
- /* Discover all subprograms before validating their layout and BTF. */
+ /* Discover all subprograms and collect the properties needed by BTF validation. */
ret = add_subprogs(env);
if (ret < 0)
goto skip_full_check;
- ret = check_subprogs(env);
+ find_subprog_properties(env);
+
+ /* Validate BTF and apply CO-RE before reporting subprogram layout errors. */
+ ret = bpf_check_btf_info(env, attr, uattr);
if (ret < 0)
goto skip_full_check;
- /* Validate BTF against the complete subprogram layout and apply CO-RE. */
- ret = bpf_check_btf_info(env, attr, uattr);
+ ret = check_subprogs(env);
if (ret < 0)
goto skip_full_check;
diff --git a/tools/testing/selftests/bpf/progs/dynptr_fail.c b/tools/testing/selftests/bpf/progs/dynptr_fail.c
index beaa73dc35f5d..9418dfe4d7b7f 100644
--- a/tools/testing/selftests/bpf/progs/dynptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/dynptr_fail.c
@@ -560,6 +560,9 @@ int global(void *ctx)
/* A direct read should fail */
SEC("?raw_tp")
__failure __msg("invalid read from stack")
+__msg("Verification failed: Memory Safety: Direct read of dynptr stack state")
+__msg("verifier-managed dynptr state")
+__msg("Use dynptr helpers or kfuncs")
int invalid_read1(void *ctx)
{
struct bpf_dynptr ptr;
@@ -1590,6 +1593,7 @@ __u32 hdr_size = sizeof(struct ethhdr);
/* Can't pass in variable-sized len to bpf_dynptr_slice */
SEC("?tc")
__failure __msg("must be a known constant")
+__msg("requires this memory size to be a verifier-known constant")
int dynptr_slice_var_len1(struct __sk_buff *skb)
{
struct bpf_dynptr ptr;
@@ -1609,6 +1613,7 @@ int dynptr_slice_var_len1(struct __sk_buff *skb)
/* Can't pass in variable-sized len to bpf_dynptr_slice */
SEC("?tc")
__failure __msg("must be a known constant")
+__msg("requires this memory size to be a verifier-known constant")
int dynptr_slice_var_len2(struct __sk_buff *skb)
{
char buffer[sizeof(struct ethhdr)] = {};
diff --git a/tools/testing/selftests/bpf/progs/irq.c b/tools/testing/selftests/bpf/progs/irq.c
index a4a007866a332..8d2b50d11fa4e 100644
--- a/tools/testing/selftests/bpf/progs/irq.c
+++ b/tools/testing/selftests/bpf/progs/irq.c
@@ -14,6 +14,19 @@ extern int bpf_copy_from_user_str(void *dst, u32 dst__sz, const void *unsafe_ptr
struct bpf_res_spin_lock lockA __hidden SEC(".data.A");
struct bpf_res_spin_lock lockB __hidden SEC(".data.B");
+SEC("?tc")
+__failure __msg("invalid read from stack")
+__msg("Verification failed: Memory Safety: Direct read of IRQ flag stack state")
+__msg("verifier-managed IRQ flag state")
+__msg("Pass the saved IRQ flag to the matching restore kfunc")
+int irq_flag_direct_read(struct __sk_buff *ctx)
+{
+ unsigned long flags;
+
+ bpf_local_irq_save(&flags);
+ return flags;
+}
+
SEC("?tc")
__failure __msg("R1 doesn't point to an irq flag on stack")
int irq_save_bad_arg(struct __sk_buff *ctx)
diff --git a/tools/testing/selftests/bpf/progs/iters_state_safety.c b/tools/testing/selftests/bpf/progs/iters_state_safety.c
index 646026430e9b5..4723ae578e534 100644
--- a/tools/testing/selftests/bpf/progs/iters_state_safety.c
+++ b/tools/testing/selftests/bpf/progs/iters_state_safety.c
@@ -332,6 +332,9 @@ int next_after_destroy_fail(void *ctx)
SEC("?raw_tp")
__failure __msg("invalid read from stack")
+__msg("Verification failed: Memory Safety: Direct read of iterator stack state")
+__msg("verifier-managed iterator state")
+__msg("Use iterator kfuncs")
int __naked read_from_iter_slot_fail(void)
{
asm volatile (
diff --git a/tools/testing/selftests/bpf/progs/preempt_lock.c b/tools/testing/selftests/bpf/progs/preempt_lock.c
index 6d5fce7e6ffc9..83c911494a34c 100644
--- a/tools/testing/selftests/bpf/progs/preempt_lock.c
+++ b/tools/testing/selftests/bpf/progs/preempt_lock.c
@@ -6,6 +6,8 @@
#include "bpf_experimental.h"
extern int bpf_copy_from_user_str(void *dst, u32 dst__sz, const void *unsafe_ptr__ign, u64 flags) __weak __ksym;
+extern void bpf_rcu_read_lock(void) __ksym;
+extern void bpf_rcu_read_unlock(void) __ksym;
SEC("?tc")
__failure __msg("BPF_EXIT instruction in main prog cannot be used inside bpf_preempt_disable-ed region")
@@ -127,6 +129,19 @@ int preempt_sleepable_kfunc(void *ctx)
return 0;
}
+SEC("?fentry/" SYS_PREFIX "sys_getpgid")
+__failure __msg("program must be sleepable to call sleepable kfunc bpf_copy_from_user_str")
+__msg("cannot be used in non-sleepable program")
+int non_sleepable_kfunc_in_rcu(void *ctx)
+{
+ u32 data;
+
+ bpf_rcu_read_lock();
+ bpf_copy_from_user_str(&data, sizeof(data), NULL, 0);
+ bpf_rcu_read_unlock();
+ return 0;
+}
+
int __noinline preempt_global_subprog(void)
{
preempt_balance_subprog();
diff --git a/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c b/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c
index 024ef2aae2008..eaaed0859f946 100644
--- a/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c
+++ b/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c
@@ -63,6 +63,7 @@ long rbtree_refcounted_node_ref_escapes(void *ctx)
SEC("?tc")
__failure __msg("Possibly NULL pointer passed to trusted R1")
+__msg("requires a non-NULL value of type (void *)")
long refcount_acquire_maybe_null(void *ctx)
{
struct node_acquire *n, *m;
@@ -80,6 +81,14 @@ long refcount_acquire_maybe_null(void *ctx)
return 0;
}
+SEC("?tc")
+__failure __msg("R1 is neither owning or non-owning ref")
+__msg("expects a pointer to a BPF-managed refcounted object, but R1 is a context pointer")
+long refcount_acquire_non_object(void *ctx)
+{
+ return bpf_refcount_acquire(ctx) != NULL;
+}
+
SEC("?tc")
__failure __msg("Unreleased reference id=3 alloc_insn={{[0-9]+}}")
long rbtree_refcounted_node_ref_escapes_owning_input(void *ctx)
diff --git a/tools/testing/selftests/bpf/progs/verifier_cfg.c b/tools/testing/selftests/bpf/progs/verifier_cfg.c
index c1f55e1d80a42..4416d915cdf38 100644
--- a/tools/testing/selftests/bpf/progs/verifier_cfg.c
+++ b/tools/testing/selftests/bpf/progs/verifier_cfg.c
@@ -55,6 +55,46 @@ __naked void out_of_range_jump2(void)
" ::: __clobber_all);
}
+static __naked __noinline __used int cross_subprog_target(void)
+{
+ asm volatile (" \
+ r0 = 0; \
+ exit; \
+" ::: __clobber_all);
+}
+
+SEC("socket")
+__description("jump across subprogram boundary")
+__failure __msg("jump out of range from insn 1")
+__msg("jump_across_subprog_boundary @ verifier_cfg.c")
+__naked void jump_across_subprog_boundary(void)
+{
+ asm volatile (" \
+ call cross_subprog_target; \
+ goto +1; \
+ exit; \
+" ::: __clobber_all);
+}
+
+static __naked __noinline __used int fallthrough_subprog(void)
+{
+ asm volatile (" \
+ r0 = 0; \
+" ::: __clobber_all);
+}
+
+SEC("socket")
+__description("subprogram fallthrough")
+__failure __msg("last insn is not an exit or jmp")
+__msg("fallthrough_subprog @ verifier_cfg.c")
+__naked void subprog_fallthrough(void)
+{
+ asm volatile (" \
+ call fallthrough_subprog; \
+ exit; \
+" ::: __clobber_all);
+}
+
SEC("socket")
__description("loop (back-edge)")
__failure __msg("unreachable insn 1")
diff --git a/tools/testing/selftests/bpf/progs/verifier_gotox.c b/tools/testing/selftests/bpf/progs/verifier_gotox.c
index 5b18c9a27717b..a835a48710213 100644
--- a/tools/testing/selftests/bpf/progs/verifier_gotox.c
+++ b/tools/testing/selftests/bpf/progs/verifier_gotox.c
@@ -53,9 +53,11 @@ DEFINE_SIMPLE_JUMP_TABLE_PROG(reserved_field_non_zero_imm, BPF_REG_0, 0, 1, __fa
*/
SEC("socket")
__failure __msg("no jump tables found for subprog starting at 0")
+__msg(">>> 1 | (0d) gotox r0")
__naked void jump_table_no_jump_table(void)
{
asm volatile (" \
+ r0 = 0; \
.8byte %[gotox_r0]; \
r0 = 1; \
exit; \
diff --git a/tools/testing/selftests/bpf/progs/verifier_loops1.c b/tools/testing/selftests/bpf/progs/verifier_loops1.c
index d248ce877f14e..d1ec19b7f66b2 100644
--- a/tools/testing/selftests/bpf/progs/verifier_loops1.c
+++ b/tools/testing/selftests/bpf/progs/verifier_loops1.c
@@ -139,6 +139,7 @@ SEC("tracepoint")
__description("bounded recursion")
__failure
__msg("recursive call from")
+__msg("The call from bounded_recursion__1() to bounded_recursion__1() would make")
__naked void bounded_recursion(void)
{
asm volatile (" \
diff --git a/tools/testing/selftests/bpf/progs/verifier_unpriv.c b/tools/testing/selftests/bpf/progs/verifier_unpriv.c
index 42de5cff7e52a..3069e70fbcbdc 100644
--- a/tools/testing/selftests/bpf/progs/verifier_unpriv.c
+++ b/tools/testing/selftests/bpf/progs/verifier_unpriv.c
@@ -96,6 +96,24 @@ __naked void pseudo_btf_id_log_masks_address(void)
: __clobber_all);
}
+static int pseudo_func_callback(__u32 index, void *ctx)
+{
+ return 0;
+}
+
+SEC("socket")
+__description("unpriv: pseudo function policy diagnostic")
+__success __failure_unpriv
+__msg_unpriv("loading/calling other bpf or kernel functions")
+__not_msg_unpriv("BPF-to-BPF function call")
+__msg_unpriv("policy check failed for BPF function reference")
+__msg_unpriv("avoid BPF function references in unprivileged")
+int unpriv_pseudo_func_policy(void *ctx)
+{
+ bpf_loop(1, pseudo_func_callback, NULL, 0);
+ return 0;
+}
+
SEC("socket")
__description("unpriv: return pointer")
__success __failure_unpriv __msg_unpriv("R0 leaks addr")
diff --git a/tools/testing/selftests/bpf/progs/verifier_xadd.c b/tools/testing/selftests/bpf/progs/verifier_xadd.c
index 05a0a55adb451..f2430b9a02184 100644
--- a/tools/testing/selftests/bpf/progs/verifier_xadd.c
+++ b/tools/testing/selftests/bpf/progs/verifier_xadd.c
@@ -121,4 +121,31 @@ l0_%=: r0 = 42; \
" ::: __clobber_all);
}
+SEC("tc")
+__description("xadd with variable stack offset")
+__failure
+__msg("variable offset stack pointer cannot be passed into helper function")
+__msg("Verification failed: Memory Safety: Variable-offset atomic stack access")
+__msg("The atomic operation would access the stack")
+__msg("Use a fixed stack offset for the atomic operation")
+__naked void xadd_variable_stack_offset(void)
+{
+ asm volatile (" \
+ r1 = 0; \
+ *(u64 *)(r10 - 16) = r1; \
+ *(u64 *)(r10 - 8) = r1; \
+ call %[bpf_get_prandom_u32]; \
+ r0 &= 8; \
+ r1 = r10; \
+ r1 += -16; \
+ r1 += r0; \
+ r2 = 1; \
+ lock *(u64 *)(r1 + 0) += r2; \
+ r0 = 0; \
+ exit; \
+" :
+ : __imm(bpf_get_prandom_u32)
+ : __clobber_all);
+}
+
char _license[] SEC("license") = "GPL";
|
| 3/2 | 2026/08/16 02:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 14048 output: 37 thoughts: 1766 Thoughts: **My Analysis of the BPF Verifier Patch** Okay, so I'm looking at this BPF verifier patch, and it seems like a mix of diagnostic improvements and potentially impactful functional changes. My initial impression is that the bulk of the changes are focused on enhancing the verifier's error messages and hints. However, I need to dig deeper to see if there are any subtle changes to the verification logic itself. First, looking at `kernel/bpf/btf.c`, I see a modification to `btf_show_name`. It now handles void types correctly. This is a minor correction, nothing to be alarmed about. It looks like the original code could have potentially led to incorrect type name representation in specific cases, which is now fixed. Then, `kernel/bpf/cfg.c` has a minor change in the `jt_from_subprog` function. It looks like an `insn_idx` has been added. This is probably just providing more context for diagnostics, to pinpoint the exact instruction causing issues. `kernel/bpf/diagnostics.c` has a number of updates; all of them seem to be about making the error messages and context reporting better. Nothing too critical here, but every bit helps with debugging BPF programs. The meat of the changes is in `kernel/bpf/verifier.c`. Here's where it gets interesting, as I suspect there are some subtle but important changes in the program's order of execution. I see that `check_subprogs` is split into `find_subprog_properties` and `check_subprogs`, and the calling order of these and `bpf_check_btf_info` has changed. This is a big red flag, and needs careful examination. Specifically, the code now calls `find_subprog_properties`, then `bpf_check_btf_info`, and finally `check_subprogs`. This means that BTF validation is now performed *before* we check subprogram layout validity. Previously, the subprogram structure was validated first. This is a potential functional change with security implications! I need to understand what `find_subprog_properties` does. Okay, it's just extracting the logic related to `has_ld_abs`, nothing critical there. The reordering means that `bpf_check_btf_info` now has the potential to process BTF information for programs that might have *invalid* subprogram structures (e.g., jumps outside subprogram boundaries, or fallthroughs). This is a potential attack vector if `bpf_check_btf_info` isn't robust enough to handle invalid program flow. The code is structured such that `add_subprogs` is still called first, which determines the subprogram boundaries. So the invalid program still should be partitioned into a set of subprograms. However, because the order of BTF and subprogram validation has been modified, there may be some previously unexpected paths. I need to quickly analyze `bpf_check_btf_info` to see if it makes assumptions about program structure that are no longer guaranteed by the new execution order. My gut feeling is this is worth fuzzing thoroughly. This reordering of validation checks could introduce new, potentially exploitable, behavior if `bpf_check_btf_info` wasn't designed to handle programs with invalid subprogram layouts. |
| 4/2 | 2026/08/16 02:13 | tool | grepper |
0mArgs:{
"Expression": "bpf_check_btf_info",
"PathPrefix": "kernel/bpf/verifier.c"
}Results: {
"Output": "kernel/bpf/verifier.c=21037=int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\n--\nkernel/bpf/verifier.c-21160-\t/* Validate BTF and apply CO-RE before reporting subprogram layout errors. */\nkernel/bpf/verifier.c:21161:\tret = bpf_check_btf_info(env, attr, uattr);\nkernel/bpf/verifier.c-21162-\tif (ret \u003c 0)\n"
} |
| 5/2 | 2026/08/16 02:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 16730 output: 40 thoughts: 18 Thoughts: Here's a summary of my thought process, as if I were considering that function: **Diving into `bpf_check_btf_info`** Alright, let's pull up the definition of `bpf_check_btf_info`. Given my expertise, I'm already anticipating what the core purpose is: this function *has* to be concerned with validating BTF information within the BPF context. Specifically, I'm expecting it will ensure the integrity and consistency of the BTF data that's being used by BPF programs or infrastructure. I would assume it's checking for things like: * **Type Safety:** Making sure that type definitions referenced in BPF code are actually present and valid in the BTF data. Any mismatches in sizes or layout should be caught. * **Version Compatibility:** Probably verifying that the BTF data is compatible with the kernel version or BPF runtime environment it's running in. A mismatch here could lead to crashes or undefined behavior. * **Security:** Are there potential exploits if BTF information is corrupted or manipulated? * **Completeness:** Confirming that all referenced types are defined, or that any partial definitions are handled in a reasonable way. I'd also be curious about where this function fits into the overall BPF verification process. Is it called early, or later? Does it relate to the BPF verifier in some way? My experience suggests it likely is invoked during program load or attach operations, probably *before* the program is allowed to run. It will be interesting to investigate the input parameters too; what exactly does this function take as arguments: the BTF data itself, pointers to kernel data structures, and the context of the program? This kind of check is crucial for BPF security and stability; if it is poorly implemented, the whole system is at risk. |
| 6/2 | 2026/08/16 02:13 | tool | grepper |
0mError:no matches Args: {
"Expression": "^static int bpf_check_btf_info",
"PathPrefix": "kernel/bpf/verifier.c"
}Results: {
"error": "no matches"
} |
| 7/2 | 2026/08/16 02:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17194 output: 35 thoughts: 18 Thoughts: Okay, here's my summary, presented as a stream of thought: **Debugging the BTF Issue** Right, so I'm digging into this... seems like the problem I'm chasing might *not* be something fundamentally static. I was initially leaning that way, but now I'm thinking maybe the error is dynamic in nature, changing in each execution of the code. Or, and this is where I need to focus my energy, maybe the root of this behavior I'm seeing is hidden away in `btf.c`. That's the likely location for debugging next. I should step through it and check what happens. |
| 8/2 | 2026/08/16 02:13 | tool | grepper |
0mArgs:{
"Expression": "int bpf_check_btf_info",
"PathPrefix": "kernel/bpf/"
}Results: {
"Output": "kernel/bpf/check_btf.c=410=int bpf_prepare_btf_info(struct bpf_verifier_env *env,\n--\nkernel/bpf/check_btf.c-437-\nkernel/bpf/check_btf.c:438:int bpf_check_btf_info(struct bpf_verifier_env *env,\nkernel/bpf/check_btf.c-439-\t\t const union bpf_attr *attr,\n"
} |
| 9/2 | 2026/08/16 02:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 17516 output: 40 thoughts: 29 Thoughts: Okay, here's my interpretation of that thought process, as if it were my own: **Diving into BTF Verification** Alright, time to dig into the heart of BTF verification. I need to understand how the kernel validates BTF information, and a good starting point is definitely the `bpf_check_btf_info` function. That's in `kernel/bpf/check_btf.c`, so let's go ahead and pull that up. I need to see how they're handling the validation logic and what checks they're performing on the BTF data. I'm especially interested in any potential vulnerabilities or common pitfalls in the verification process. Getting a handle on this will be essential for building robust and secure BPF programs. |
| 10/2 | 2026/08/16 02:13 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/check_btf.c",
"FirstLine": 438,
"LineCount": 100
}Results: {
"Contents": " 438:\tint bpf_check_btf_info(struct bpf_verifier_env *env,\n 439:\t\t\t const union bpf_attr *attr,\n 440:\t\t\t bpfptr_t uattr)\n 441:\t{\n 442:\t\tint err;\n 443:\t\n 444:\t\tif (!attr-\u003efunc_info_cnt \u0026\u0026 !attr-\u003eline_info_cnt) {\n 445:\t\t\tif (check_abnormal_return(env))\n 446:\t\t\t\treturn -EINVAL;\n 447:\t\t\treturn 0;\n 448:\t\t}\n 449:\t\n 450:\t\terr = check_btf_func(env, attr, uattr);\n 451:\t\tif (err)\n 452:\t\t\treturn err;\n 453:\t\n 454:\t\terr = check_btf_line(env, attr, uattr);\n 455:\t\tif (err)\n 456:\t\t\treturn err;\n 457:\t\n 458:\t\terr = check_core_relo(env, attr, uattr);\n 459:\t\tif (err)\n 460:\t\t\treturn err;\n 461:\t\n 462:\t\treturn 0;\n 463:\t}\n"
} |
| 11/2 | 2026/08/16 02:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 18102 output: 34 thoughts: 2329 Thoughts: **Analyzing the BPF Verifier Patch: A Deep Dive** Okay, so I'm digging into this patch. The core of it seems to be about reordering how the BPF verifier handles BTF (BPF Type Format) validation and subprogram layout checks. My initial thought is that the primary motivation is to prioritize error reporting: show BTF/CO-RE (Compile-Once, Run-Everywhere) errors *before* potentially misleading errors caused by the subprogram layout being incorrect. I mean, it makes sense; if CO-RE is messing with the program, it's probably better to know that *first* instead of seeing an error about an invalid jump within a subprogram. This is a functional change. The process involves `check_btf_func`, `check_btf_line`, and `check_core_relo`. These functions are central to validating BTF against the instructions of the BPF program. The key is that `check_subprogs` checks for jumps that cross subprogram boundaries or fallthroughs *after* relocations have been applied. But then there's a crucial point: `add_subprogs` populates `env->subprog_info` *before* `bpf_check_btf_info`. So, `check_subprogs` checks for invalid layouts *after* subprograms are actually defined. Therefore, moving `check_subprogs` after `bpf_check_btf_info` potentially means validating BTF information on a program with an incorrect subprogram layout. I need to be sure. I'm thinking about this. CO-RE relocations *do* modify instructions, but the patch seems to assume that they don't change jump *offsets* (they change immediates instead). However, I also saw the commit message: "Validate BTF and apply CO-RE before reporting subprogram layout errors." Looking at the code, there are numerous diagnostic changes. The diagnostic changes include the reordering of `bpf_diag_mod_begin`, the consolidation and clarification of sleepable kfunc checks, and various improvements to error messages in `check_kfunc_args`. Specifically, error message improvements were made in `KF_ARG_CONST_MEM_SIZE`, `KF_ARG_PTR_TO_REFCOUNTED_KPTR`. These are all about making the verifier's feedback more helpful, providing clearer suggestions to the user. However, the change in the order of execution of the functions is the most important functional change. Since the `bpf_check_btf_info` now executes on a potentially "invalid" subprogram layout. Could this cause a crash or out-of-bounds access? I should check `check_btf_func` to ensure the program has a valid layout. |
| 12/2 | 2026/08/16 02:13 | tool | grepper |
0mArgs:{
"Expression": "^static int check_btf_func",
"PathPrefix": "kernel/bpf/"
}Results: {
"Output": "kernel/bpf/check_btf.c=31=static int prepare_btf_func(struct bpf_verifier_env *env,\n--\nkernel/bpf/check_btf.c-132-\nkernel/bpf/check_btf.c:133:static int check_btf_func(struct bpf_verifier_env *env,\nkernel/bpf/check_btf.c-134-\t\t\t const union bpf_attr *attr,\n"
} |
| 13/2 | 2026/08/16 02:13 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 21190 output: 40 thoughts: 12 Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person: **Analyzing the Function 'check_btf_func'** Alright, let's dive into this `check_btf_func` routine. My immediate thought is, what's its purpose within the broader context? Is this part of a kernel module, a user-space utility, or something else entirely? The name itself suggests a function related to BPF Type Format (BTF) checking, which is immediately interesting. This means we're dealing with metadata about BPF programs, and verifying its integrity. I'm already mentally running through potential failure modes. Is it looking for malformed BTF data, inconsistencies between the BTF info and the actual BPF program, or perhaps type mismatches? My experience tells me there will be several areas that require close inspection, such as: * **BTF Header Integrity:** Ensuring the BTF header is well-formed and valid. * **Type Descriptor Consistency:** Verifying the consistency between the types recorded in the BTF data. * **Size and Alignment:** Confirming the data structures match expectations for architecture specifics. * **Dependency Resolution:** Are there cross-references between types? We need to ensure dependencies are correctly resolved. * **Error Reporting:** How are errors handled? Does it log, return an error code, or trigger some other mechanism? How thorough is it? I need to understand what specific checks this function is implementing. Are we looking at a basic validation, or something more sophisticated like detecting potential type confusion or security vulnerabilities? My mental checklist is already running through common BTF-related issues. Now, the goal is to see how this function handles them. Let's see how it's implemented. Let's start reading! |
| 14/2 | 2026/08/16 02:14 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/check_btf.c",
"FirstLine": 133,
"LineCount": 100
}Results: {
"Contents": " 133:\tstatic int check_btf_func(struct bpf_verifier_env *env,\n 134:\t\t\t\t const union bpf_attr *attr,\n 135:\t\t\t\t bpfptr_t uattr)\n 136:\t{\n 137:\t\tconst struct btf_type *type, *func_proto, *ret_type;\n 138:\t\tu32 i, nfuncs, urec_size;\n 139:\t\tstruct bpf_func_info *krecord;\n 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 205:\terr_free:\n 206:\t\tkfree(info_aux);\n 207:\t\treturn ret;\n 208:\t}\n 209:\t\n 210:\t#define MIN_BPF_LINEINFO_SIZE\toffsetofend(struct bpf_line_info, line_col)\n 211:\t#define MAX_LINEINFO_REC_SIZE\tMAX_FUNCINFO_REC_SIZE\n 212:\t\n 213:\tstatic int check_btf_line(struct bpf_verifier_env *env,\n 214:\t\t\t\t const union bpf_attr *attr,\n 215:\t\t\t\t bpfptr_t uattr)\n 216:\t{\n 217:\t\tu32 i, s, nr_linfo, ncopy, expected_size, rec_size, prev_offset = 0;\n 218:\t\tstruct bpf_subprog_info *sub;\n 219:\t\tstruct bpf_line_info *linfo;\n 220:\t\tstruct bpf_prog *prog;\n 221:\t\tconst struct btf *btf;\n 222:\t\tbpfptr_t ulinfo;\n 223:\t\tint err;\n 224:\t\n 225:\t\tnr_linfo = attr-\u003eline_info_cnt;\n 226:\t\tif (!nr_linfo)\n 227:\t\t\treturn 0;\n 228:\t\tif (nr_linfo \u003e INT_MAX / sizeof(struct bpf_line_info))\n 229:\t\t\treturn -EINVAL;\n 230:\t\n 231:\t\trec_size = attr-\u003eline_info_rec_size;\n 232:\t\tif (rec_size \u003c MIN_BPF_LINEINFO_SIZE ||\n"
} |
| 15/2 | 2026/08/16 02:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 23308 output: 40 thoughts: 240 Thoughts: **My Reasoning on BTF Verification** Okay, let's break this down. I see that `check_btf_func` is relying on `env->subprog_info[i].has_ld_abs`, which makes perfect sense. Ah, that's why `find_subprog_properties` was split off from `check_subprogs` in the first place – `find_subprog_properties` is responsible for setting that flag for each subprogram. This allows `check_btf_func` to efficiently use the precomputed information. However, a potential problem is that `check_subprogs` also performs essential validation like checking for invalid jumps and fallthroughs. The question then becomes: if I move `check_subprogs` *after* `bpf_check_btf_info`, will that create an issue? Would `bpf_check_btf_info` potentially process a program that contains invalid jump instructions? I need to know if `bpf_check_btf_info` cares about those particular checks. Let's look more closely at what `bpf_check_btf_info` actually does. `check_btf_func` is a straightforward iteration through `nfuncs`, checking the `env->subprog_info[i].start` value. Then, I see `check_btf_line` iterates through `nr_linfo` and focuses on verifying `linfo[i].insn_off`. Let me now check `check_btf_line`. |
| 16/2 | 2026/08/16 02:14 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/check_btf.c",
"FirstLine": 233,
"LineCount": 100
}Results: {
"Contents": " 233:\t\t rec_size \u003e MAX_LINEINFO_REC_SIZE ||\n 234:\t\t rec_size \u0026 (sizeof(u32) - 1))\n 235:\t\t\treturn -EINVAL;\n 236:\t\n 237:\t\t/* Need to zero it in case the userspace may\n 238:\t\t * pass in a smaller bpf_line_info object.\n 239:\t\t */\n 240:\t\tlinfo = kvzalloc_objs(struct bpf_line_info, nr_linfo,\n 241:\t\t\t\t GFP_KERNEL_ACCOUNT | __GFP_NOWARN);\n 242:\t\tif (!linfo)\n 243:\t\t\treturn -ENOMEM;\n 244:\t\n 245:\t\tprog = env-\u003eprog;\n 246:\t\tbtf = prog-\u003eaux-\u003ebtf;\n 247:\t\n 248:\t\ts = 0;\n 249:\t\tsub = env-\u003esubprog_info;\n 250:\t\tulinfo = make_bpfptr(attr-\u003eline_info, uattr.is_kernel);\n 251:\t\texpected_size = sizeof(struct bpf_line_info);\n 252:\t\tncopy = min_t(u32, expected_size, rec_size);\n 253:\t\tfor (i = 0; i \u003c nr_linfo; i++) {\n 254:\t\t\terr = bpf_check_uarg_tail_zero(ulinfo, expected_size, rec_size);\n 255:\t\t\tif (err) {\n 256:\t\t\t\tif (err == -E2BIG) {\n 257:\t\t\t\t\tverbose(env, \"nonzero tailing record in line_info\");\n 258:\t\t\t\t\tif (copy_to_bpfptr_offset(uattr,\n 259:\t\t\t\t\t\t\t\t offsetof(union bpf_attr, line_info_rec_size),\n 260:\t\t\t\t\t\t\t\t \u0026expected_size, sizeof(expected_size)))\n 261:\t\t\t\t\t\terr = -EFAULT;\n 262:\t\t\t\t}\n 263:\t\t\t\tgoto err_free;\n 264:\t\t\t}\n 265:\t\n 266:\t\t\tif (copy_from_bpfptr(\u0026linfo[i], ulinfo, ncopy)) {\n 267:\t\t\t\terr = -EFAULT;\n 268:\t\t\t\tgoto err_free;\n 269:\t\t\t}\n 270:\t\n 271:\t\t\t/*\n 272:\t\t\t * Check insn_off to ensure\n 273:\t\t\t * 1) strictly increasing AND\n 274:\t\t\t * 2) bounded by prog-\u003elen\n 275:\t\t\t *\n 276:\t\t\t * The linfo[0].insn_off == 0 check logically falls into\n 277:\t\t\t * the later \"missing bpf_line_info for func...\" case\n 278:\t\t\t * because the first linfo[0].insn_off must be the\n 279:\t\t\t * first sub also and the first sub must have\n 280:\t\t\t * subprog_info[0].start == 0.\n 281:\t\t\t */\n 282:\t\t\tif ((i \u0026\u0026 linfo[i].insn_off \u003c= prev_offset) ||\n 283:\t\t\t linfo[i].insn_off \u003e= prog-\u003elen) {\n 284:\t\t\t\tverbose(env, \"Invalid line_info[%u].insn_off:%u (prev_offset:%u prog-\u003elen:%u)\\n\",\n 285:\t\t\t\t\ti, linfo[i].insn_off, prev_offset,\n 286:\t\t\t\t\tprog-\u003elen);\n 287:\t\t\t\terr = -EINVAL;\n 288:\t\t\t\tgoto err_free;\n 289:\t\t\t}\n 290:\t\n 291:\t\t\tif (!prog-\u003einsnsi[linfo[i].insn_off].code) {\n 292:\t\t\t\tverbose(env,\n 293:\t\t\t\t\t\"Invalid insn code at line_info[%u].insn_off\\n\",\n 294:\t\t\t\t\ti);\n 295:\t\t\t\terr = -EINVAL;\n 296:\t\t\t\tgoto err_free;\n 297:\t\t\t}\n 298:\t\n 299:\t\t\tif (!btf_name_by_offset(btf, linfo[i].line_off) ||\n 300:\t\t\t !btf_name_by_offset(btf, linfo[i].file_name_off)) {\n 301:\t\t\t\tverbose(env, \"Invalid line_info[%u].line_off or .file_name_off\\n\", i);\n 302:\t\t\t\terr = -EINVAL;\n 303:\t\t\t\tgoto err_free;\n 304:\t\t\t}\n 305:\t\n 306:\t\t\tif (s != env-\u003esubprog_cnt) {\n 307:\t\t\t\tif (linfo[i].insn_off == sub[s].start) {\n 308:\t\t\t\t\tsub[s].linfo_idx = i;\n 309:\t\t\t\t\ts++;\n 310:\t\t\t\t} else if (sub[s].start \u003c linfo[i].insn_off) {\n 311:\t\t\t\t\tverbose(env, \"missing bpf_line_info for func#%u\\n\", s);\n 312:\t\t\t\t\terr = -EINVAL;\n 313:\t\t\t\t\tgoto err_free;\n 314:\t\t\t\t}\n 315:\t\t\t}\n 316:\t\n 317:\t\t\tprev_offset = linfo[i].insn_off;\n 318:\t\t\tbpfptr_add(\u0026ulinfo, rec_size);\n 319:\t\t}\n 320:\t\n 321:\t\tif (s != env-\u003esubprog_cnt) {\n 322:\t\t\tverbose(env, \"missing bpf_line_info for %u funcs starting from func#%u\\n\",\n 323:\t\t\t\tenv-\u003esubprog_cnt - s, s);\n 324:\t\t\terr = -EINVAL;\n 325:\t\t\tgoto err_free;\n 326:\t\t}\n 327:\t\n 328:\t\tprog-\u003eaux-\u003elinfo = linfo;\n 329:\t\tprog-\u003eaux-\u003enr_linfo = nr_linfo;\n 330:\t\n 331:\t\treturn 0;\n 332:\t\n"
} |
| 17/2 | 2026/08/16 02:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 25549 output: 40 thoughts: 127 Thoughts: **My Reasoning on the Code Flow** Okay, so I'm looking at this code and trying to understand why a certain check is happening where it is. It looks like `bpf_check_btf_info` isn't concerned with jumps or how the code flows within the subprograms. Its primary job seems to be validating that the BTF information aligns with the subprogram boundaries that were previously identified by `add_subprogs`. So, the reordering makes sense – moving `check_subprogs` after `bpf_check_btf_info` is intended to prioritize reporting BTF/CO-RE errors before any issues related to the overall subprogram layout. It makes the diagnostic messages cleaner and more intuitive. But wait, a nagging question: does `bpf_check_btf_info` actually *apply* CO-RE relocation? I need to clarify that. If it does, then the ordering is even more critical. Time to dive into `check_core_relo` and confirm my assumptions. |
| 18/2 | 2026/08/16 02:14 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/check_btf.c",
"FirstLine": 333,
"LineCount": 100
}Results: {
"Contents": " 333:\terr_free:\n 334:\t\tkvfree(linfo);\n 335:\t\treturn err;\n 336:\t}\n 337:\t\n 338:\t#define MIN_CORE_RELO_SIZE\tsizeof(struct bpf_core_relo)\n 339:\t#define MAX_CORE_RELO_SIZE\tMAX_FUNCINFO_REC_SIZE\n 340:\t\n 341:\tstatic int check_core_relo(struct bpf_verifier_env *env,\n 342:\t\t\t\t const union bpf_attr *attr,\n 343:\t\t\t\t bpfptr_t uattr)\n 344:\t{\n 345:\t\tu32 i, nr_core_relo, ncopy, expected_size, rec_size;\n 346:\t\tstruct bpf_core_relo core_relo = {};\n 347:\t\tstruct bpf_prog *prog = env-\u003eprog;\n 348:\t\tconst struct btf *btf = prog-\u003eaux-\u003ebtf;\n 349:\t\tstruct bpf_core_ctx ctx = {\n 350:\t\t\t.log = \u0026env-\u003elog,\n 351:\t\t\t.btf = btf,\n 352:\t\t};\n 353:\t\tbpfptr_t u_core_relo;\n 354:\t\tint err;\n 355:\t\n 356:\t\tnr_core_relo = attr-\u003ecore_relo_cnt;\n 357:\t\tif (!nr_core_relo)\n 358:\t\t\treturn 0;\n 359:\t\tif (nr_core_relo \u003e INT_MAX / sizeof(struct bpf_core_relo))\n 360:\t\t\treturn -EINVAL;\n 361:\t\n 362:\t\trec_size = attr-\u003ecore_relo_rec_size;\n 363:\t\tif (rec_size \u003c MIN_CORE_RELO_SIZE ||\n 364:\t\t rec_size \u003e MAX_CORE_RELO_SIZE ||\n 365:\t\t rec_size % sizeof(u32))\n 366:\t\t\treturn -EINVAL;\n 367:\t\n 368:\t\tu_core_relo = make_bpfptr(attr-\u003ecore_relos, uattr.is_kernel);\n 369:\t\texpected_size = sizeof(struct bpf_core_relo);\n 370:\t\tncopy = min_t(u32, expected_size, rec_size);\n 371:\t\n 372:\t\t/* Unlike func_info and line_info, copy and apply each CO-RE\n 373:\t\t * relocation record one at a time.\n 374:\t\t */\n 375:\t\tfor (i = 0; i \u003c nr_core_relo; i++) {\n 376:\t\t\t/* future proofing when sizeof(bpf_core_relo) changes */\n 377:\t\t\terr = bpf_check_uarg_tail_zero(u_core_relo, expected_size, rec_size);\n 378:\t\t\tif (err) {\n 379:\t\t\t\tif (err == -E2BIG) {\n 380:\t\t\t\t\tverbose(env, \"nonzero tailing record in core_relo\");\n 381:\t\t\t\t\tif (copy_to_bpfptr_offset(uattr,\n 382:\t\t\t\t\t\t\t\t offsetof(union bpf_attr, core_relo_rec_size),\n 383:\t\t\t\t\t\t\t\t \u0026expected_size, sizeof(expected_size)))\n 384:\t\t\t\t\t\terr = -EFAULT;\n 385:\t\t\t\t}\n 386:\t\t\t\tbreak;\n 387:\t\t\t}\n 388:\t\n 389:\t\t\tif (copy_from_bpfptr(\u0026core_relo, u_core_relo, ncopy)) {\n 390:\t\t\t\terr = -EFAULT;\n 391:\t\t\t\tbreak;\n 392:\t\t\t}\n 393:\t\n 394:\t\t\tif (core_relo.insn_off % 8 || core_relo.insn_off / 8 \u003e= prog-\u003elen) {\n 395:\t\t\t\tverbose(env, \"Invalid core_relo[%u].insn_off:%u prog-\u003elen:%u\\n\",\n 396:\t\t\t\t\ti, core_relo.insn_off, prog-\u003elen);\n 397:\t\t\t\terr = -EINVAL;\n 398:\t\t\t\tbreak;\n 399:\t\t\t}\n 400:\t\n 401:\t\t\terr = bpf_core_apply(\u0026ctx, \u0026core_relo, i,\n 402:\t\t\t\t\t \u0026prog-\u003einsnsi[core_relo.insn_off / 8]);\n 403:\t\t\tif (err)\n 404:\t\t\t\tbreak;\n 405:\t\t\tbpfptr_add(\u0026u_core_relo, rec_size);\n 406:\t\t}\n 407:\t\treturn err;\n 408:\t}\n 409:\t\n 410:\tint bpf_prepare_btf_info(struct bpf_verifier_env *env,\n 411:\t\t\t\t const union bpf_attr *attr,\n 412:\t\t\t\t bpfptr_t uattr)\n 413:\t{\n 414:\t\tstruct btf *btf;\n 415:\t\tint err;\n 416:\t\n 417:\t\tif (!attr-\u003efunc_info_cnt \u0026\u0026 !attr-\u003eline_info_cnt) {\n 418:\t\t\tif (check_abnormal_return(env))\n 419:\t\t\t\treturn -EINVAL;\n 420:\t\t\treturn 0;\n 421:\t\t}\n 422:\t\n 423:\t\tbtf = btf_get_by_fd(attr-\u003eprog_btf_fd);\n 424:\t\tif (IS_ERR(btf))\n 425:\t\t\treturn PTR_ERR(btf);\n 426:\t\tif (btf_is_kernel(btf)) {\n 427:\t\t\tbtf_put(btf);\n 428:\t\t\treturn -EACCES;\n 429:\t\t}\n 430:\t\tenv-\u003eprog-\u003eaux-\u003ebtf = btf;\n 431:\t\n 432:\t\terr = prepare_btf_func(env, attr, uattr);\n"
} |
| 19/2 | 2026/08/16 02:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 27574 output: 38 thoughts: 2446 Thoughts: **Analysis of BPF Verifier Diagnostic Patch** Okay, let me break this down. My initial thought was whether `bpf_core_apply` could impact `check_subprogs`, which deals with subprogram boundaries and jumps. Since CO-RE relocations primarily touch the `imm` field and not jump offsets or opcodes (except for NOPs or direct value loads), I figured `check_subprogs` should still be safe to run after `bpf_check_btf_info`. The potential for a NOP, while a valid alteration, doesn't introduce or alter jumps. Next, I looked for any other functional changes beyond this reordering. The commit log and the code changes point almost entirely toward improved diagnostics – better error messages, clearer hints, that sort of thing. The main structural shift is the move of `check_subprogs` *after* `bpf_check_btf_info`. The question is, does this constitute a "significant functional change" that warrants fuzzing? The hint is helpful. Since the changes are essentially improved logging and error reporting, and they don't alter the execution flow of *valid* BPF programs, I need to evaluate the impact on invalid programs and the verifier itself. The change alters the verifier's failure path, but the fundamental checks remain the same. A key point is the rationale for the reordering: prioritize BTF and CO-RE errors *before* subprogram layout errors. The idea is to give users the most relevant error messages first. This makes the user experience better with error messages. Even if there's a problem with subprogram layout and CO-RE/BTF, this prioritizes the CO-RE/BTF error. The verifier's execution path is different in that it will return a different error and produce a different log output. I need to confirm if there are any *potential* for crashes. I reviewed the code. Specifically, the BTF checking code does not depend on the specific structural checks performed by `check_subprogs`, so moving `check_subprogs` doesn't introduce a risk of a crash in `bpf_check_btf_info`. Given that, and considering the explicit guidance to return `WorthFuzzing=false` for diagnostic and error message improvements, I think the answer is clear. |
| 20/2 | 2026/08/16 02:14 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/verifier.c",
"FirstLine": 3033,
"LineCount": 100
}Results: {
"Contents": "3033:\t\n3034:\tstatic int check_subprogs(struct bpf_verifier_env *env)\n3035:\t{\n3036:\t\tint i, subprog_start, subprog_end, off, cur_subprog = 0;\n3037:\t\tstruct bpf_subprog_info *subprog = env-\u003esubprog_info;\n3038:\t\tstruct bpf_insn *insn = env-\u003eprog-\u003einsnsi;\n3039:\t\tint insn_cnt = env-\u003eprog-\u003elen;\n3040:\t\n3041:\t\t/* now check that all jumps are within the same subprog */\n3042:\t\tsubprog_start = subprog[cur_subprog].start;\n3043:\t\tsubprog_end = subprog[cur_subprog + 1].start;\n3044:\t\tfor (i = 0; i \u003c insn_cnt; i++) {\n3045:\t\t\tu8 code = insn[i].code;\n3046:\t\n3047:\t\t\tif (BPF_CLASS(code) != BPF_JMP \u0026\u0026 BPF_CLASS(code) != BPF_JMP32)\n3048:\t\t\t\tgoto next;\n3049:\t\t\tif (BPF_OP(code) == BPF_CALL)\n3050:\t\t\t\tgoto next;\n3051:\t\t\tif (BPF_OP(code) == BPF_EXIT) {\n3052:\t\t\t\tsubprog[cur_subprog].exit_idx = i;\n3053:\t\t\t\tgoto next;\n3054:\t\t\t}\n3055:\t\t\toff = i + bpf_jmp_offset(\u0026insn[i]) + 1;\n3056:\t\t\tif (off \u003c subprog_start || off \u003e= subprog_end) {\n3057:\t\t\t\tverbose(env, \"jump out of range from insn %d to %d\\n\", i, off);\n3058:\t\t\t\tbpf_diag_program_structure(\n3059:\t\t\t\t\tenv, i, \"jump out of range\",\n3060:\t\t\t\t\t\"Keep branch targets within the same subprogram, or use an explicit subprogram call.\",\n3061:\t\t\t\t\t\"Instruction %d jumps to instruction %d, but subprogram %d only contains instructions %d through %d. \"\n3062:\t\t\t\t\t\"A branch target must stay inside the same subprogram.\",\n3063:\t\t\t\t\ti, off, cur_subprog, subprog_start, subprog_end - 1);\n3064:\t\t\t\treturn -EINVAL;\n3065:\t\t\t}\n3066:\tnext:\n3067:\t\t\tif (i == subprog_end - 1) {\n3068:\t\t\t\t/*\n3069:\t\t\t\t * To avoid fall-through from one subprog into another,\n3070:\t\t\t\t * the last insn of the subprog should be either exit\n3071:\t\t\t\t * or unconditional jump back or bpf_throw call.\n3072:\t\t\t\t */\n3073:\t\t\t\tif (code != (BPF_JMP | BPF_EXIT) \u0026\u0026\n3074:\t\t\t\t code != (BPF_JMP32 | BPF_JA) \u0026\u0026\n3075:\t\t\t\t code != (BPF_JMP | BPF_JA)) {\n3076:\t\t\t\t\tverbose(env, \"last insn is not an exit or jmp\\n\");\n3077:\t\t\t\t\tbpf_diag_program_structure(\n3078:\t\t\t\t\t\tenv, i, \"subprogram can fall through\",\n3079:\t\t\t\t\t\t\"End each subprogram with an exit or an explicit jump that keeps control flow inside the subprogram.\",\n3080:\t\t\t\t\t\t\"Subprogram %d reaches its last instruction %d without an exit or jump, so control could continue into the next subprogram.\",\n3081:\t\t\t\t\t\tcur_subprog, i);\n3082:\t\t\t\t\treturn -EINVAL;\n3083:\t\t\t\t}\n3084:\t\t\t\tsubprog_start = subprog_end;\n3085:\t\t\t\tcur_subprog++;\n3086:\t\t\t\tif (cur_subprog \u003c env-\u003esubprog_cnt)\n3087:\t\t\t\t\tsubprog_end = subprog[cur_subprog + 1].start;\n3088:\t\t\t}\n3089:\t\t}\n3090:\t\treturn 0;\n3091:\t}\n3092:\t\n3093:\t/*\n3094:\t * Sort subprogs in topological order so that leaf subprogs come first and\n3095:\t * their callers come later. This is a DFS post-order traversal of the call\n3096:\t * graph. Scan only reachable instructions (those in the computed postorder) of\n3097:\t * the current subprog to discover callees (direct subprogs and sync\n3098:\t * callbacks).\n3099:\t */\n3100:\tstatic int sort_subprogs_topo(struct bpf_verifier_env *env)\n3101:\t{\n3102:\t\tstruct bpf_subprog_info *si = env-\u003esubprog_info;\n3103:\t\tint *insn_postorder = env-\u003ecfg.insn_postorder;\n3104:\t\tstruct bpf_insn *insn = env-\u003eprog-\u003einsnsi;\n3105:\t\tint cnt = env-\u003esubprog_cnt;\n3106:\t\tint *dfs_stack = NULL;\n3107:\t\tint top = 0, order = 0;\n3108:\t\tint i, ret = 0;\n3109:\t\tu8 *color = NULL;\n3110:\t\n3111:\t\tcolor = kvzalloc_objs(*color, cnt, GFP_KERNEL_ACCOUNT);\n3112:\t\tdfs_stack = kvmalloc_objs(*dfs_stack, cnt, GFP_KERNEL_ACCOUNT);\n3113:\t\tif (!color || !dfs_stack) {\n3114:\t\t\tret = -ENOMEM;\n3115:\t\t\tgoto out;\n3116:\t\t}\n3117:\t\n3118:\t\t/*\n3119:\t\t * DFS post-order traversal.\n3120:\t\t * Color values: 0 = unvisited, 1 = on stack, 2 = done.\n3121:\t\t */\n3122:\t\tfor (i = 0; i \u003c cnt; i++) {\n3123:\t\t\tif (color[i])\n3124:\t\t\t\tcontinue;\n3125:\t\t\tcolor[i] = 1;\n3126:\t\t\tdfs_stack[top++] = i;\n3127:\t\n3128:\t\t\twhile (top \u003e 0) {\n3129:\t\t\t\tint cur = dfs_stack[top - 1];\n3130:\t\t\t\tint po_start = si[cur].postorder_start;\n3131:\t\t\t\tint po_end = si[cur + 1].postorder_start;\n3132:\t\t\t\tbool pushed = false;\n"
} |
| 21/2 | 2026/08/16 02:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 32318 output: 32 thoughts: 3655 Thoughts: **Analysis of BPF Verifier Patch for Functional Impact** Okay, so I've been tasked with evaluating a BPF verifier patch to determine if it introduces any functional changes that necessitate fuzzing. My initial impression, based on the diff, is that this is largely about diagnostics and error reporting, with some code reorganization to support this. I'll need to confirm this rigorously. First, the primary change seems to involve reordering `check_subprogs` *after* `bpf_check_btf_info`. `check_subprogs` validates jump boundaries within subprograms, while `bpf_check_btf_info` uses BTF information to ensure correctness. The key is to understand if this reordering *changes* how valid programs are treated. The patch doesn't *introduce* new checks within `bpf_check_btf_info`, it's primarily ensuring that BTF information aligns with subprogram definitions discovered in `add_subprogs`, which in turn is based on `CALL` instructions. Therefore, assuming the BTF metadata remains consistent and the reordering doesn't expose any new vulnerabilities in how the BTF information is read/interpreted, this reordering shouldn't affect the acceptance of valid programs. I'm focusing on "doesn't affect the acceptance of *valid* programs", as the introduction of new diagnostics might increase the rejection of *invalid* programs or, more likely, provide better error messages for rejected *invalid* programs. Next, I see several diagnostic changes in `kernel/bpf/verifier.c`. These appear to be refinements to error messages for specific kfunc argument checks. This is basically logging and doesn't change what is and isn't accepted as valid BPF code. The changes are about making the error messages more informative or specific about *why* a particular argument is failing validation. I'm also seeing improvements to the error messages around sleepable kfunc calls in the critical section. The original code was throwing an `-EACCES` error in multiple places for various scenarios (a program that wasn't sleepable calling a sleepable kfunc, a program calling it from inside an RCU read lock, etc.). These changes consolidate the checks around `in_sleepable_context()` to handle all those situations in one place. I dug in and verified that the behavior remains the same: the program will be rejected. Now it is just more efficient because some checks are skipped that don't matter because the program is being rejected anyway. The *order* of error reporting *is* changed, and the error messages are refined to suggest how to fix the problem more clearly. There are no functional changes that affect the acceptance of valid programs. I went back and checked `find_subprog_properties` because it was new in the diff. This function populates `has_ld_abs` for each subprogram. This is required by `check_btf_func` so that it doesn't allow LD_ABS instructions in functions that don't return an int. The new function is *exactly* the same logic as what was in `check_subprogs` originally. Its purpose is to pre-calculate `has_ld_abs` before `bpf_check_btf_info` is run to be consistent with the reordering. Based on all this, there are no changes to any of the fundamental checks that *would allow* valid programs that were previously rejected, or *reject* programs that were previously valid. All changes appear to be for improved diagnostic messaging and improved code structure to ensure that better error messaging can be provided. This patch is therefore about "Return WorthFuzzing=false". |
| 22/2 | 2026/08/16 02:14 | tool | grepper |
0mArgs:{
"Expression": "has_tail_call = true",
"PathPrefix": "kernel/bpf/"
}Results: {
"Output": "kernel/bpf/verifier.c=3006=static void find_subprog_properties(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-3019-\t\t insn[i].imm == BPF_FUNC_tail_call) {\nkernel/bpf/verifier.c:3020:\t\t\tsubprog[cur_subprog].has_tail_call = true;\nkernel/bpf/verifier.c-3021-\t\t\tsubprog[cur_subprog].tail_call_reachable = true;\n"
} |
| 23/2 | 2026/08/16 02:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 36925 output: 37 thoughts: 400 Thoughts: **Investigating the `has_tail_call` Flag in BPF Verification** Okay, I was just reviewing this patch and realized I missed something. I thought `has_tail_call` was populated within `find_subprog_properties`. But hold on... the code diff for `find_subprog_properties` *doesn't* actually show it being set there! I was so sure I had seen that flag being populated during that function. Let me refresh my memory and double-check the patch. Wait a second, the diff for `find_subprog_properties` explicitly shows no changes related to `has_tail_call`. This is strange. Now I am curious. Where is `has_tail_call` actually being set? Time to look at the real source code and see how it is working. I'll need to go to `kernel/bpf/verifier.c` and take a look around line 3020 and see what is happening there. |
| 24/2 | 2026/08/16 02:14 | tool | read-file |
0mArgs:{
"File": "kernel/bpf/verifier.c",
"FirstLine": 3006,
"LineCount": 30
}Results: {
"Contents": "3006:\tstatic void find_subprog_properties(struct bpf_verifier_env *env)\n3007:\t{\n3008:\t\tint i, subprog_end, cur_subprog = 0;\n3009:\t\tstruct bpf_subprog_info *subprog = env-\u003esubprog_info;\n3010:\t\tstruct bpf_insn *insn = env-\u003eprog-\u003einsnsi;\n3011:\t\tint insn_cnt = env-\u003eprog-\u003elen;\n3012:\t\n3013:\t\tsubprog_end = subprog[cur_subprog + 1].start;\n3014:\t\tfor (i = 0; i \u003c insn_cnt; i++) {\n3015:\t\t\tu8 code = insn[i].code;\n3016:\t\n3017:\t\t\tif (code == (BPF_JMP | BPF_CALL) \u0026\u0026\n3018:\t\t\t insn[i].src_reg == 0 \u0026\u0026\n3019:\t\t\t insn[i].imm == BPF_FUNC_tail_call) {\n3020:\t\t\t\tsubprog[cur_subprog].has_tail_call = true;\n3021:\t\t\t\tsubprog[cur_subprog].tail_call_reachable = true;\n3022:\t\t\t}\n3023:\t\t\tif (BPF_CLASS(code) == BPF_LD \u0026\u0026\n3024:\t\t\t (BPF_MODE(code) == BPF_ABS || BPF_MODE(code) == BPF_IND))\n3025:\t\t\t\tsubprog[cur_subprog].has_ld_abs = true;\n3026:\t\t\tif (i == subprog_end - 1) {\n3027:\t\t\t\tcur_subprog++;\n3028:\t\t\t\tif (cur_subprog \u003c env-\u003esubprog_cnt)\n3029:\t\t\t\t\tsubprog_end = subprog[cur_subprog + 1].start;\n3030:\t\t\t}\n3031:\t\t}\n3032:\t}\n3033:\t\n3034:\tstatic int check_subprogs(struct bpf_verifier_env *env)\n3035:\t{\n"
} |
| 25/2 | 2026/08/16 02:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 38138 output: 105 thoughts: 1065 Thoughts: **Analyzing the BPF Verifier Patch for Fuzzing Suitability** Okay, so I'm looking at this diff and initially, I was thrown off a bit. I thought the `has_tail_call` check was missing, but on closer inspection, it seems the patch is refactoring some existing code. The key is to see that the `has_tail_call` part was *already* present in the original `check_subprogs` function and simply moved along with `has_ld_abs` during the refactor to `find_subprog_properties`. It wasn't actually a new addition. I missed it at first because it wasn't explicitly shown in the diff, and was simply part of the loop. Now, looking at the bigger picture, this patch is *solely* about improving the verifier's diagnostics. The changes in `bpf_check` are just about reordering checks and in `in_sleepable_context` is about the error reporting path, and `bpf_diag_*` are purely cosmetic. There are no functional changes to the BPF program execution itself. Valid programs will still work as before, and the set of accepted programs is unchanged. This isn't introducing any new code paths or logic that could lead to memory corruption or crashes. The focus here is error message clarity and order, which is really just an improvement in debugging, rather than functionality. So, the changes here are effectively just logging/error reporting enhancements. My understanding of fuzzing is that it's meant to find vulnerabilities or bugs, such as memory corruption, crashes, or logic flaws in *execution*. I went back and reviewed the prompt, just to double-check. It said `WorthFuzzing=false` if there are: * Documentation, Kconfig, or comment changes * Logging or tracepoint changes * Numeric constants or macro changes that *don't* alter execution flow. In this case, the verifier diagnostics are essentially logging/error reporting. Changing the *text* of an error message or the order in which two validation steps are performed when both must pass anyway (regardless of order) doesn't create new attack surface or intricate logic that demands fuzzing. Therefore, no new attack surface. Thus, `WorthFuzzing` should be `false`. |
| 26/2 | 2026/08/16 02:15 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch is entirely focused on improving BPF verifier diagnostics. It updates error messages, adds hints, and reorders the BTF validation and subprogram layout checks to report BTF/CO-RE errors before subprogram layout errors. These are purely decorative and diagnostic changes that do not alter the set of accepted BPF programs or the execution flow of valid programs.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch is entirely focused on improving BPF verifier diagnostics. It updates error messages, adds hints, and reorders the BTF validation and subprogram layout checks to report BTF/CO-RE errors before subprogram layout errors. These are purely decorative and diagnostic changes that do not alter the set of accepted BPF programs or the execution flow of valid programs.",
"WorthFuzzing": false
} |
| 27/1 | 2026/08/16 02:15 | action | If |
0mArgs:{
"WorthFuzzing": false
}Results: null |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|