AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/06 03:57 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": [
    "BPF_JIT",
    "BPF_SYSCALL"
  ],
  "FocusSymbols": [
    "bpf_int_jit_compile",
    "emit_bpf_tail_call",
    "build_prologue",
    "__build_epilogue"
  ],
  "Reasoning": "The patch modifies the BPF JIT compiler for LoongArch to fix the handling of tail call counts (REG_TCC) during BPF-to-BPF calls and tail calls. This is a functional change in the core BPF JIT logic for LoongArch and should be fuzzed to ensure it doesn't introduce regressions or vulnerabilities in BPF program execution.",
  "WorthFuzzing": true
}

1/1 2026/07/06 03:57 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 7f82fc6d52d601c7dadb970b05c81b7718d1448d\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Jul 6 03:57:34 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h\nindex 76b723590023d7..19feae166e1c18 100644\n--- a/arch/loongarch/include/asm/inst.h\n+++ b/arch/loongarch/include/asm/inst.h\n@@ -97,6 +97,7 @@ enum reg2i6_op {\n };\n \n enum reg2i12_op {\n+\tsltui_op\t= 0x09,\n \taddiw_op\t= 0x0a,\n \taddid_op\t= 0x0b,\n \tlu52id_op\t= 0x0c,\n@@ -153,6 +154,8 @@ enum reg3_op {\n \taddd_op\t\t= 0x21,\n \tsubw_op\t\t= 0x22,\n \tsubd_op\t\t= 0x23,\n+\tmaskeqz_op\t= 0x26,\n+\tmasknez_op\t= 0x27,\n \tnor_op\t\t= 0x28,\n \tand_op\t\t= 0x29,\n \tor_op\t\t= 0x2a,\n@@ -644,6 +647,7 @@ static inline void emit_##NAME(union loongarch_instruction *insn,\t\\\n \tinsn-\u003ereg2i12_format.rj = rj;\t\t\t\t\t\\\n }\n \n+DEF_EMIT_REG2I12_FORMAT(sltui, sltui_op)\n DEF_EMIT_REG2I12_FORMAT(addiw, addiw_op)\n DEF_EMIT_REG2I12_FORMAT(addid, addid_op)\n DEF_EMIT_REG2I12_FORMAT(lu52id, lu52id_op)\n@@ -749,6 +753,8 @@ DEF_EMIT_REG3_FORMAT(divd, divd_op)\n DEF_EMIT_REG3_FORMAT(modd, modd_op)\n DEF_EMIT_REG3_FORMAT(divdu, divdu_op)\n DEF_EMIT_REG3_FORMAT(moddu, moddu_op)\n+DEF_EMIT_REG3_FORMAT(maskeqz, maskeqz_op)\n+DEF_EMIT_REG3_FORMAT(masknez, masknez_op)\n DEF_EMIT_REG3_FORMAT(and, and_op)\n DEF_EMIT_REG3_FORMAT(or, or_op)\n DEF_EMIT_REG3_FORMAT(xor, xor_op)\ndiff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c\nindex ad7e28375aa951..b56ae7ed3e06ed 100644\n--- a/arch/loongarch/net/bpf_jit.c\n+++ b/arch/loongarch/net/bpf_jit.c\n@@ -52,50 +52,56 @@ static void prepare_bpf_tail_call_cnt(struct jit_ctx *ctx, int *store_offset)\n \tconst struct bpf_prog *prog = ctx-\u003eprog;\n \tconst bool is_main_prog = !bpf_is_subprog(prog);\n \n+\t*store_offset -= sizeof(long);\n \tif (is_main_prog) {\n-\t\t/*\n-\t\t * LOONGARCH_GPR_T3 = MAX_TAIL_CALL_CNT\n-\t\t * if (REG_TCC \u003e T3 )\n-\t\t *\tstd REG_TCC -\u003e LOONGARCH_GPR_SP + store_offset\n-\t\t * else\n-\t\t *\tstd REG_TCC -\u003e LOONGARCH_GPR_SP + store_offset\n-\t\t *\tREG_TCC = LOONGARCH_GPR_SP + store_offset\n-\t\t *\n-\t\t * std REG_TCC -\u003e LOONGARCH_GPR_SP + store_offset\n-\t\t *\n-\t\t * The purpose of this code is to first push the TCC into stack,\n-\t\t * and then push the address of TCC into stack.\n-\t\t * In cases where bpf2bpf and tailcall are used in combination,\n-\t\t * the value in REG_TCC may be a count or an address,\n-\t\t * these two cases need to be judged and handled separately.\n-\t\t */\n-\t\temit_insn(ctx, addid, LOONGARCH_GPR_T3, LOONGARCH_GPR_ZERO, MAX_TAIL_CALL_CNT);\n-\t\t*store_offset -= sizeof(long);\n-\n-\t\temit_cond_jmp(ctx, BPF_JGT, REG_TCC, LOONGARCH_GPR_T3, 4);\n-\n-\t\t/*\n-\t\t * If REG_TCC \u003c MAX_TAIL_CALL_CNT, the value in REG_TCC is a count,\n-\t\t * push tcc into stack\n-\t\t */\n+\t\t/* Save entrance TCC state (scalar count or kernel pointer) to local 'tcc' slot */\n \t\temit_insn(ctx, std, REG_TCC, LOONGARCH_GPR_SP, *store_offset);\n \n-\t\t/* Push the address of TCC into the REG_TCC */\n-\t\temit_insn(ctx, addid, REG_TCC, LOONGARCH_GPR_SP, *store_offset);\n-\n-\t\temit_uncond_jmp(ctx, 2);\n+\t\t/* Compute the absolute pointer to the local 'tcc' slot */\n+\t\temit_insn(ctx, addid, LOONGARCH_GPR_T7, LOONGARCH_GPR_SP, *store_offset);\n \n \t\t/*\n-\t\t * If REG_TCC \u003e MAX_TAIL_CALL_CNT, the value in REG_TCC is an address,\n-\t\t * push tcc_ptr into stack\n+\t\t * Branchless classification and blending:\n+\t\t *\n+\t\t * In combined bpf2bpf and tailcall scenarios, REG_TCC can carry\n+\t\t * either a scalar count (0 to 33) or an inherited kernel pointer address.\n+\t\t *\n+\t\t * Entrance status for a main program based on REG_TCC physical content:\n+\t\t * 1) Initial Entry (Scalar): Enter via a standard event, where REG_TCC\n+\t\t *    is initialized to a pure scalar 0.\n+\t\t * 2) Inherited Entry (Scalar): Enter via a flat main-to-main tail call,\n+\t\t *    where REG_TCC carries an accumulated scalar count (1 to 33) reloaded\n+\t\t *    from the prior stack frame.\n+\t\t * 3) Inherited Entry (Pointer): Enter via a tail call from a subprogram,\n+\t\t *    where REG_TCC inherits and carries a massive kernel pointer address\n+\t\t *    reloaded from the prior stack frame.\n+\t\t *\n+\t\t * sltui:   Identify the type of REG_TCC.\n+\t\t *          If REG_TCC \u003c MAX_TAIL_CALL_CNT + 1 (scalar 0~33), sets T8 = 1.\n+\t\t *          If REG_TCC \u003e= MAX_TAIL_CALL_CNT + 1 (kernel pointer), sets T8 = 0.\n+\t\t *\n+\t\t * maskeqz: Handle the local stack pointer (T7).\n+\t\t *          If T8 == 1, keeps T7 intact.\n+\t\t *          If T8 == 0, clears T7 to 0.\n+\t\t *\n+\t\t * masknez: Handle the incoming REG_TCC to prevent register pollution.\n+\t\t *          If T8 == 1, clears REG_TCC to 0 (erasing 1~33 scalars).\n+\t\t *          If T8 == 0, keeps the massive kernel pointer intact.\n+\t\t *\n+\t\t * or:      Combine the results.\n+\t\t *          Scalar Case:  REG_TCC = 0 | T7       -\u003e Clean local stack pointer.\n+\t\t *          Pointer Case: REG_TCC = REG_TCC | 0  -\u003e Clean inherited global pointer.\n \t\t */\n-\t\temit_insn(ctx, std, REG_TCC, LOONGARCH_GPR_SP, *store_offset);\n+\t\temit_insn(ctx, sltui, LOONGARCH_GPR_T8, REG_TCC, MAX_TAIL_CALL_CNT + 1);\n+\t\temit_insn(ctx, maskeqz, LOONGARCH_GPR_T7, LOONGARCH_GPR_T7, LOONGARCH_GPR_T8);\n+\t\temit_insn(ctx, masknez, REG_TCC, REG_TCC, LOONGARCH_GPR_T8);\n+\t\temit_insn(ctx, or, REG_TCC, REG_TCC, LOONGARCH_GPR_T7);\n \t} else {\n-\t\t*store_offset -= sizeof(long);\n+\t\t/* Subprograms: backup the verified TCC pointer inherited via REG_TCC */\n \t\temit_insn(ctx, std, REG_TCC, LOONGARCH_GPR_SP, *store_offset);\n \t}\n \n-\t/* Push tcc_ptr into stack */\n+\t/* Store the finalized TCC pointer value securely into the local 'tcc_ptr' slot */\n \t*store_offset -= sizeof(long);\n \temit_insn(ctx, std, REG_TCC, LOONGARCH_GPR_SP, *store_offset);\n }\n@@ -124,6 +130,9 @@ static void prepare_bpf_tail_call_cnt(struct jit_ctx *ctx, int *store_offset)\n  *                            |           tcc           |\n  *                            +-------------------------+\n  *                            |           tcc_ptr       |\n+ *                            +-------------------------+\n+ *                            |           arena         |\n+ *                            |         (optional)      |\n  *                            +-------------------------+ \u003c--BPF_REG_FP\n  *                            |  prog-\u003eaux-\u003estack_depth |\n  *                            |        (optional)       |\n@@ -145,7 +154,7 @@ static void build_prologue(struct jit_ctx *ctx)\n \tstack_adjust += sizeof(long) * 2;\n \n \tif (ctx-\u003earena_vm_start)\n-\t\tstack_adjust += 8;\n+\t\tstack_adjust += sizeof(long);\n \n \tstack_adjust = round_up(stack_adjust, 16);\n \tstack_adjust += bpf_stack_adjust;\n@@ -194,13 +203,13 @@ static void build_prologue(struct jit_ctx *ctx)\n \tstore_offset -= sizeof(long);\n \temit_insn(ctx, std, LOONGARCH_GPR_S5, LOONGARCH_GPR_SP, store_offset);\n \n+\tprepare_bpf_tail_call_cnt(ctx, \u0026store_offset);\n+\n \tif (ctx-\u003earena_vm_start) {\n \t\tstore_offset -= sizeof(long);\n \t\temit_insn(ctx, std, REG_ARENA, LOONGARCH_GPR_SP, store_offset);\n \t}\n \n-\tprepare_bpf_tail_call_cnt(ctx, \u0026store_offset);\n-\n \temit_insn(ctx, addid, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, stack_adjust);\n \n \tif (bpf_stack_adjust)\n@@ -241,21 +250,18 @@ static void __build_epilogue(struct jit_ctx *ctx, bool is_tail_call)\n \tload_offset -= sizeof(long);\n \temit_insn(ctx, ldd, LOONGARCH_GPR_S5, LOONGARCH_GPR_SP, load_offset);\n \n+\t/* Only restore the TCC state into REG_TCC from the higher slot */\n+\tload_offset -= sizeof(long);\n+\temit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, load_offset);\n+\n+\t/* Skip the unused local 'tcc_ptr' slot to align with arena */\n+\tload_offset -= sizeof(long);\n+\n \tif (ctx-\u003earena_vm_start) {\n \t\tload_offset -= sizeof(long);\n \t\temit_insn(ctx, ldd, REG_ARENA, LOONGARCH_GPR_SP, load_offset);\n \t}\n \n-\t/*\n-\t * When push into the stack, follow the order of tcc then tcc_ptr.\n-\t * When pop from the stack, first pop tcc_ptr then followed by tcc.\n-\t */\n-\tload_offset -= 2 * sizeof(long);\n-\temit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, load_offset);\n-\n-\tload_offset += sizeof(long);\n-\temit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, load_offset);\n-\n \temit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, stack_adjust);\n \n \tif (!is_tail_call) {\n@@ -290,17 +296,13 @@ bool bpf_jit_supports_far_kfunc_call(void)\n \n static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn)\n {\n-\tint off, tc_ninsn = 0;\n+\tint off, jmp_offset;\n \tint tcc_ptr_off = BPF_TAIL_CALL_CNT_PTR_STACK_OFF(ctx-\u003estack_size);\n \tu8 a1 = LOONGARCH_GPR_A1;\n \tu8 a2 = LOONGARCH_GPR_A2;\n \tu8 t1 = LOONGARCH_GPR_T1;\n \tu8 t2 = LOONGARCH_GPR_T2;\n \tu8 t3 = LOONGARCH_GPR_T3;\n-\tconst int idx0 = ctx-\u003eidx;\n-\n-#define cur_offset (ctx-\u003eidx - idx0)\n-#define jmp_offset (tc_ninsn - (cur_offset))\n \n \t/*\n \t * a0: \u0026ctx\n@@ -310,12 +312,12 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn)\n \t * if (index \u003e= array-\u003emap.max_entries)\n \t *\t goto out;\n \t */\n-\ttc_ninsn = insn ? ctx-\u003eoffset[insn+1] - ctx-\u003eoffset[insn] : ctx-\u003eoffset[0];\n \temit_zext_32(ctx, a2, true);\n \n \toff = offsetof(struct bpf_array, map.max_entries);\n \temit_insn(ctx, ldwu, t1, a1, off);\n \t/* bgeu $a2, $t1, jmp_offset */\n+\tjmp_offset = ctx-\u003eimage ? (ctx-\u003eoffset[insn + 1] - ctx-\u003eidx) : 0;\n \tif (emit_tailcall_jmp(ctx, BPF_JGE, a2, t1, jmp_offset) \u003c 0)\n \t\tgoto toofar;\n \n@@ -326,6 +328,7 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn)\n \temit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, tcc_ptr_off);\n \temit_insn(ctx, ldd, t3, REG_TCC, 0);\n \temit_insn(ctx, addid, t2, LOONGARCH_GPR_ZERO, MAX_TAIL_CALL_CNT);\n+\tjmp_offset = ctx-\u003eimage ? (ctx-\u003eoffset[insn + 1] - ctx-\u003eidx) : 0;\n \tif (emit_tailcall_jmp(ctx, BPF_JSGE, t3, t2, jmp_offset) \u003c 0)\n \t\tgoto toofar;\n \n@@ -340,6 +343,7 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn)\n \toff = offsetof(struct bpf_array, ptrs);\n \temit_insn(ctx, ldd, t2, t2, off);\n \t/* beq $t2, $zero, jmp_offset */\n+\tjmp_offset = ctx-\u003eimage ? (ctx-\u003eoffset[insn + 1] - ctx-\u003eidx) : 0;\n \tif (emit_tailcall_jmp(ctx, BPF_JEQ, t2, LOONGARCH_GPR_ZERO, jmp_offset) \u003c 0)\n \t\tgoto toofar;\n \n@@ -355,8 +359,6 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn)\n toofar:\n \tpr_info_once(\"tail_call: jump too far\\n\");\n \treturn -1;\n-#undef cur_offset\n-#undef jmp_offset\n }\n \n static void emit_store_stack_imm64(struct jit_ctx *ctx, int reg, int stack_off, u64 imm64)\n"
}

2/1 2026/07/06 03:57 agent patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": [
    "BPF_JIT",
    "BPF_SYSCALL"
  ],
  "FocusSymbols": [
    "bpf_int_jit_compile",
    "emit_bpf_tail_call",
    "build_prologue",
    "__build_epilogue"
  ],
  "Reasoning": "The patch modifies the BPF JIT compiler for LoongArch to fix the handling of tail call counts (REG_TCC) during BPF-to-BPF calls and tail calls. This is a functional change in the core BPF JIT logic for LoongArch and should be fuzzed to ensure it doesn't introduce regressions or vulnerabilities in BPF program execution.",
  "WorthFuzzing": true
}

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).

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.
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.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. 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 7f82fc6d52d601c7dadb970b05c81b7718d1448d
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Jul 6 03:57:34 2026 +0000

    syz-cluster: applied patch under review

diff --git a/arch/loongarch/include/asm/inst.h b/arch/loongarch/include/asm/inst.h
index 76b723590023d7..19feae166e1c18 100644
--- a/arch/loongarch/include/asm/inst.h
+++ b/arch/loongarch/include/asm/inst.h
@@ -97,6 +97,7 @@ enum reg2i6_op {
 };
 
 enum reg2i12_op {
+	sltui_op	= 0x09,
 	addiw_op	= 0x0a,
 	addid_op	= 0x0b,
 	lu52id_op	= 0x0c,
@@ -153,6 +154,8 @@ enum reg3_op {
 	addd_op		= 0x21,
 	subw_op		= 0x22,
 	subd_op		= 0x23,
+	maskeqz_op	= 0x26,
+	masknez_op	= 0x27,
 	nor_op		= 0x28,
 	and_op		= 0x29,
 	or_op		= 0x2a,
@@ -644,6 +647,7 @@ static inline void emit_##NAME(union loongarch_instruction *insn,	\
 	insn->reg2i12_format.rj = rj;					\
 }
 
+DEF_EMIT_REG2I12_FORMAT(sltui, sltui_op)
 DEF_EMIT_REG2I12_FORMAT(addiw, addiw_op)
 DEF_EMIT_REG2I12_FORMAT(addid, addid_op)
 DEF_EMIT_REG2I12_FORMAT(lu52id, lu52id_op)
@@ -749,6 +753,8 @@ DEF_EMIT_REG3_FORMAT(divd, divd_op)
 DEF_EMIT_REG3_FORMAT(modd, modd_op)
 DEF_EMIT_REG3_FORMAT(divdu, divdu_op)
 DEF_EMIT_REG3_FORMAT(moddu, moddu_op)
+DEF_EMIT_REG3_FORMAT(maskeqz, maskeqz_op)
+DEF_EMIT_REG3_FORMAT(masknez, masknez_op)
 DEF_EMIT_REG3_FORMAT(and, and_op)
 DEF_EMIT_REG3_FORMAT(or, or_op)
 DEF_EMIT_REG3_FORMAT(xor, xor_op)
diff --git a/arch/loongarch/net/bpf_jit.c b/arch/loongarch/net/bpf_jit.c
index ad7e28375aa951..b56ae7ed3e06ed 100644
--- a/arch/loongarch/net/bpf_jit.c
+++ b/arch/loongarch/net/bpf_jit.c
@@ -52,50 +52,56 @@ static void prepare_bpf_tail_call_cnt(struct jit_ctx *ctx, int *store_offset)
 	const struct bpf_prog *prog = ctx->prog;
 	const bool is_main_prog = !bpf_is_subprog(prog);
 
+	*store_offset -= sizeof(long);
 	if (is_main_prog) {
-		/*
-		 * LOONGARCH_GPR_T3 = MAX_TAIL_CALL_CNT
-		 * if (REG_TCC > T3 )
-		 *	std REG_TCC -> LOONGARCH_GPR_SP + store_offset
-		 * else
-		 *	std REG_TCC -> LOONGARCH_GPR_SP + store_offset
-		 *	REG_TCC = LOONGARCH_GPR_SP + store_offset
-		 *
-		 * std REG_TCC -> LOONGARCH_GPR_SP + store_offset
-		 *
-		 * The purpose of this code is to first push the TCC into stack,
-		 * and then push the address of TCC into stack.
-		 * In cases where bpf2bpf and tailcall are used in combination,
-		 * the value in REG_TCC may be a count or an address,
-		 * these two cases need to be judged and handled separately.
-		 */
-		emit_insn(ctx, addid, LOONGARCH_GPR_T3, LOONGARCH_GPR_ZERO, MAX_TAIL_CALL_CNT);
-		*store_offset -= sizeof(long);
-
-		emit_cond_jmp(ctx, BPF_JGT, REG_TCC, LOONGARCH_GPR_T3, 4);
-
-		/*
-		 * If REG_TCC < MAX_TAIL_CALL_CNT, the value in REG_TCC is a count,
-		 * push tcc into stack
-		 */
+		/* Save entrance TCC state (scalar count or kernel pointer) to local 'tcc' slot */
 		emit_insn(ctx, std, REG_TCC, LOONGARCH_GPR_SP, *store_offset);
 
-		/* Push the address of TCC into the REG_TCC */
-		emit_insn(ctx, addid, REG_TCC, LOONGARCH_GPR_SP, *store_offset);
-
-		emit_uncond_jmp(ctx, 2);
+		/* Compute the absolute pointer to the local 'tcc' slot */
+		emit_insn(ctx, addid, LOONGARCH_GPR_T7, LOONGARCH_GPR_SP, *store_offset);
 
 		/*
-		 * If REG_TCC > MAX_TAIL_CALL_CNT, the value in REG_TCC is an address,
-		 * push tcc_ptr into stack
+		 * Branchless classification and blending:
+		 *
+		 * In combined bpf2bpf and tailcall scenarios, REG_TCC can carry
+		 * either a scalar count (0 to 33) or an inherited kernel pointer address.
+		 *
+		 * Entrance status for a main program based on REG_TCC physical content:
+		 * 1) Initial Entry (Scalar): Enter via a standard event, where REG_TCC
+		 *    is initialized to a pure scalar 0.
+		 * 2) Inherited Entry (Scalar): Enter via a flat main-to-main tail call,
+		 *    where REG_TCC carries an accumulated scalar count (1 to 33) reloaded
+		 *    from the prior stack frame.
+		 * 3) Inherited Entry (Pointer): Enter via a tail call from a subprogram,
+		 *    where REG_TCC inherits and carries a massive kernel pointer address
+		 *    reloaded from the prior stack frame.
+		 *
+		 * sltui:   Identify the type of REG_TCC.
+		 *          If REG_TCC < MAX_TAIL_CALL_CNT + 1 (scalar 0~33), sets T8 = 1.
+		 *          If REG_TCC >= MAX_TAIL_CALL_CNT + 1 (kernel pointer), sets T8 = 0.
+		 *
+		 * maskeqz: Handle the local stack pointer (T7).
+		 *          If T8 == 1, keeps T7 intact.
+		 *          If T8 == 0, clears T7 to 0.
+		 *
+		 * masknez: Handle the incoming REG_TCC to prevent register pollution.
+		 *          If T8 == 1, clears REG_TCC to 0 (erasing 1~33 scalars).
+		 *          If T8 == 0, keeps the massive kernel pointer intact.
+		 *
+		 * or:      Combine the results.
+		 *          Scalar Case:  REG_TCC = 0 | T7       -> Clean local stack pointer.
+		 *          Pointer Case: REG_TCC = REG_TCC | 0  -> Clean inherited global pointer.
 		 */
-		emit_insn(ctx, std, REG_TCC, LOONGARCH_GPR_SP, *store_offset);
+		emit_insn(ctx, sltui, LOONGARCH_GPR_T8, REG_TCC, MAX_TAIL_CALL_CNT + 1);
+		emit_insn(ctx, maskeqz, LOONGARCH_GPR_T7, LOONGARCH_GPR_T7, LOONGARCH_GPR_T8);
+		emit_insn(ctx, masknez, REG_TCC, REG_TCC, LOONGARCH_GPR_T8);
+		emit_insn(ctx, or, REG_TCC, REG_TCC, LOONGARCH_GPR_T7);
 	} else {
-		*store_offset -= sizeof(long);
+		/* Subprograms: backup the verified TCC pointer inherited via REG_TCC */
 		emit_insn(ctx, std, REG_TCC, LOONGARCH_GPR_SP, *store_offset);
 	}
 
-	/* Push tcc_ptr into stack */
+	/* Store the finalized TCC pointer value securely into the local 'tcc_ptr' slot */
 	*store_offset -= sizeof(long);
 	emit_insn(ctx, std, REG_TCC, LOONGARCH_GPR_SP, *store_offset);
 }
@@ -124,6 +130,9 @@ static void prepare_bpf_tail_call_cnt(struct jit_ctx *ctx, int *store_offset)
  *                            |           tcc           |
  *                            +-------------------------+
  *                            |           tcc_ptr       |
+ *                            +-------------------------+
+ *                            |           arena         |
+ *                            |         (optional)      |
  *                            +-------------------------+ <--BPF_REG_FP
  *                            |  prog->aux->stack_depth |
  *                            |        (optional)       |
@@ -145,7 +154,7 @@ static void build_prologue(struct jit_ctx *ctx)
 	stack_adjust += sizeof(long) * 2;
 
 	if (ctx->arena_vm_start)
-		stack_adjust += 8;
+		stack_adjust += sizeof(long);
 
 	stack_adjust = round_up(stack_adjust, 16);
 	stack_adjust += bpf_stack_adjust;
@@ -194,13 +203,13 @@ static void build_prologue(struct jit_ctx *ctx)
 	store_offset -= sizeof(long);
 	emit_insn(ctx, std, LOONGARCH_GPR_S5, LOONGARCH_GPR_SP, store_offset);
 
+	prepare_bpf_tail_call_cnt(ctx, &store_offset);
+
 	if (ctx->arena_vm_start) {
 		store_offset -= sizeof(long);
 		emit_insn(ctx, std, REG_ARENA, LOONGARCH_GPR_SP, store_offset);
 	}
 
-	prepare_bpf_tail_call_cnt(ctx, &store_offset);
-
 	emit_insn(ctx, addid, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, stack_adjust);
 
 	if (bpf_stack_adjust)
@@ -241,21 +250,18 @@ static void __build_epilogue(struct jit_ctx *ctx, bool is_tail_call)
 	load_offset -= sizeof(long);
 	emit_insn(ctx, ldd, LOONGARCH_GPR_S5, LOONGARCH_GPR_SP, load_offset);
 
+	/* Only restore the TCC state into REG_TCC from the higher slot */
+	load_offset -= sizeof(long);
+	emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, load_offset);
+
+	/* Skip the unused local 'tcc_ptr' slot to align with arena */
+	load_offset -= sizeof(long);
+
 	if (ctx->arena_vm_start) {
 		load_offset -= sizeof(long);
 		emit_insn(ctx, ldd, REG_ARENA, LOONGARCH_GPR_SP, load_offset);
 	}
 
-	/*
-	 * When push into the stack, follow the order of tcc then tcc_ptr.
-	 * When pop from the stack, first pop tcc_ptr then followed by tcc.
-	 */
-	load_offset -= 2 * sizeof(long);
-	emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, load_offset);
-
-	load_offset += sizeof(long);
-	emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, load_offset);
-
 	emit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, stack_adjust);
 
 	if (!is_tail_call) {
@@ -290,17 +296,13 @@ bool bpf_jit_supports_far_kfunc_call(void)
 
 static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn)
 {
-	int off, tc_ninsn = 0;
+	int off, jmp_offset;
 	int tcc_ptr_off = BPF_TAIL_CALL_CNT_PTR_STACK_OFF(ctx->stack_size);
 	u8 a1 = LOONGARCH_GPR_A1;
 	u8 a2 = LOONGARCH_GPR_A2;
 	u8 t1 = LOONGARCH_GPR_T1;
 	u8 t2 = LOONGARCH_GPR_T2;
 	u8 t3 = LOONGARCH_GPR_T3;
-	const int idx0 = ctx->idx;
-
-#define cur_offset (ctx->idx - idx0)
-#define jmp_offset (tc_ninsn - (cur_offset))
 
 	/*
 	 * a0: &ctx
@@ -310,12 +312,12 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn)
 	 * if (index >= array->map.max_entries)
 	 *	 goto out;
 	 */
-	tc_ninsn = insn ? ctx->offset[insn+1] - ctx->offset[insn] : ctx->offset[0];
 	emit_zext_32(ctx, a2, true);
 
 	off = offsetof(struct bpf_array, map.max_entries);
 	emit_insn(ctx, ldwu, t1, a1, off);
 	/* bgeu $a2, $t1, jmp_offset */
+	jmp_offset = ctx->image ? (ctx->offset[insn + 1] - ctx->idx) : 0;
 	if (emit_tailcall_jmp(ctx, BPF_JGE, a2, t1, jmp_offset) < 0)
 		goto toofar;
 
@@ -326,6 +328,7 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn)
 	emit_insn(ctx, ldd, REG_TCC, LOONGARCH_GPR_SP, tcc_ptr_off);
 	emit_insn(ctx, ldd, t3, REG_TCC, 0);
 	emit_insn(ctx, addid, t2, LOONGARCH_GPR_ZERO, MAX_TAIL_CALL_CNT);
+	jmp_offset = ctx->image ? (ctx->offset[insn + 1] - ctx->idx) : 0;
 	if (emit_tailcall_jmp(ctx, BPF_JSGE, t3, t2, jmp_offset) < 0)
 		goto toofar;
 
@@ -340,6 +343,7 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn)
 	off = offsetof(struct bpf_array, ptrs);
 	emit_insn(ctx, ldd, t2, t2, off);
 	/* beq $t2, $zero, jmp_offset */
+	jmp_offset = ctx->image ? (ctx->offset[insn + 1] - ctx->idx) : 0;
 	if (emit_tailcall_jmp(ctx, BPF_JEQ, t2, LOONGARCH_GPR_ZERO, jmp_offset) < 0)
 		goto toofar;
 
@@ -355,8 +359,6 @@ static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn)
 toofar:
 	pr_info_once("tail_call: jump too far\n");
 	return -1;
-#undef cur_offset
-#undef jmp_offset
 }
 
 static void emit_store_stack_imm64(struct jit_ctx *ctx, int reg, int stack_off, u64 imm64)

3/2 2026/07/06 03:57 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 5933
							output: 33
							thoughts: 402
						

Thoughts:
**Analyzing a LoongArch BPF JIT Patch**

Okay, so I'm looking at this patch. It's touching the BPF JIT compiler specifically for LoongArch, the `arch/loongarch/net/bpf_jit.c` file, and adding some definitions in `arch/loongarch/include/asm/inst.h`. Right off the bat, this is a very architecture-specific change. It looks like it's addressing the handling of `REG_TCC`, the tail call count, in the BPF JIT on LoongArch. This means changes to how the register is saved, restored, and how tail calls are generated. Definitely functional code, no question, and probably performance critical.

Now, the immediate question is, how does this interact with fuzzing? This patch is isolated under `arch/loongarch/`, so it's going to need a LoongArch machine or emulator to be exercised. My initial thought is syzkaller is usually x86_64, arm64, maybe riscv. It *does* run on LoongArch, right? I seem to remember some LoongArch support. QEMU does support LoongArch, so we can test this in virtual environments, right? 

Wait, the instructions are warning about unreachable code paths in virtualized environments. But this is the catch: Since LoongArch can be emulated by QEMU, *if* the patch is LoongArch-specific, which it looks like it is, we can build a LoongArch kernel and fuzz it that way. I'd better double check to confirm that we build the kernel with the proper configs!

Let's see, what specific configs do we need? Oh yeah, `CONFIG_BPF_JIT` is definitely a prerequisite. Let's start by looking at `arch/loongarch/net/bpf_jit.c` to see if that is activated.



4/2 2026/07/06 03:57 tool grepper
0m Args:
{
  "Expression": "CONFIG_BPF_JIT",
  "PathPrefix": "arch/loongarch/"
}

Results:
{
  "Output": "arch/loongarch/configs/loongson64_defconfig=7=CONFIG_BPF_SYSCALL=y\narch/loongarch/configs/loongson64_defconfig:8:CONFIG_BPF_JIT=y\narch/loongarch/configs/loongson64_defconfig-9-CONFIG_PREEMPT=y\n--\narch/loongarch/include/asm/extable.h=26=do {\t\t\t\t\t\t\t\\\n--\narch/loongarch/include/asm/extable.h-34-\narch/loongarch/include/asm/extable.h:35:#ifdef CONFIG_BPF_JIT\narch/loongarch/include/asm/extable.h-36-bool ex_handler_bpf(const struct exception_table_entry *ex, struct pt_regs *regs);\n--\narch/loongarch/include/asm/extable.h=39=bool ex_handler_bpf(const struct exception_table_entry *ex, struct pt_regs *regs)\n--\narch/loongarch/include/asm/extable.h-42-}\narch/loongarch/include/asm/extable.h:43:#endif /* !CONFIG_BPF_JIT */\narch/loongarch/include/asm/extable.h-44-\n--\narch/loongarch/net/Makefile-6-#\narch/loongarch/net/Makefile:7:obj-$(CONFIG_BPF_JIT) += bpf_jit.o\n"
}

5/2 2026/07/06 03:57 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 7161
							output: 42
							thoughts: 188
						

Thoughts:
**Analyzing the LoongArch BPF JIT Patch**

Okay, so this patch is digging into `arch/loongarch/net/bpf_jit.c`. Makes sense, we're dealing with the LoongArch architecture and BPF JIT compilation here. I'll need to remember that this requires `CONFIG_BPF_JIT` and `CONFIG_BPF_SYSCALL` enabled, that's crucial for context. If I'm going to flag this for fuzzing, which I am leaning towards given the modification location, I'll need to list those two config options.

Now, let's see, what specifically is being touched? The main functions in `arch/loongarch/net/bpf_jit.c` are `prepare_bpf_tail_call_cnt`, `build_prologue`, `__build_epilogue`, and `emit_bpf_tail_call`. Ah, they are all internal to the BPF JIT compile process, being static functions used by `bpf_int_jit_compile`. These are good candidates to focus on during fuzzing as they form the core of the JIT compilation process. This will be an interesting one to follow up on.



6/2 2026/07/06 03:57 tool grepper
0m Args:
{
  "Expression": "bpf_int_jit_compile",
  "PathPrefix": "arch/loongarch/net/bpf_jit.c"
}

Results:
{
  "Output": "arch/loongarch/net/bpf_jit.c=2184=int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,\n--\narch/loongarch/net/bpf_jit.c-2198-\narch/loongarch/net/bpf_jit.c:2199:struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)\narch/loongarch/net/bpf_jit.c-2200-{\n"
}

7/2 2026/07/06 03:57 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 7814
							output: 44
							thoughts: 25
						

Thoughts:
Okay, here's my interpretation of those thoughts, crafted with the understanding that I'm speaking to a fellow expert:

**Tracing the JIT Compilation Process**

Alright, my initial thought is always to zero in on the entry point. In this case, it looks like `bpf_int_jit_compile` is where things kick off. Makes perfect sense. That's the logical starting place. Now, before I dive deep into this specific function, I'm going to quickly broaden the scope. I need to get a sense of the *entire* landscape. My next action will be to identify and map out related functions. I'm thinking other functions may also be involved in the JIT compilation process. I'll need to know these to fully understand the flow of execution. Let's see what else is out there.



8/2 2026/07/06 03:57 tool grepper
0m Args:
{
  "Expression": "prepare_bpf_tail_call_cnt",
  "PathPrefix": "arch/loongarch/net/bpf_jit.c"
}

Results:
{
  "Output": "arch/loongarch/net/bpf_jit.c=30=static const int regmap[] = {\n--\narch/loongarch/net/bpf_jit.c-49-\narch/loongarch/net/bpf_jit.c:50:static void prepare_bpf_tail_call_cnt(struct jit_ctx *ctx, int *store_offset)\narch/loongarch/net/bpf_jit.c-51-{\n--\narch/loongarch/net/bpf_jit.c=142=static void build_prologue(struct jit_ctx *ctx)\n--\narch/loongarch/net/bpf_jit.c-205-\narch/loongarch/net/bpf_jit.c:206:\tprepare_bpf_tail_call_cnt(ctx, \u0026store_offset);\narch/loongarch/net/bpf_jit.c-207-\n"
}

9/2 2026/07/06 03:57 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 8300
							output: 26
							thoughts: 210
						

Thoughts:
**Focusing on BPF JIT Compilation for LoongArch**

Okay, so I need to zero in on the core of this. Let's see... `bpf_int_jit_compile` – that's the main entry point for the JIT compiler, right? It's where the magic happens when a BPF program is loaded. It's not a hot-path function, not like packet processing, but it *is* the central function for the BPF JIT process itself. So, yeah, focusing on `bpf_int_jit_compile` as the primary symbol to investigate makes perfect sense. I should probably also keep an eye on related functions like `build_prologue`, `__build_epilogue`, and `emit_bpf_tail_call` as part of this.

But, is it even worth fuzzing? Considering that this is a functional change related to how tail call counts are handled in the LoongArch BPF JIT compiler, and therefore a more complete implementation, then yes, it warrants a closer look.

Let me just double-check, but the configurations that enable the relevant portions of the kernel are `BPF_JIT` and `BPF_SYSCALL` to target that functionality.



10/2 2026/07/06 03:57 tool grepper
0m Args:
{
  "Expression": "config BPF_JIT",
  "PathPrefix": ""
}

Results:
{
  "Output": "kernel/bpf/Kconfig=27=config BPF_SYSCALL\n--\nkernel/bpf/Kconfig-41-\nkernel/bpf/Kconfig:42:config BPF_JIT\nkernel/bpf/Kconfig-43-\tbool \"Enable BPF Just In Time compiler\"\n--\nkernel/bpf/Kconfig-57-\nkernel/bpf/Kconfig:58:config BPF_JIT_ALWAYS_ON\nkernel/bpf/Kconfig-59-\tbool \"Permanently enable BPF JIT and remove BPF interpreter\"\n--\nkernel/bpf/Kconfig-68-\nkernel/bpf/Kconfig:69:config BPF_JIT_DEFAULT_ON\nkernel/bpf/Kconfig-70-\tdef_bool ARCH_WANT_DEFAULT_BPF_JIT || BPF_JIT_ALWAYS_ON\n"
}

11/2 2026/07/06 03:57 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 9028
							output: 42
							thoughts: 315
						

Thoughts:
**Analyzing the LoongArch BPF JIT Patch**

Okay, so I'm looking at this patch for the LoongArch architecture and the first things that jump out are the `BPF_JIT` and `BPF_SYSCALL` configurations. Those feel like the correct starting points. Just to be sure, I'll quickly scan for any other potentially relevant configurations.

Hmm, this patch is using `MAX_TAIL_CALL_CNT`, which is a pretty standard BPF macro. The actual file being modified is `arch/loongarch/net/bpf_jit.c`. Yep, seems like `BPF_JIT` is the critical configuration here.

The core of this is about fixing the BPF JIT compiler on LoongArch, specifically how it handles tail call counts – `REG_TCC` – during BPF-to-BPF calls and tail calls. This is a functional change deep within the BPF JIT logic, so we absolutely need to fuzz this to make sure we don't introduce any regressions or vulnerabilities in BPF program execution.

Now, let's identify the key symbols. I see `bpf_int_jit_compile`, `build_prologue`, `__build_epilogue`, and `emit_bpf_tail_call`. Wait, `build_prologue` and `__build_epilogue` are static functions! Syzkaller could definitely focus on those if we give it their names, but for now I'll just focus on `bpf_int_jit_compile` and `emit_bpf_tail_call`.

I'll check `emit_bpf_tail_call` in `arch/loongarch/net/bpf_jit.c` immediately.



12/2 2026/07/06 03:58 tool grepper
0m Args:
{
  "Expression": "emit_bpf_tail_call",
  "PathPrefix": "arch/loongarch/net/bpf_jit.c"
}

Results:
{
  "Output": "arch/loongarch/net/bpf_jit.c=292=bool bpf_jit_supports_far_kfunc_call(void)\n--\narch/loongarch/net/bpf_jit.c-296-\narch/loongarch/net/bpf_jit.c:297:static int emit_bpf_tail_call(struct jit_ctx *ctx, int insn)\narch/loongarch/net/bpf_jit.c-298-{\n--\narch/loongarch/net/bpf_jit.c=717=static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool extra_pass)\n--\narch/loongarch/net/bpf_jit.c-1215-\tcase BPF_JMP | BPF_TAIL_CALL:\narch/loongarch/net/bpf_jit.c:1216:\t\tif (emit_bpf_tail_call(ctx, i) \u003c 0)\narch/loongarch/net/bpf_jit.c-1217-\t\t\treturn -EINVAL;\n"
}

13/2 2026/07/06 03:58 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 10058
							output: 163
							thoughts: 24
						

Thoughts:
Okay, here's the summary, rendered as a first-person thought process, keeping in mind the user is an expert:

**Reflecting on `emit_bpf_tail_call`**

Right, `emit_bpf_tail_call`… that's a static function, yes. Got it. No ambiguity there. I need to make a note of this.

Now, let's proceed to set up and verify the results. This function calls for careful attention, as any incorrect parameters can produce undesirable results. Time to get to work.



14/2 2026/07/06 03:58 tool set-results
0m Args:
{
  "EnableConfigs": [
    "BPF_JIT",
    "BPF_SYSCALL"
  ],
  "FocusSymbols": [
    "bpf_int_jit_compile",
    "emit_bpf_tail_call",
    "build_prologue",
    "__build_epilogue"
  ],
  "Reasoning": "The patch modifies the BPF JIT compiler for LoongArch to fix the handling of tail call counts (REG_TCC) during BPF-to-BPF calls and tail calls. This is a functional change in the core BPF JIT logic for LoongArch and should be fuzzed to ensure it doesn't introduce regressions or vulnerabilities in BPF program execution.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "BPF_JIT",
    "BPF_SYSCALL"
  ],
  "FocusSymbols": [
    "bpf_int_jit_compile",
    "emit_bpf_tail_call",
    "build_prologue",
    "__build_epilogue"
  ],
  "Reasoning": "The patch modifies the BPF JIT compiler for LoongArch to fix the handling of tail call counts (REG_TCC) during BPF-to-BPF calls and tail calls. This is a functional change in the core BPF JIT logic for LoongArch and should be fuzzed to ensure it doesn't introduce regressions or vulnerabilities in BPF program execution.",
  "WorthFuzzing": true
}

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