AI Trajectory


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

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL",
    "BPF_JIT"
  ],
  "FocusSymbols": [
    "bpf_do_misc_fixups",
    "bpf_patch_list_commit",
    "bpf_patch_list_add",
    "bpf_patch_list_free",
    "bpf_insn_array_remap"
  ],
  "KMSANReasoning": "The patch series replaces repeated individual instruction patching during bpf_do_misc_fixups() with a batched instruction patching mechanism (bpf_patch_list_add and bpf_patch_list_commit).\n\nAnalysis of memory initialization and info-leak risks:\n1. All allocated structures and arrays (pl-\u003epatches, pl-\u003einsns, new_off, intermediate insns, env-\u003einsn_aux_data, and reallocated bpf_prog) are completely initialized:\n   - pl-\u003epatches and pl-\u003einsns have their elements fully populated upon queueing in bpf_patch_list_add().\n   - new_off is allocated for cnt + 1 elements and fully filled in the loop covering 0 to cnt.\n   - insns is populated for every offset in [0, new_cnt) through contiguous partition slices.\n   - env-\u003einsn_aux_data is reallocated with __GFP_ZERO, zeroed via memset for new patch slots, and initialized through patch_list_adj_aux().\n   - Local variables in helper functions (e.g., insn_get_rel, patch_list_adj_insn) are explicitly assigned before use.\n2. No uninitialized memory or structure padding is exposed to userspace.\n3. Potential bugs in offset calculation, jump relocation, or buffer resizing would manifest as out-of-bounds memory accesses or invalid control flow, which are covered by KASAN.\n\nTherefore, the changes do not expose uninitialized memory risks that would necessitate a dedicated KMSAN fuzzing session.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch replaces the incremental instruction patching in bpf_do_misc_fixups() with a deferred, batched instruction patching mechanism (bpf_patch_list_add and bpf_patch_list_commit). This involves complex jump target recalculations, instruction aux data remapping, subprog offset adjustments, and memory reallocations in the BPF verifier. The code is directly reachable from userspace via the bpf() system call when loading BPF programs. Fuzzing is critical to detect potential branch offset calculation bugs, out-of-bounds array access, or invariant violations during instruction rewriting.",
  "WorthFuzzing": true
}

1/1 2026/10/02 05:48 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 4a0d28041e8f5b17363997575245b86c94c1991c\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Fri Oct 2 05:48:19 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/include/linux/bpf.h b/include/linux/bpf.h\nindex 4bae3796c42f6..17932ff8b0ae9 100644\n--- a/include/linux/bpf.h\n+++ b/include/linux/bpf.h\n@@ -4299,6 +4299,7 @@ int bpf_insn_array_ready(struct bpf_map *map);\n void bpf_insn_array_release(struct bpf_map *map);\n void bpf_insn_array_adjust(struct bpf_map *map, u32 first, u32 len);\n void bpf_insn_array_adjust_after_remove(struct bpf_map *map, u32 off, u32 len);\n+void bpf_insn_array_remap(struct bpf_map *map, const u32 *new_off, u32 cnt, u32 grow);\n \n #ifdef CONFIG_BPF_SYSCALL\n void bpf_prog_update_insn_ptrs(struct bpf_prog *prog, u32 *offsets, void *image);\ndiff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h\nindex 811342e3c0419..5a968539589f8 100644\n--- a/include/linux/bpf_verifier.h\n+++ b/include/linux/bpf_verifier.h\n@@ -929,6 +929,27 @@ struct bpf_fd_array {\n \t};\n };\n \n+/* one deferred patch: insn 'off' is replaced by 'len' insns */\n+struct bpf_insn_patch {\n+\tu32 off;\n+\tu32 len;\n+\tu32 start;\t\t/* first insn of the patch in bpf_patch_list.insns */\n+\tstruct bpf_insn orig;\t/* the replaced insn when the patch was queued */\n+};\n+\n+/*\n+ * Patches queued by a rewrite pass, applied together by\n+ * bpf_patch_list_commit() in O(prog-\u003elen + inserted insns).\n+ */\n+struct bpf_patch_list {\n+\tstruct bpf_insn_patch *patches;\n+\tstruct bpf_insn *insns;\n+\tu32 cnt;\n+\tu32 cap;\n+\tu32 insn_cnt;\n+\tu32 insn_cap;\n+};\n+\n /* single container for all structs\n  * one verifier_env per bpf_check() call\n  */\n@@ -973,6 +994,7 @@ struct bpf_verifier_env {\n \tbool signature;\n \tu32 insn_aux_data_len;\n \tstruct bpf_insn_aux_data *insn_aux_data; /* array of per-insn state */\n+\tstruct bpf_patch_list patch_list;\n \tconst struct bpf_line_info *prev_linfo;\n \tstruct bpf_verifier_log log;\n \tstruct bpf_diag *diag;\n@@ -1834,5 +1856,9 @@ int bpf_jit_subprogs(struct bpf_verifier_env *env);\n int bpf_fixup_call_args(struct bpf_verifier_env *env);\n int bpf_do_misc_fixups(struct bpf_verifier_env *env);\n int bpf_insn_def32(struct bpf_prog *prog, struct bpf_insn *insn);\n+struct bpf_insn *bpf_patch_list_add(struct bpf_verifier_env *env, u32 off,\n+\t\t\t\t    const struct bpf_insn *patch, u32 len);\n+int bpf_patch_list_commit(struct bpf_verifier_env *env);\n+void bpf_patch_list_free(struct bpf_verifier_env *env);\n \n #endif /* _LINUX_BPF_VERIFIER_H */\ndiff --git a/kernel/bpf/bpf_insn_array.c b/kernel/bpf/bpf_insn_array.c\nindex 92bbb71576ae3..74f07ced85cb0 100644\n--- a/kernel/bpf/bpf_insn_array.c\n+++ b/kernel/bpf/bpf_insn_array.c\n@@ -253,6 +253,24 @@ void bpf_insn_array_adjust(struct bpf_map *map, u32 first, u32 len)\n \t}\n }\n \n+/*\n+ * Move each offset to new_off[offset], new_off[] covers [0, cnt]. Offsets\n+ * past the end of the old program only move by 'grow'.\n+ */\n+void bpf_insn_array_remap(struct bpf_map *map, const u32 *new_off, u32 cnt, u32 grow)\n+{\n+\tstruct bpf_insn_array *insn_array = cast_insn_array(map);\n+\tu32 off;\n+\tint i;\n+\n+\tfor (i = 0; i \u003c map-\u003emax_entries; i++) {\n+\t\toff = insn_array-\u003evalues[i].xlated_off;\n+\t\tif (off == INSN_DELETED)\n+\t\t\tcontinue;\n+\t\tinsn_array-\u003evalues[i].xlated_off = off \u003c= cnt ? new_off[off] : off + grow;\n+\t}\n+}\n+\n void bpf_insn_array_adjust_after_remove(struct bpf_map *map, u32 off, u32 len)\n {\n \tstruct bpf_insn_array *insn_array = cast_insn_array(map);\ndiff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c\nindex 37cf130ebb57b..0e8c61ede9a5b 100644\n--- a/kernel/bpf/fixups.c\n+++ b/kernel/bpf/fixups.c\n@@ -404,6 +404,301 @@ struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,\n \treturn __bpf_patch_insn_data(env, off, patch, len, BPF_PATCH_KEEP_TARGET);\n }\n \n+/*\n+ * Queue the replacement of insn 'off' by 'patch[0..len)'. Nothing moves until\n+ * bpf_patch_list_commit(), so a pass keeps addressing insns and their aux data\n+ * by their index in env-\u003eprog. Patches must be queued in increasing order of\n+ * 'off'. Jumps in the patch that leave it are relative to the patch placed at\n+ * 'off', the same way as for bpf_patch_insn_data().\n+ *\n+ * Return the copy of the last insn of the patch, which stays valid until the\n+ * next call.\n+ */\n+struct bpf_insn *bpf_patch_list_add(struct bpf_verifier_env *env, u32 off,\n+\t\t\t\t    const struct bpf_insn *patch, u32 len)\n+{\n+\tstruct bpf_patch_list *pl = \u0026env-\u003epatch_list;\n+\tstruct bpf_insn_patch *p;\n+\tstruct bpf_insn *insns;\n+\tu32 cap;\n+\n+\tif (bpf_rewrite_must_abort())\n+\t\treturn ERR_PTR(-EINTR);\n+\n+\tif (verifier_bug_if(!len || off \u003e= env-\u003eprog-\u003elen ||\n+\t\t\t    (pl-\u003ecnt \u0026\u0026 off \u003c= pl-\u003epatches[pl-\u003ecnt - 1].off),\n+\t\t\t    env, \"insn %u patched out of order\", off))\n+\t\treturn ERR_PTR(-EFAULT);\n+\n+\t/* Replacing one insn by one moves nothing, do it right away. */\n+\tif (len == 1) {\n+\t\tenv-\u003eprog-\u003einsnsi[off] = *patch;\n+\t\tadjust_insn_aux_data(env, env-\u003eprog, off, 1, NULL,\n+\t\t\t\t     BPF_PATCH_KEEP_TARGET);\n+\t\treturn \u0026env-\u003eprog-\u003einsnsi[off];\n+\t}\n+\n+\tif (pl-\u003ecnt == pl-\u003ecap) {\n+\t\tcap = max(pl-\u003ecap * 2, 16U);\n+\t\tp = kvrealloc(pl-\u003epatches, array_size(cap, sizeof(*p)),\n+\t\t\t      GFP_KERNEL_ACCOUNT);\n+\t\tif (!p)\n+\t\t\treturn ERR_PTR(-ENOMEM);\n+\t\tpl-\u003epatches = p;\n+\t\tpl-\u003ecap = cap;\n+\t}\n+\n+\tif (pl-\u003einsn_cnt + len \u003e pl-\u003einsn_cap) {\n+\t\tcap = max3(pl-\u003einsn_cap * 2, pl-\u003einsn_cnt + len, 64U);\n+\t\tinsns = kvrealloc(pl-\u003einsns, array_size(cap, sizeof(*insns)),\n+\t\t\t\t  GFP_KERNEL_ACCOUNT);\n+\t\tif (!insns)\n+\t\t\treturn ERR_PTR(-ENOMEM);\n+\t\tpl-\u003einsns = insns;\n+\t\tpl-\u003einsn_cap = cap;\n+\t}\n+\n+\tp = \u0026pl-\u003epatches[pl-\u003ecnt++];\n+\tp-\u003eoff = off;\n+\tp-\u003elen = len;\n+\tp-\u003estart = pl-\u003einsn_cnt;\n+\tp-\u003eorig = env-\u003eprog-\u003einsnsi[off];\n+\tmemcpy(pl-\u003einsns + p-\u003estart, patch, len * sizeof(*patch));\n+\tpl-\u003einsn_cnt += len;\n+\n+\treturn \u0026pl-\u003einsns[p-\u003estart + len - 1];\n+}\n+\n+void bpf_patch_list_free(struct bpf_verifier_env *env)\n+{\n+\tstruct bpf_patch_list *pl = \u0026env-\u003epatch_list;\n+\n+\tkvfree(pl-\u003epatches);\n+\tkvfree(pl-\u003einsns);\n+\tmemset(pl, 0, sizeof(*pl));\n+}\n+\n+/* Extract the pc-relative operand of a jump, a subprog call or a ld_imm64 of a subprog. */\n+static bool insn_get_rel(const struct bpf_insn *insn, s64 *rel, bool *is_imm)\n+{\n+\tu8 code = insn-\u003ecode;\n+\n+\tif (bpf_pseudo_func(insn) || bpf_pseudo_call(insn)) {\n+\t\t*rel = insn-\u003eimm;\n+\t\t*is_imm = true;\n+\t\treturn true;\n+\t}\n+\tif ((BPF_CLASS(code) != BPF_JMP \u0026\u0026 BPF_CLASS(code) != BPF_JMP32) ||\n+\t    BPF_OP(code) == BPF_CALL || BPF_OP(code) == BPF_EXIT)\n+\t\treturn false;\n+\n+\t*is_imm = code == (BPF_JMP32 | BPF_JA);\n+\t*rel = *is_imm ? insn-\u003eimm : insn-\u003eoff;\n+\treturn true;\n+}\n+\n+/*\n+ * insn is number 'idx' of the 'len' insns that replace insn 'off' of the old\n+ * image (an insn that isn't patched is its own patch of length 1). It lands\n+ * at 'pos' of the new image. 'new_off' maps the old image to the new one.\n+ */\n+static int patch_list_adj_insn(struct bpf_insn *insn, const u32 *new_off,\n+\t\t\t       u32 cnt, u32 off, u32 idx, u32 len, u32 pos)\n+{\n+\tbool is_imm;\n+\ts64 rel, tgt;\n+\n+\tif (!insn_get_rel(insn, \u0026rel, \u0026is_imm))\n+\t\treturn 0;\n+\n+\t/* relative to the first insn of the patch */\n+\ttgt = (s64)idx + rel + 1;\n+\tif (tgt \u003e= 0 \u0026\u0026 tgt \u003c len)\n+\t\treturn 0;\n+\n+\t/* the patch pushed what follows 'off' down by len - 1 */\n+\ttgt = tgt \u003c 0 ? off + tgt : off + tgt - len + 1;\n+\tif (tgt \u003c 0 || tgt \u003e cnt)\n+\t\treturn -EFAULT;\n+\n+\trel = (s64)new_off[tgt] - pos - 1;\n+\tif (is_imm) {\n+\t\tif (rel \u003c S32_MIN || rel \u003e S32_MAX)\n+\t\t\treturn -ERANGE;\n+\t\tinsn-\u003eimm = rel;\n+\t} else {\n+\t\tif (rel \u003c S16_MIN || rel \u003e S16_MAX)\n+\t\t\treturn -ERANGE;\n+\t\tinsn-\u003eoff = rel;\n+\t}\n+\treturn 0;\n+}\n+\n+/*\n+ * Fill the aux data of the 'p-\u003elen' insns at 'slot' from the aux data 'old'\n+ * of the insn they replace, the same way as adjust_insn_aux_data() does.\n+ */\n+static void patch_list_adj_aux(struct bpf_prog *prog, struct bpf_insn_patch *p,\n+\t\t\t       struct bpf_insn *patch,\n+\t\t\t       const struct bpf_insn_aux_data *old,\n+\t\t\t       struct bpf_insn_aux_data *slot)\n+{\n+\tstruct bpf_insn_aux_data *last = slot + p-\u003elen - 1;\n+\tstruct bpf_insn *orig = \u0026p-\u003eorig;\n+\tu32 i;\n+\n+\t*last = *old;\n+\tlast-\u003ezext_dst = bpf_insn_def32(prog, \u0026patch[p-\u003elen - 1]) \u003e= 0;\n+\tmemset(slot, 0, sizeof(*slot) * (p-\u003elen - 1));\n+\n+\tfor (i = 0; i \u003c p-\u003elen - 1; i++) {\n+\t\tslot[i].seen = last-\u003eseen;\n+\t\tslot[i].zext_dst = bpf_insn_def32(prog, \u0026patch[i]) \u003e= 0;\n+\t\tif (!memcmp(\u0026patch[i], orig, sizeof(*orig))) {\n+\t\t\tslot[i].non_stack_access = last-\u003enon_stack_access;\n+\t\t\tlast-\u003enon_stack_access = false;\n+\t\t} else if (bpf_is_mem_insn(\u0026patch[i])) {\n+\t\t\tslot[i].non_stack_access = true;\n+\t\t}\n+\t}\n+\n+\tif (bpf_is_mem_insn(\u0026patch[p-\u003elen - 1]) \u0026\u0026\n+\t    memcmp(\u0026patch[p-\u003elen - 1], orig, sizeof(*orig)))\n+\t\tlast-\u003enon_stack_access = true;\n+\n+\t/* indirect jumps to the replaced insn land on the first new one */\n+\tif (last-\u003eindirect_target) {\n+\t\tslot[0].indirect_target = 1;\n+\t\tlast-\u003eindirect_target = 0;\n+\t}\n+}\n+\n+static u32 patch_list_remap(const u32 *new_off, u32 cnt, u32 grow, u32 off)\n+{\n+\treturn off \u003c= cnt ? new_off[off] : off + grow;\n+}\n+\n+/*\n+ * Apply all queued patches in one go. Everything that names an insn of the\n+ * old image follows it to the new one, a patched insn is named by the first\n+ * insn of its patch, like with bpf_patch_insn_data().\n+ */\n+int bpf_patch_list_commit(struct bpf_verifier_env *env)\n+{\n+\tstruct bpf_patch_list *pl = \u0026env-\u003epatch_list;\n+\tstruct bpf_prog *prog = env-\u003eprog;\n+\tu32 cnt = prog-\u003elen, new_cnt, grow = 0, end, i, k, n;\n+\tstruct bpf_insn_aux_data *data;\n+\tstruct bpf_insn *insns = NULL;\n+\tstruct bpf_insn_patch *p;\n+\tu32 *new_off = NULL;\n+\tint err = 0;\n+\n+\tif (!pl-\u003ecnt)\n+\t\tgoto out;\n+\n+\terr = -ENOMEM;\n+\tnew_off = kvmalloc_array(cnt + 1, sizeof(*new_off), GFP_KERNEL_ACCOUNT);\n+\tif (!new_off)\n+\t\tgoto out;\n+\n+\tfor (i = 0, k = 0; i \u003c= cnt; i++) {\n+\t\tnew_off[i] = i + grow;\n+\t\tif (k \u003c pl-\u003ecnt \u0026\u0026 pl-\u003epatches[k].off == i)\n+\t\t\tgrow += pl-\u003epatches[k++].len - 1;\n+\t}\n+\tnew_cnt = cnt + grow;\n+\n+\t/*\n+\t * Build the new image aside first, so that a jump that goes out of\n+\t * range leaves the program untouched.\n+\t */\n+\tinsns = kvmalloc_array(new_cnt, sizeof(*insns), GFP_KERNEL_ACCOUNT);\n+\tif (!insns)\n+\t\tgoto out;\n+\n+\tfor (i = 0, k = 0; i \u003c cnt; i++) {\n+\t\tconst struct bpf_insn *src = \u0026prog-\u003einsnsi[i];\n+\t\tu32 len = 1;\n+\n+\t\tif (k \u003c pl-\u003ecnt \u0026\u0026 pl-\u003epatches[k].off == i) {\n+\t\t\tsrc = \u0026pl-\u003einsns[pl-\u003epatches[k].start];\n+\t\t\tlen = pl-\u003epatches[k++].len;\n+\t\t}\n+\n+\t\tfor (n = 0; n \u003c len; n++) {\n+\t\t\tinsns[new_off[i] + n] = src[n];\n+\t\t\terr = patch_list_adj_insn(\u0026insns[new_off[i] + n], new_off,\n+\t\t\t\t\t\t  cnt, i, n, len, new_off[i] + n);\n+\t\t\tif (err == -ERANGE) {\n+\t\t\t\tverbose(env, \"insn %d cannot be patched due to 16-bit range\\n\",\n+\t\t\t\t\tenv-\u003einsn_aux_data[i].orig_idx);\n+\t\t\t\tgoto out;\n+\t\t\t}\n+\t\t\tif (verifier_bug_if(err, env, \"insn %u jumps out of the program\", i))\n+\t\t\t\tgoto out;\n+\t\t}\n+\t}\n+\n+\terr = -ENOMEM;\n+\tdata = vrealloc(env-\u003einsn_aux_data,\n+\t\t\tarray_size(new_cnt, sizeof(*data)),\n+\t\t\tGFP_KERNEL_ACCOUNT | __GFP_ZERO);\n+\tif (!data)\n+\t\tgoto out;\n+\tenv-\u003einsn_aux_data = data;\n+\n+\tprog = bpf_prog_realloc(prog, bpf_prog_size(new_cnt), GFP_USER);\n+\tif (!prog)\n+\t\tgoto out;\n+\tenv-\u003eprog = prog;\n+\n+\tmemcpy(prog-\u003einsnsi, insns, sizeof(*insns) * new_cnt);\n+\tprog-\u003elen = new_cnt;\n+\n+\t/* Move aux data from the end, an insn never moves to a lower index. */\n+\tend = cnt;\n+\tfor (k = pl-\u003ecnt; k-- \u003e 0;) {\n+\t\tp = \u0026pl-\u003epatches[k];\n+\t\tmemmove(data + new_off[p-\u003eoff + 1], data + p-\u003eoff + 1,\n+\t\t\tsizeof(*data) * (end - p-\u003eoff - 1));\n+\t\tpatch_list_adj_aux(prog, p, \u0026pl-\u003einsns[p-\u003estart], \u0026data[p-\u003eoff],\n+\t\t\t\t   \u0026data[new_off[p-\u003eoff]]);\n+\t\tend = p-\u003eoff;\n+\t}\n+\tenv-\u003einsn_aux_data_len = new_cnt;\n+\n+\t/* NOTE: fake 'exit' subprog should be updated as well. */\n+\tfor (i = 0; i \u003c= env-\u003esubprog_cnt; i++)\n+\t\tenv-\u003esubprog_info[i].start =\n+\t\t\tpatch_list_remap(new_off, cnt, grow, env-\u003esubprog_info[i].start);\n+\n+\tfor (i = 0; i \u003c prog-\u003eaux-\u003enr_linfo; i++)\n+\t\tprog-\u003eaux-\u003elinfo[i].insn_off =\n+\t\t\tpatch_list_remap(new_off, cnt, grow, prog-\u003eaux-\u003elinfo[i].insn_off);\n+\n+\tfor (i = 0; i \u003c env-\u003einsn_array_map_cnt; i++)\n+\t\tbpf_insn_array_remap(env-\u003einsn_array_maps[i], new_off, cnt, grow);\n+\n+\tfor (i = 0; i \u003c env-\u003efunc_ptr_cnt; i++) {\n+\t\tif (env-\u003efunc_ptrs[i].xlated_off == BPF_FUNC_PTR_DELETED)\n+\t\t\tcontinue;\n+\t\tenv-\u003efunc_ptrs[i].xlated_off =\n+\t\t\tpatch_list_remap(new_off, cnt, grow, env-\u003efunc_ptrs[i].xlated_off);\n+\t}\n+\n+\tfor (i = 0; i \u003c prog-\u003eaux-\u003esize_poke_tab; i++)\n+\t\tprog-\u003eaux-\u003epoke_tab[i].insn_idx =\n+\t\t\tpatch_list_remap(new_off, cnt, grow, prog-\u003eaux-\u003epoke_tab[i].insn_idx);\n+\n+\terr = 0;\n+out:\n+\tkvfree(insns);\n+\tkvfree(new_off);\n+\tbpf_patch_list_free(env);\n+\treturn err;\n+}\n+\n /*\n  * insn was moved down by delta insns inside its own patch. Operands relative\n  * to the pc that point in front of the old position did not move with it.\n@@ -1749,7 +2044,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \tstruct bpf_insn *insn_buf = env-\u003einsn_buf;\n \tstruct bpf_prog *new_prog;\n \tstruct bpf_map *map_ptr;\n-\tint i, ret, cnt, delta = 0, cur_subprog = 0;\n+\tint i, ret, cnt, cur_subprog = 0;\n \tstruct bpf_subprog_info *subprogs = env-\u003esubprog_info;\n \tu16 stack_depth = subprogs[cur_subprog].stack_depth;\n \tu16 stack_depth_extra = 0;\n@@ -1781,7 +2076,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tgoto next_insn;\n \t\t}\n \n-\t\tif (env-\u003einsn_aux_data[i + delta].needs_zext)\n+\t\tif (env-\u003einsn_aux_data[i].needs_zext)\n \t\t\t/* Convert BPF_CLASS(insn-\u003ecode) == BPF_ALU64 to 32-bit ALU */\n \t\t\tinsn-\u003ecode = BPF_ALU | BPF_OP(insn-\u003ecode) | BPF_SRC(insn-\u003ecode);\n \n@@ -1804,13 +2099,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \n \t\t\tcnt = patch - insn_buf;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta    += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n \n@@ -1895,13 +2186,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\tcnt = patch - insn_buf;\n \t\t\t}\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta    += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n \n@@ -1925,13 +2212,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t*patch++ = BPF_MOV64_IMM(insn-\u003edst_reg, 0);\n \n \t\t\tcnt = patch - insn_buf;\n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta    += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n \n@@ -1945,13 +2228,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\treturn -EFAULT;\n \t\t\t}\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta    += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n \n@@ -1964,7 +2243,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tbool issrc, isneg, isimm;\n \t\t\tu32 off_reg;\n \n-\t\t\taux = \u0026env-\u003einsn_aux_data[i + delta];\n+\t\t\taux = \u0026env-\u003einsn_aux_data[i];\n \t\t\tif (!aux-\u003ealu_state ||\n \t\t\t    aux-\u003ealu_state == BPF_ALU_NON_POINTER)\n \t\t\t\tgoto next_insn;\n@@ -1998,13 +2277,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\t*patch++ = BPF_ALU64_IMM(BPF_MUL, off_reg, -1);\n \t\t\tcnt = patch - insn_buf;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta    += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n \n@@ -2041,13 +2316,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tcnt = may_goto_expand(insn_buf, insn-\u003eoff, stack_off_cnt,\n \t\t\t\t\t      tail, ARRAY_SIZE(tail));\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t} else if (bpf_is_may_goto_insn(insn)) {\n \t\t\tint stack_off = -stack_depth - 8;\n@@ -2060,13 +2331,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tcnt = may_goto_expand(insn_buf, insn-\u003eoff, stack_off,\n \t\t\t\t\t      tail, ARRAY_SIZE(tail));\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n \n@@ -2076,7 +2343,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t     insn-\u003esrc_reg == BPF_PSEUDO_MAP_IDX_VALUE)) {\n \t\t\tstruct bpf_map *map;\n \n-\t\t\taux = \u0026env-\u003einsn_aux_data[i + delta];\n+\t\t\taux = \u0026env-\u003einsn_aux_data[i];\n \t\t\tmap = env-\u003eused_maps[aux-\u003emap_index];\n \t\t\tif (map-\u003emap_type != BPF_MAP_TYPE_PERCPU_ARRAY)\n \t\t\t\tgoto next_insn;\n@@ -2097,13 +2364,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tcnt = 2;\n \n \t\t\ti++;\n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta    += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n \n@@ -2112,19 +2375,15 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\tif (insn-\u003esrc_reg == BPF_PSEUDO_CALL)\n \t\t\tgoto next_insn;\n \t\tif (insn-\u003esrc_reg == BPF_PSEUDO_KFUNC_CALL) {\n-\t\t\tret = bpf_fixup_kfunc_call(env, insn, insn_buf, i + delta, \u0026cnt);\n+\t\t\tret = bpf_fixup_kfunc_call(env, insn, insn_buf, i, \u0026cnt);\n \t\t\tif (ret)\n \t\t\t\treturn ret;\n \t\t\tif (cnt == 0)\n \t\t\t\tgoto next_insn;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta\t += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn\t  = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n \n@@ -2159,7 +2418,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn-\u003eimm = 0;\n \t\t\tinsn-\u003ecode = BPF_JMP | BPF_TAIL_CALL;\n \n-\t\t\taux = \u0026env-\u003einsn_aux_data[i + delta];\n+\t\t\taux = \u0026env-\u003einsn_aux_data[i];\n \t\t\tif (env-\u003ebpf_capable \u0026\u0026 !prog-\u003eblinding_requested \u0026\u0026\n \t\t\t    prog-\u003ejit_requested \u0026\u0026\n \t\t\t    !bpf_map_key_poisoned(aux) \u0026\u0026\n@@ -2169,7 +2428,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\t\t.reason = BPF_POKE_REASON_TAIL_CALL,\n \t\t\t\t\t.tail_call.map = aux-\u003emap_ptr_state.map_ptr,\n \t\t\t\t\t.tail_call.key = bpf_map_key_immediate(aux),\n-\t\t\t\t\t.insn_idx = i + delta,\n+\t\t\t\t\t.insn_idx = i,\n \t\t\t\t};\n \n \t\t\t\tret = bpf_jit_add_poke_descriptor(prog, \u0026desc);\n@@ -2205,17 +2464,13 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\t\t\t\t\t map)-\u003eindex_mask);\n \t\t\tinsn_buf[2] = *insn;\n \t\t\tcnt = 3;\n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta    += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n \n-\t\taux = \u0026env-\u003einsn_aux_data[i + delta];\n+\t\taux = \u0026env-\u003einsn_aux_data[i];\n \t\tif (aux-\u003earg_prog) {\n \t\t\t/* The verifier will process callback_fn as many times as necessary\n \t\t\t * with different maps and the register states prepared by\n@@ -2239,18 +2494,14 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[2] = *insn;\n \t\t\tcnt = 3;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta    += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto patch_call_imm;\n \t\t}\n \n \t\t/* bpf_per_cpu_ptr() and bpf_this_cpu_ptr() */\n-\t\tif (env-\u003einsn_aux_data[i + delta].call_with_percpu_alloc_ptr) {\n+\t\tif (env-\u003einsn_aux_data[i].call_with_percpu_alloc_ptr) {\n \t\t\t/* patch with 'r1 = *(u64 *)(r1 + 0)' since for percpu data,\n \t\t\t * bpf_mem_alloc() returns a ptr to the percpu data ptr.\n \t\t\t */\n@@ -2258,13 +2509,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[1] = *insn;\n \t\t\tcnt = 2;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto patch_call_imm;\n \t\t}\n \n@@ -2282,7 +2529,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t     insn-\u003eimm == BPF_FUNC_redirect_map    ||\n \t\t     insn-\u003eimm == BPF_FUNC_for_each_map_elem ||\n \t\t     insn-\u003eimm == BPF_FUNC_map_lookup_percpu_elem)) {\n-\t\t\taux = \u0026env-\u003einsn_aux_data[i + delta];\n+\t\t\taux = \u0026env-\u003einsn_aux_data[i];\n \t\t\tif (bpf_map_ptr_poisoned(aux))\n \t\t\t\tgoto patch_call_imm;\n \n@@ -2301,14 +2548,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\tif (bpf_map_is_percpu_map(map_ptr-\u003emap_type))\n \t\t\t\t\tprog-\u003ejit_required = true;\n \n-\t\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta,\n-\t\t\t\t\t\t\t       insn_buf, cnt);\n-\t\t\t\tif (!new_prog)\n-\t\t\t\t\treturn -ENOMEM;\n-\n-\t\t\t\tdelta    += cnt - 1;\n-\t\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\t\tif (IS_ERR(insn))\n+\t\t\t\t\treturn PTR_ERR(insn);\n \t\t\t\tgoto next_insn;\n \t\t\t}\n \n@@ -2384,14 +2626,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\t\t\t  BPF_REG_0, 0);\n \t\t\tcnt = 3;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n-\t\t\t\t\t\t       cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta    += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n \n@@ -2414,13 +2651,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[0] = BPF_ALU32_REG(BPF_XOR, BPF_REG_0, BPF_REG_0);\n \t\t\tcnt = 1;\n #endif\n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta    += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n \n@@ -2433,13 +2666,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[2] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0);\n \t\t\tcnt = 3;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta    += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n #endif\n@@ -2467,13 +2696,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[cnt++] = BPF_JMP_A(1);\n \t\t\tinsn_buf[cnt++] = BPF_MOV64_IMM(BPF_REG_0, -EINVAL);\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta    += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n \n@@ -2499,13 +2724,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\tcnt = 1;\n \t\t\t}\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta    += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n \n@@ -2525,13 +2746,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\tcnt = 2;\n \t\t\t}\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta    += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n \n@@ -2541,12 +2758,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t/* Load IP address from ctx - 16 */\n \t\t\tinsn_buf[0] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, -16);\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, 1);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, 1);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n \n@@ -2596,13 +2810,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[10] = BPF_MOV64_IMM(BPF_REG_0, -ENOENT);\n \t\t\tcnt = 11;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta    += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n \n@@ -2614,13 +2824,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[1] = BPF_ATOMIC_OP(BPF_DW, BPF_XCHG, BPF_REG_1, BPF_REG_0, 0);\n \t\t\tcnt = 2;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n-\t\t\tif (!new_prog)\n-\t\t\t\treturn -ENOMEM;\n-\n-\t\t\tdelta    += cnt - 1;\n-\t\t\tenv-\u003eprog = prog = new_prog;\n-\t\t\tinsn      = new_prog-\u003einsnsi + i + delta;\n+\t\t\tinsn = bpf_patch_list_add(env, i, insn_buf, cnt);\n+\t\t\tif (IS_ERR(insn))\n+\t\t\t\treturn PTR_ERR(insn);\n \t\t\tgoto next_insn;\n \t\t}\n patch_call_imm:\n@@ -2636,7 +2842,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t}\n \t\tinsn-\u003eimm = BPF_CALL_IMM(fn-\u003efunc);\n next_insn:\n-\t\tif (subprogs[cur_subprog + 1].start == i + delta + 1) {\n+\t\tif (subprogs[cur_subprog + 1].start == i + 1) {\n \t\t\tsubprogs[cur_subprog].stack_depth += stack_depth_extra;\n \t\t\tsubprogs[cur_subprog].stack_extra = stack_depth_extra;\n \n@@ -2651,9 +2857,14 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tstack_depth_extra = 0;\n \t\t}\n \t\ti++;\n-\t\tinsn++;\n+\t\tinsn = \u0026prog-\u003einsnsi[i];\n \t}\n \n+\tret = bpf_patch_list_commit(env);\n+\tif (ret)\n+\t\treturn ret;\n+\tprog = env-\u003eprog;\n+\n \tenv-\u003eprog-\u003eaux-\u003estack_depth = subprogs[0].stack_depth;\n \tfor (i = 0; i \u003c env-\u003esubprog_cnt; i++) {\n \t\tint delta = bpf_jit_supports_timed_may_goto() ? 2 : 1;\ndiff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c\nindex b840b3eb9b229..7c1bed0c66c64 100644\n--- a/kernel/bpf/verifier.c\n+++ b/kernel/bpf/verifier.c\n@@ -22753,6 +22753,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\n err_free_env:\n \tbpf_free_subprog_jts(env);\n \tvfree(env-\u003einsn_aux_data);\n+\tbpf_patch_list_free(env);\n \tkvfree(env-\u003efd_array);\n \tbpf_stack_liveness_free(env);\n \tkvfree(env-\u003ecfg.insn_postorder);\ndiff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c\nindex 8a6d341b754ac..af107b0c1595e 100644\n--- a/tools/testing/selftests/bpf/prog_tests/verifier.c\n+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c\n@@ -90,6 +90,7 @@\n #include \"verifier_netfilter_retcode.skel.h\"\n #include \"verifier_bpf_fastcall.skel.h\"\n #include \"verifier_or_jmp32_k.skel.h\"\n+#include \"verifier_patch_list.skel.h\"\n #include \"verifier_percpu_addr.skel.h\"\n #include \"verifier_precision.skel.h\"\n #include \"verifier_prevent_map_lookup.skel.h\"\n@@ -273,6 +274,7 @@ void test_verifier_netfilter_ctx(void)        { RUN(verifier_netfilter_ctx); }\n void test_verifier_netfilter_retcode(void)    { RUN(verifier_netfilter_retcode); }\n void test_verifier_bpf_fastcall(void)         { RUN(verifier_bpf_fastcall); }\n void test_verifier_or_jmp32_k(void)           { RUN(verifier_or_jmp32_k); }\n+void test_verifier_patch_list(void)           { RUN(verifier_patch_list); }\n void test_verifier_percpu_addr(void)          { RUN(verifier_percpu_addr); }\n void test_verifier_precision(void)            { RUN(verifier_precision); }\n void test_verifier_prevent_map_lookup(void)   { RUN(verifier_prevent_map_lookup); }\ndiff --git a/tools/testing/selftests/bpf/progs/verifier_patch_list.c b/tools/testing/selftests/bpf/progs/verifier_patch_list.c\nnew file mode 100644\nindex 0000000000000..268cebd179984\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/verifier_patch_list.c\n@@ -0,0 +1,120 @@\n+// SPDX-License-Identifier: GPL-2.0\n+\n+#include \u003clinux/bpf.h\u003e\n+#include \u003cbpf/bpf_helpers.h\u003e\n+#include \"../../../include/linux/filter.h\"\n+#include \"bpf_misc.h\"\n+\n+/*\n+ * bpf_do_misc_fixups() guards each division by a register against division\n+ * by zero with a 4 insn patch. Jumps to a patched insn must land on the\n+ * first insn of its patch, other jumps must follow the insns they target.\n+ */\n+\n+SEC(\"raw_tp\")\n+__description(\"patch list: forward jumps to and over patched insns\")\n+__arch_x86_64\n+__arch_arm64\n+__success\n+__xlated(\"0: call\")\n+__xlated(\"1: r1 = r0\")\n+__xlated(\"2: r0 = 7\")\n+__xlated(\"3: if r1 \u003e 0x5 goto pc+1\")\n+__xlated(\"4: r0 = 9\")\n+__xlated(\"5: if r1 != 0x0 goto pc+2\")\n+__xlated(\"6: w0 ^= w0\")\n+__xlated(\"7: goto pc+1\")\n+__xlated(\"8: r0 /= r1\")\n+__xlated(\"9: if r0 \u003e 0x3 goto pc+4\")\n+__xlated(\"10: if r1 != 0x0 goto pc+2\")\n+__xlated(\"11: w0 ^= w0\")\n+__xlated(\"12: goto pc+1\")\n+__xlated(\"13: r0 /= r1\")\n+__xlated(\"14: exit\")\n+__naked void patch_list_forward(void)\n+{\n+\tasm volatile (\n+\t\"call %[bpf_get_prandom_u32];\"\n+\t\"r1 = r0;\"\n+\t\"r0 = 7;\"\n+\t\"if r1 \u003e 5 goto l0_%=;\"\n+\t\"r0 = 9;\"\n+\"l0_%=:\"\n+\t\"r0 /= r1;\"\n+\t\"if r0 \u003e 3 goto l1_%=;\"\n+\t\"r0 /= r1;\"\n+\"l1_%=:\"\n+\t\"exit;\"\n+\t:\n+\t: __imm(bpf_get_prandom_u32)\n+\t: __clobber_all);\n+}\n+\n+SEC(\"raw_tp\")\n+__description(\"patch list: backward jump to a patched insn\")\n+__arch_x86_64\n+__arch_arm64\n+__success\n+__xlated(\"0: call\")\n+__xlated(\"1: r1 = r0\")\n+__xlated(\"2: r2 = 0\")\n+__xlated(\"3: if r1 != 0x0 goto pc+2\")\n+__xlated(\"4: w0 ^= w0\")\n+__xlated(\"5: goto pc+1\")\n+__xlated(\"6: r0 /= r1\")\n+__xlated(\"7: r2 += 1\")\n+__xlated(\"8: if r2 \u003c 0x3 goto pc-6\")\n+__xlated(\"9: exit\")\n+__naked void patch_list_backward(void)\n+{\n+\tasm volatile (\n+\t\"call %[bpf_get_prandom_u32];\"\n+\t\"r1 = r0;\"\n+\t\"r2 = 0;\"\n+\"l0_%=:\"\n+\t\"r0 /= r1;\"\n+\t\"r2 += 1;\"\n+\t\"if r2 \u003c 3 goto l0_%=;\"\n+\t\"exit;\"\n+\t:\n+\t: __imm(bpf_get_prandom_u32)\n+\t: __clobber_all);\n+}\n+\n+/* The jump of may_goto leaves its own patch and crosses another one. */\n+SEC(\"raw_tp\")\n+__description(\"patch list: may_goto between patched insns\")\n+__arch_x86_64\n+__success\n+__xlated(\"0: *(u64 *)(r10 -16) = 65535\")\n+__xlated(\"1: *(u64 *)(r10 -8) = 0\")\n+__xlated(\"2: call\")\n+__xlated(\"3: r1 = r0\")\n+__xlated(\"4: if r1 != 0x0 goto pc+2\")\n+__xlated(\"5: w0 ^= w0\")\n+__xlated(\"6: goto pc+1\")\n+__xlated(\"7: r0 /= r1\")\n+__xlated(\"8: r12 = *(u64 *)(r10 -16)\")\n+__xlated(\"9: if r12 == 0x0 goto pc+9\")\n+__xlated(\"...\")\n+__xlated(\"15: if r1 != 0x0 goto pc+2\")\n+__xlated(\"16: w0 ^= w0\")\n+__xlated(\"17: goto pc+1\")\n+__xlated(\"18: r0 /= r1\")\n+__xlated(\"19: exit\")\n+__naked void patch_list_may_goto(void)\n+{\n+\tasm volatile (\n+\t\"call %[bpf_get_prandom_u32];\"\n+\t\"r1 = r0;\"\n+\t\"r0 /= r1;\"\n+\t\".8byte %[may_goto];\"\n+\t\"r0 /= r1;\"\n+\t\"exit;\"\n+\t:\n+\t: __imm(bpf_get_prandom_u32),\n+\t  __imm_insn(may_goto, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 1 /* offset */, 0))\n+\t: __clobber_all);\n+}\n+\n+char _license[] SEC(\"license\") = \"GPL\";\n"
}

2/1 2026/10/02 05:48 agent patch-evaluator
1m Model:
core

Args:
null

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL",
    "BPF_JIT"
  ],
  "FocusSymbols": [
    "bpf_do_misc_fixups",
    "bpf_patch_list_commit",
    "bpf_patch_list_add",
    "bpf_patch_list_free",
    "bpf_insn_array_remap"
  ],
  "Reasoning": "The patch replaces the incremental instruction patching in bpf_do_misc_fixups() with a deferred, batched instruction patching mechanism (bpf_patch_list_add and bpf_patch_list_commit). This involves complex jump target recalculations, instruction aux data remapping, subprog offset adjustments, and memory reallocations in the BPF verifier. The code is directly reachable from userspace via the bpf() system call when loading BPF programs. Fuzzing is critical to detect potential branch offset calculation bugs, out-of-bounds array access, or invariant violations during instruction rewriting.",
  "WorthFuzzing": true
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 4a0d28041e8f5b17363997575245b86c94c1991c
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Oct 2 05:48:19 2026 +0000

    syz-cluster: applied patch under review

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 4bae3796c42f6..17932ff8b0ae9 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -4299,6 +4299,7 @@ int bpf_insn_array_ready(struct bpf_map *map);
 void bpf_insn_array_release(struct bpf_map *map);
 void bpf_insn_array_adjust(struct bpf_map *map, u32 first, u32 len);
 void bpf_insn_array_adjust_after_remove(struct bpf_map *map, u32 off, u32 len);
+void bpf_insn_array_remap(struct bpf_map *map, const u32 *new_off, u32 cnt, u32 grow);
 
 #ifdef CONFIG_BPF_SYSCALL
 void bpf_prog_update_insn_ptrs(struct bpf_prog *prog, u32 *offsets, void *image);
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 811342e3c0419..5a968539589f8 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -929,6 +929,27 @@ struct bpf_fd_array {
 	};
 };
 
+/* one deferred patch: insn 'off' is replaced by 'len' insns */
+struct bpf_insn_patch {
+	u32 off;
+	u32 len;
+	u32 start;		/* first insn of the patch in bpf_patch_list.insns */
+	struct bpf_insn orig;	/* the replaced insn when the patch was queued */
+};
+
+/*
+ * Patches queued by a rewrite pass, applied together by
+ * bpf_patch_list_commit() in O(prog->len + inserted insns).
+ */
+struct bpf_patch_list {
+	struct bpf_insn_patch *patches;
+	struct bpf_insn *insns;
+	u32 cnt;
+	u32 cap;
+	u32 insn_cnt;
+	u32 insn_cap;
+};
+
 /* single container for all structs
  * one verifier_env per bpf_check() call
  */
@@ -973,6 +994,7 @@ struct bpf_verifier_env {
 	bool signature;
 	u32 insn_aux_data_len;
 	struct bpf_insn_aux_data *insn_aux_data; /* array of per-insn state */
+	struct bpf_patch_list patch_list;
 	const struct bpf_line_info *prev_linfo;
 	struct bpf_verifier_log log;
 	struct bpf_diag *diag;
@@ -1834,5 +1856,9 @@ int bpf_jit_subprogs(struct bpf_verifier_env *env);
 int bpf_fixup_call_args(struct bpf_verifier_env *env);
 int bpf_do_misc_fixups(struct bpf_verifier_env *env);
 int bpf_insn_def32(struct bpf_prog *prog, struct bpf_insn *insn);
+struct bpf_insn *bpf_patch_list_add(struct bpf_verifier_env *env, u32 off,
+				    const struct bpf_insn *patch, u32 len);
+int bpf_patch_list_commit(struct bpf_verifier_env *env);
+void bpf_patch_list_free(struct bpf_verifier_env *env);
 
 #endif /* _LINUX_BPF_VERIFIER_H */
diff --git a/kernel/bpf/bpf_insn_array.c b/kernel/bpf/bpf_insn_array.c
index 92bbb71576ae3..74f07ced85cb0 100644
--- a/kernel/bpf/bpf_insn_array.c
+++ b/kernel/bpf/bpf_insn_array.c
@@ -253,6 +253,24 @@ void bpf_insn_array_adjust(struct bpf_map *map, u32 first, u32 len)
 	}
 }
 
+/*
+ * Move each offset to new_off[offset], new_off[] covers [0, cnt]. Offsets
+ * past the end of the old program only move by 'grow'.
+ */
+void bpf_insn_array_remap(struct bpf_map *map, const u32 *new_off, u32 cnt, u32 grow)
+{
+	struct bpf_insn_array *insn_array = cast_insn_array(map);
+	u32 off;
+	int i;
+
+	for (i = 0; i < map->max_entries; i++) {
+		off = insn_array->values[i].xlated_off;
+		if (off == INSN_DELETED)
+			continue;
+		insn_array->values[i].xlated_off = off <= cnt ? new_off[off] : off + grow;
+	}
+}
+
 void bpf_insn_array_adjust_after_remove(struct bpf_map *map, u32 off, u32 len)
 {
 	struct bpf_insn_array *insn_array = cast_insn_array(map);
diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
index 37cf130ebb57b..0e8c61ede9a5b 100644
--- a/kernel/bpf/fixups.c
+++ b/kernel/bpf/fixups.c
@@ -404,6 +404,301 @@ struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
 	return __bpf_patch_insn_data(env, off, patch, len, BPF_PATCH_KEEP_TARGET);
 }
 
+/*
+ * Queue the replacement of insn 'off' by 'patch[0..len)'. Nothing moves until
+ * bpf_patch_list_commit(), so a pass keeps addressing insns and their aux data
+ * by their index in env->prog. Patches must be queued in increasing order of
+ * 'off'. Jumps in the patch that leave it are relative to the patch placed at
+ * 'off', the same way as for bpf_patch_insn_data().
+ *
+ * Return the copy of the last insn of the patch, which stays valid until the
+ * next call.
+ */
+struct bpf_insn *bpf_patch_list_add(struct bpf_verifier_env *env, u32 off,
+				    const struct bpf_insn *patch, u32 len)
+{
+	struct bpf_patch_list *pl = &env->patch_list;
+	struct bpf_insn_patch *p;
+	struct bpf_insn *insns;
+	u32 cap;
+
+	if (bpf_rewrite_must_abort())
+		return ERR_PTR(-EINTR);
+
+	if (verifier_bug_if(!len || off >= env->prog->len ||
+			    (pl->cnt && off <= pl->patches[pl->cnt - 1].off),
+			    env, "insn %u patched out of order", off))
+		return ERR_PTR(-EFAULT);
+
+	/* Replacing one insn by one moves nothing, do it right away. */
+	if (len == 1) {
+		env->prog->insnsi[off] = *patch;
+		adjust_insn_aux_data(env, env->prog, off, 1, NULL,
+				     BPF_PATCH_KEEP_TARGET);
+		return &env->prog->insnsi[off];
+	}
+
+	if (pl->cnt == pl->cap) {
+		cap = max(pl->cap * 2, 16U);
+		p = kvrealloc(pl->patches, array_size(cap, sizeof(*p)),
+			      GFP_KERNEL_ACCOUNT);
+		if (!p)
+			return ERR_PTR(-ENOMEM);
+		pl->patches = p;
+		pl->cap = cap;
+	}
+
+	if (pl->insn_cnt + len > pl->insn_cap) {
+		cap = max3(pl->insn_cap * 2, pl->insn_cnt + len, 64U);
+		insns = kvrealloc(pl->insns, array_size(cap, sizeof(*insns)),
+				  GFP_KERNEL_ACCOUNT);
+		if (!insns)
+			return ERR_PTR(-ENOMEM);
+		pl->insns = insns;
+		pl->insn_cap = cap;
+	}
+
+	p = &pl->patches[pl->cnt++];
+	p->off = off;
+	p->len = len;
+	p->start = pl->insn_cnt;
+	p->orig = env->prog->insnsi[off];
+	memcpy(pl->insns + p->start, patch, len * sizeof(*patch));
+	pl->insn_cnt += len;
+
+	return &pl->insns[p->start + len - 1];
+}
+
+void bpf_patch_list_free(struct bpf_verifier_env *env)
+{
+	struct bpf_patch_list *pl = &env->patch_list;
+
+	kvfree(pl->patches);
+	kvfree(pl->insns);
+	memset(pl, 0, sizeof(*pl));
+}
+
+/* Extract the pc-relative operand of a jump, a subprog call or a ld_imm64 of a subprog. */
+static bool insn_get_rel(const struct bpf_insn *insn, s64 *rel, bool *is_imm)
+{
+	u8 code = insn->code;
+
+	if (bpf_pseudo_func(insn) || bpf_pseudo_call(insn)) {
+		*rel = insn->imm;
+		*is_imm = true;
+		return true;
+	}
+	if ((BPF_CLASS(code) != BPF_JMP && BPF_CLASS(code) != BPF_JMP32) ||
+	    BPF_OP(code) == BPF_CALL || BPF_OP(code) == BPF_EXIT)
+		return false;
+
+	*is_imm = code == (BPF_JMP32 | BPF_JA);
+	*rel = *is_imm ? insn->imm : insn->off;
+	return true;
+}
+
+/*
+ * insn is number 'idx' of the 'len' insns that replace insn 'off' of the old
+ * image (an insn that isn't patched is its own patch of length 1). It lands
+ * at 'pos' of the new image. 'new_off' maps the old image to the new one.
+ */
+static int patch_list_adj_insn(struct bpf_insn *insn, const u32 *new_off,
+			       u32 cnt, u32 off, u32 idx, u32 len, u32 pos)
+{
+	bool is_imm;
+	s64 rel, tgt;
+
+	if (!insn_get_rel(insn, &rel, &is_imm))
+		return 0;
+
+	/* relative to the first insn of the patch */
+	tgt = (s64)idx + rel + 1;
+	if (tgt >= 0 && tgt < len)
+		return 0;
+
+	/* the patch pushed what follows 'off' down by len - 1 */
+	tgt = tgt < 0 ? off + tgt : off + tgt - len + 1;
+	if (tgt < 0 || tgt > cnt)
+		return -EFAULT;
+
+	rel = (s64)new_off[tgt] - pos - 1;
+	if (is_imm) {
+		if (rel < S32_MIN || rel > S32_MAX)
+			return -ERANGE;
+		insn->imm = rel;
+	} else {
+		if (rel < S16_MIN || rel > S16_MAX)
+			return -ERANGE;
+		insn->off = rel;
+	}
+	return 0;
+}
+
+/*
+ * Fill the aux data of the 'p->len' insns at 'slot' from the aux data 'old'
+ * of the insn they replace, the same way as adjust_insn_aux_data() does.
+ */
+static void patch_list_adj_aux(struct bpf_prog *prog, struct bpf_insn_patch *p,
+			       struct bpf_insn *patch,
+			       const struct bpf_insn_aux_data *old,
+			       struct bpf_insn_aux_data *slot)
+{
+	struct bpf_insn_aux_data *last = slot + p->len - 1;
+	struct bpf_insn *orig = &p->orig;
+	u32 i;
+
+	*last = *old;
+	last->zext_dst = bpf_insn_def32(prog, &patch[p->len - 1]) >= 0;
+	memset(slot, 0, sizeof(*slot) * (p->len - 1));
+
+	for (i = 0; i < p->len - 1; i++) {
+		slot[i].seen = last->seen;
+		slot[i].zext_dst = bpf_insn_def32(prog, &patch[i]) >= 0;
+		if (!memcmp(&patch[i], orig, sizeof(*orig))) {
+			slot[i].non_stack_access = last->non_stack_access;
+			last->non_stack_access = false;
+		} else if (bpf_is_mem_insn(&patch[i])) {
+			slot[i].non_stack_access = true;
+		}
+	}
+
+	if (bpf_is_mem_insn(&patch[p->len - 1]) &&
+	    memcmp(&patch[p->len - 1], orig, sizeof(*orig)))
+		last->non_stack_access = true;
+
+	/* indirect jumps to the replaced insn land on the first new one */
+	if (last->indirect_target) {
+		slot[0].indirect_target = 1;
+		last->indirect_target = 0;
+	}
+}
+
+static u32 patch_list_remap(const u32 *new_off, u32 cnt, u32 grow, u32 off)
+{
+	return off <= cnt ? new_off[off] : off + grow;
+}
+
+/*
+ * Apply all queued patches in one go. Everything that names an insn of the
+ * old image follows it to the new one, a patched insn is named by the first
+ * insn of its patch, like with bpf_patch_insn_data().
+ */
+int bpf_patch_list_commit(struct bpf_verifier_env *env)
+{
+	struct bpf_patch_list *pl = &env->patch_list;
+	struct bpf_prog *prog = env->prog;
+	u32 cnt = prog->len, new_cnt, grow = 0, end, i, k, n;
+	struct bpf_insn_aux_data *data;
+	struct bpf_insn *insns = NULL;
+	struct bpf_insn_patch *p;
+	u32 *new_off = NULL;
+	int err = 0;
+
+	if (!pl->cnt)
+		goto out;
+
+	err = -ENOMEM;
+	new_off = kvmalloc_array(cnt + 1, sizeof(*new_off), GFP_KERNEL_ACCOUNT);
+	if (!new_off)
+		goto out;
+
+	for (i = 0, k = 0; i <= cnt; i++) {
+		new_off[i] = i + grow;
+		if (k < pl->cnt && pl->patches[k].off == i)
+			grow += pl->patches[k++].len - 1;
+	}
+	new_cnt = cnt + grow;
+
+	/*
+	 * Build the new image aside first, so that a jump that goes out of
+	 * range leaves the program untouched.
+	 */
+	insns = kvmalloc_array(new_cnt, sizeof(*insns), GFP_KERNEL_ACCOUNT);
+	if (!insns)
+		goto out;
+
+	for (i = 0, k = 0; i < cnt; i++) {
+		const struct bpf_insn *src = &prog->insnsi[i];
+		u32 len = 1;
+
+		if (k < pl->cnt && pl->patches[k].off == i) {
+			src = &pl->insns[pl->patches[k].start];
+			len = pl->patches[k++].len;
+		}
+
+		for (n = 0; n < len; n++) {
+			insns[new_off[i] + n] = src[n];
+			err = patch_list_adj_insn(&insns[new_off[i] + n], new_off,
+						  cnt, i, n, len, new_off[i] + n);
+			if (err == -ERANGE) {
+				verbose(env, "insn %d cannot be patched due to 16-bit range\n",
+					env->insn_aux_data[i].orig_idx);
+				goto out;
+			}
+			if (verifier_bug_if(err, env, "insn %u jumps out of the program", i))
+				goto out;
+		}
+	}
+
+	err = -ENOMEM;
+	data = vrealloc(env->insn_aux_data,
+			array_size(new_cnt, sizeof(*data)),
+			GFP_KERNEL_ACCOUNT | __GFP_ZERO);
+	if (!data)
+		goto out;
+	env->insn_aux_data = data;
+
+	prog = bpf_prog_realloc(prog, bpf_prog_size(new_cnt), GFP_USER);
+	if (!prog)
+		goto out;
+	env->prog = prog;
+
+	memcpy(prog->insnsi, insns, sizeof(*insns) * new_cnt);
+	prog->len = new_cnt;
+
+	/* Move aux data from the end, an insn never moves to a lower index. */
+	end = cnt;
+	for (k = pl->cnt; k-- > 0;) {
+		p = &pl->patches[k];
+		memmove(data + new_off[p->off + 1], data + p->off + 1,
+			sizeof(*data) * (end - p->off - 1));
+		patch_list_adj_aux(prog, p, &pl->insns[p->start], &data[p->off],
+				   &data[new_off[p->off]]);
+		end = p->off;
+	}
+	env->insn_aux_data_len = new_cnt;
+
+	/* NOTE: fake 'exit' subprog should be updated as well. */
+	for (i = 0; i <= env->subprog_cnt; i++)
+		env->subprog_info[i].start =
+			patch_list_remap(new_off, cnt, grow, env->subprog_info[i].start);
+
+	for (i = 0; i < prog->aux->nr_linfo; i++)
+		prog->aux->linfo[i].insn_off =
+			patch_list_remap(new_off, cnt, grow, prog->aux->linfo[i].insn_off);
+
+	for (i = 0; i < env->insn_array_map_cnt; i++)
+		bpf_insn_array_remap(env->insn_array_maps[i], new_off, cnt, grow);
+
+	for (i = 0; i < env->func_ptr_cnt; i++) {
+		if (env->func_ptrs[i].xlated_off == BPF_FUNC_PTR_DELETED)
+			continue;
+		env->func_ptrs[i].xlated_off =
+			patch_list_remap(new_off, cnt, grow, env->func_ptrs[i].xlated_off);
+	}
+
+	for (i = 0; i < prog->aux->size_poke_tab; i++)
+		prog->aux->poke_tab[i].insn_idx =
+			patch_list_remap(new_off, cnt, grow, prog->aux->poke_tab[i].insn_idx);
+
+	err = 0;
+out:
+	kvfree(insns);
+	kvfree(new_off);
+	bpf_patch_list_free(env);
+	return err;
+}
+
 /*
  * insn was moved down by delta insns inside its own patch. Operands relative
  * to the pc that point in front of the old position did not move with it.
@@ -1749,7 +2044,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 	struct bpf_insn *insn_buf = env->insn_buf;
 	struct bpf_prog *new_prog;
 	struct bpf_map *map_ptr;
-	int i, ret, cnt, delta = 0, cur_subprog = 0;
+	int i, ret, cnt, cur_subprog = 0;
 	struct bpf_subprog_info *subprogs = env->subprog_info;
 	u16 stack_depth = subprogs[cur_subprog].stack_depth;
 	u16 stack_depth_extra = 0;
@@ -1781,7 +2076,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			goto next_insn;
 		}
 
-		if (env->insn_aux_data[i + delta].needs_zext)
+		if (env->insn_aux_data[i].needs_zext)
 			/* Convert BPF_CLASS(insn->code) == BPF_ALU64 to 32-bit ALU */
 			insn->code = BPF_ALU | BPF_OP(insn->code) | BPF_SRC(insn->code);
 
@@ -1804,13 +2099,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 
 			cnt = patch - insn_buf;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -1895,13 +2186,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 				cnt = patch - insn_buf;
 			}
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -1925,13 +2212,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			*patch++ = BPF_MOV64_IMM(insn->dst_reg, 0);
 
 			cnt = patch - insn_buf;
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -1945,13 +2228,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 				return -EFAULT;
 			}
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -1964,7 +2243,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			bool issrc, isneg, isimm;
 			u32 off_reg;
 
-			aux = &env->insn_aux_data[i + delta];
+			aux = &env->insn_aux_data[i];
 			if (!aux->alu_state ||
 			    aux->alu_state == BPF_ALU_NON_POINTER)
 				goto next_insn;
@@ -1998,13 +2277,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 				*patch++ = BPF_ALU64_IMM(BPF_MUL, off_reg, -1);
 			cnt = patch - insn_buf;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2041,13 +2316,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			cnt = may_goto_expand(insn_buf, insn->off, stack_off_cnt,
 					      tail, ARRAY_SIZE(tail));
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta += cnt - 1;
-			env->prog = prog = new_prog;
-			insn = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		} else if (bpf_is_may_goto_insn(insn)) {
 			int stack_off = -stack_depth - 8;
@@ -2060,13 +2331,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			cnt = may_goto_expand(insn_buf, insn->off, stack_off,
 					      tail, ARRAY_SIZE(tail));
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta += cnt - 1;
-			env->prog = prog = new_prog;
-			insn = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2076,7 +2343,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 		     insn->src_reg == BPF_PSEUDO_MAP_IDX_VALUE)) {
 			struct bpf_map *map;
 
-			aux = &env->insn_aux_data[i + delta];
+			aux = &env->insn_aux_data[i];
 			map = env->used_maps[aux->map_index];
 			if (map->map_type != BPF_MAP_TYPE_PERCPU_ARRAY)
 				goto next_insn;
@@ -2097,13 +2364,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			cnt = 2;
 
 			i++;
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2112,19 +2375,15 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 		if (insn->src_reg == BPF_PSEUDO_CALL)
 			goto next_insn;
 		if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
-			ret = bpf_fixup_kfunc_call(env, insn, insn_buf, i + delta, &cnt);
+			ret = bpf_fixup_kfunc_call(env, insn, insn_buf, i, &cnt);
 			if (ret)
 				return ret;
 			if (cnt == 0)
 				goto next_insn;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta	 += cnt - 1;
-			env->prog = prog = new_prog;
-			insn	  = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2159,7 +2418,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			insn->imm = 0;
 			insn->code = BPF_JMP | BPF_TAIL_CALL;
 
-			aux = &env->insn_aux_data[i + delta];
+			aux = &env->insn_aux_data[i];
 			if (env->bpf_capable && !prog->blinding_requested &&
 			    prog->jit_requested &&
 			    !bpf_map_key_poisoned(aux) &&
@@ -2169,7 +2428,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 					.reason = BPF_POKE_REASON_TAIL_CALL,
 					.tail_call.map = aux->map_ptr_state.map_ptr,
 					.tail_call.key = bpf_map_key_immediate(aux),
-					.insn_idx = i + delta,
+					.insn_idx = i,
 				};
 
 				ret = bpf_jit_add_poke_descriptor(prog, &desc);
@@ -2205,17 +2464,13 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 								 map)->index_mask);
 			insn_buf[2] = *insn;
 			cnt = 3;
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
-		aux = &env->insn_aux_data[i + delta];
+		aux = &env->insn_aux_data[i];
 		if (aux->arg_prog) {
 			/* The verifier will process callback_fn as many times as necessary
 			 * with different maps and the register states prepared by
@@ -2239,18 +2494,14 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			insn_buf[2] = *insn;
 			cnt = 3;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto patch_call_imm;
 		}
 
 		/* bpf_per_cpu_ptr() and bpf_this_cpu_ptr() */
-		if (env->insn_aux_data[i + delta].call_with_percpu_alloc_ptr) {
+		if (env->insn_aux_data[i].call_with_percpu_alloc_ptr) {
 			/* patch with 'r1 = *(u64 *)(r1 + 0)' since for percpu data,
 			 * bpf_mem_alloc() returns a ptr to the percpu data ptr.
 			 */
@@ -2258,13 +2509,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			insn_buf[1] = *insn;
 			cnt = 2;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta += cnt - 1;
-			env->prog = prog = new_prog;
-			insn = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto patch_call_imm;
 		}
 
@@ -2282,7 +2529,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 		     insn->imm == BPF_FUNC_redirect_map    ||
 		     insn->imm == BPF_FUNC_for_each_map_elem ||
 		     insn->imm == BPF_FUNC_map_lookup_percpu_elem)) {
-			aux = &env->insn_aux_data[i + delta];
+			aux = &env->insn_aux_data[i];
 			if (bpf_map_ptr_poisoned(aux))
 				goto patch_call_imm;
 
@@ -2301,14 +2548,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 				if (bpf_map_is_percpu_map(map_ptr->map_type))
 					prog->jit_required = true;
 
-				new_prog = bpf_patch_insn_data(env, i + delta,
-							       insn_buf, cnt);
-				if (!new_prog)
-					return -ENOMEM;
-
-				delta    += cnt - 1;
-				env->prog = prog = new_prog;
-				insn      = new_prog->insnsi + i + delta;
+				insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+				if (IS_ERR(insn))
+					return PTR_ERR(insn);
 				goto next_insn;
 			}
 
@@ -2384,14 +2626,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 						  BPF_REG_0, 0);
 			cnt = 3;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
-						       cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2414,13 +2651,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			insn_buf[0] = BPF_ALU32_REG(BPF_XOR, BPF_REG_0, BPF_REG_0);
 			cnt = 1;
 #endif
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2433,13 +2666,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			insn_buf[2] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0);
 			cnt = 3;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 #endif
@@ -2467,13 +2696,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			insn_buf[cnt++] = BPF_JMP_A(1);
 			insn_buf[cnt++] = BPF_MOV64_IMM(BPF_REG_0, -EINVAL);
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2499,13 +2724,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 				cnt = 1;
 			}
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2525,13 +2746,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 				cnt = 2;
 			}
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2541,12 +2758,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			/* Load IP address from ctx - 16 */
 			insn_buf[0] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, -16);
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, 1);
-			if (!new_prog)
-				return -ENOMEM;
-
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, 1);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2596,13 +2810,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			insn_buf[10] = BPF_MOV64_IMM(BPF_REG_0, -ENOENT);
 			cnt = 11;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2614,13 +2824,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			insn_buf[1] = BPF_ATOMIC_OP(BPF_DW, BPF_XCHG, BPF_REG_1, BPF_REG_0, 0);
 			cnt = 2;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 patch_call_imm:
@@ -2636,7 +2842,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 		}
 		insn->imm = BPF_CALL_IMM(fn->func);
 next_insn:
-		if (subprogs[cur_subprog + 1].start == i + delta + 1) {
+		if (subprogs[cur_subprog + 1].start == i + 1) {
 			subprogs[cur_subprog].stack_depth += stack_depth_extra;
 			subprogs[cur_subprog].stack_extra = stack_depth_extra;
 
@@ -2651,9 +2857,14 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			stack_depth_extra = 0;
 		}
 		i++;
-		insn++;
+		insn = &prog->insnsi[i];
 	}
 
+	ret = bpf_patch_list_commit(env);
+	if (ret)
+		return ret;
+	prog = env->prog;
+
 	env->prog->aux->stack_depth = subprogs[0].stack_depth;
 	for (i = 0; i < env->subprog_cnt; i++) {
 		int delta = bpf_jit_supports_timed_may_goto() ? 2 : 1;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index b840b3eb9b229..7c1bed0c66c64 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -22753,6 +22753,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
 err_free_env:
 	bpf_free_subprog_jts(env);
 	vfree(env->insn_aux_data);
+	bpf_patch_list_free(env);
 	kvfree(env->fd_array);
 	bpf_stack_liveness_free(env);
 	kvfree(env->cfg.insn_postorder);
diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index 8a6d341b754ac..af107b0c1595e 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -90,6 +90,7 @@
 #include "verifier_netfilter_retcode.skel.h"
 #include "verifier_bpf_fastcall.skel.h"
 #include "verifier_or_jmp32_k.skel.h"
+#include "verifier_patch_list.skel.h"
 #include "verifier_percpu_addr.skel.h"
 #include "verifier_precision.skel.h"
 #include "verifier_prevent_map_lookup.skel.h"
@@ -273,6 +274,7 @@ void test_verifier_netfilter_ctx(void)        { RUN(verifier_netfilter_ctx); }
 void test_verifier_netfilter_retcode(void)    { RUN(verifier_netfilter_retcode); }
 void test_verifier_bpf_fastcall(void)         { RUN(verifier_bpf_fastcall); }
 void test_verifier_or_jmp32_k(void)           { RUN(verifier_or_jmp32_k); }
+void test_verifier_patch_list(void)           { RUN(verifier_patch_list); }
 void test_verifier_percpu_addr(void)          { RUN(verifier_percpu_addr); }
 void test_verifier_precision(void)            { RUN(verifier_precision); }
 void test_verifier_prevent_map_lookup(void)   { RUN(verifier_prevent_map_lookup); }
diff --git a/tools/testing/selftests/bpf/progs/verifier_patch_list.c b/tools/testing/selftests/bpf/progs/verifier_patch_list.c
new file mode 100644
index 0000000000000..268cebd179984
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_patch_list.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include "../../../include/linux/filter.h"
+#include "bpf_misc.h"
+
+/*
+ * bpf_do_misc_fixups() guards each division by a register against division
+ * by zero with a 4 insn patch. Jumps to a patched insn must land on the
+ * first insn of its patch, other jumps must follow the insns they target.
+ */
+
+SEC("raw_tp")
+__description("patch list: forward jumps to and over patched insns")
+__arch_x86_64
+__arch_arm64
+__success
+__xlated("0: call")
+__xlated("1: r1 = r0")
+__xlated("2: r0 = 7")
+__xlated("3: if r1 > 0x5 goto pc+1")
+__xlated("4: r0 = 9")
+__xlated("5: if r1 != 0x0 goto pc+2")
+__xlated("6: w0 ^= w0")
+__xlated("7: goto pc+1")
+__xlated("8: r0 /= r1")
+__xlated("9: if r0 > 0x3 goto pc+4")
+__xlated("10: if r1 != 0x0 goto pc+2")
+__xlated("11: w0 ^= w0")
+__xlated("12: goto pc+1")
+__xlated("13: r0 /= r1")
+__xlated("14: exit")
+__naked void patch_list_forward(void)
+{
+	asm volatile (
+	"call %[bpf_get_prandom_u32];"
+	"r1 = r0;"
+	"r0 = 7;"
+	"if r1 > 5 goto l0_%=;"
+	"r0 = 9;"
+"l0_%=:"
+	"r0 /= r1;"
+	"if r0 > 3 goto l1_%=;"
+	"r0 /= r1;"
+"l1_%=:"
+	"exit;"
+	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+SEC("raw_tp")
+__description("patch list: backward jump to a patched insn")
+__arch_x86_64
+__arch_arm64
+__success
+__xlated("0: call")
+__xlated("1: r1 = r0")
+__xlated("2: r2 = 0")
+__xlated("3: if r1 != 0x0 goto pc+2")
+__xlated("4: w0 ^= w0")
+__xlated("5: goto pc+1")
+__xlated("6: r0 /= r1")
+__xlated("7: r2 += 1")
+__xlated("8: if r2 < 0x3 goto pc-6")
+__xlated("9: exit")
+__naked void patch_list_backward(void)
+{
+	asm volatile (
+	"call %[bpf_get_prandom_u32];"
+	"r1 = r0;"
+	"r2 = 0;"
+"l0_%=:"
+	"r0 /= r1;"
+	"r2 += 1;"
+	"if r2 < 3 goto l0_%=;"
+	"exit;"
+	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+/* The jump of may_goto leaves its own patch and crosses another one. */
+SEC("raw_tp")
+__description("patch list: may_goto between patched insns")
+__arch_x86_64
+__success
+__xlated("0: *(u64 *)(r10 -16) = 65535")
+__xlated("1: *(u64 *)(r10 -8) = 0")
+__xlated("2: call")
+__xlated("3: r1 = r0")
+__xlated("4: if r1 != 0x0 goto pc+2")
+__xlated("5: w0 ^= w0")
+__xlated("6: goto pc+1")
+__xlated("7: r0 /= r1")
+__xlated("8: r12 = *(u64 *)(r10 -16)")
+__xlated("9: if r12 == 0x0 goto pc+9")
+__xlated("...")
+__xlated("15: if r1 != 0x0 goto pc+2")
+__xlated("16: w0 ^= w0")
+__xlated("17: goto pc+1")
+__xlated("18: r0 /= r1")
+__xlated("19: exit")
+__naked void patch_list_may_goto(void)
+{
+	asm volatile (
+	"call %[bpf_get_prandom_u32];"
+	"r1 = r0;"
+	"r0 /= r1;"
+	".8byte %[may_goto];"
+	"r0 /= r1;"
+	"exit;"
+	:
+	: __imm(bpf_get_prandom_u32),
+	  __imm_insn(may_goto, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 1 /* offset */, 0))
+	: __clobber_all);
+}
+
+char _license[] SEC("license") = "GPL";

3/2 2026/10/02 05:48 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 17405
							output: 33
							thoughts: 603
						

Thoughts:
**Analyzing a BPF Verifier Patch**

Okay, so this patch is digging into the heart of BPF, specifically the verifier and instruction patching mechanisms. I see it's touching these files: `include/linux/bpf.h`, `include/linux/bpf_verifier.h`, `kernel/bpf/bpf_insn_array.c`, `kernel/bpf/fixups.c`, `kernel/bpf/verifier.c`, and a few selftests. Seems like a pretty focused area of change.

Looking closer at `include/linux/bpf_verifier.h`, there are new structs like `bpf_insn_patch`, `bpf_patch_list`, and the `bpf_verifier_env` now contains a `bpf_patch_list`. I see the usual suspects: `bpf_patch_list_add`, `bpf_patch_list_commit`, and `bpf_patch_list_free`. This is clearly setting up a deferred patching mechanism.

In `kernel/bpf/fixups.c`, there's a bunch of new or modified functions: `bpf_patch_list_add()`, `bpf_patch_list_free()`, `insn_get_rel()`, `patch_list_adj_insn()`, `patch_list_adj_aux()`, `patch_list_remap()`, and crucially, `bpf_patch_list_commit()`. The big change is the rewrite of `bpf_do_misc_fixups` to use this new list-based approach instead of immediate `bpf_patch_insn_data()` calls. `bpf_insn_array.c` has `bpf_insn_array_remap()`. And, in `verifier.c`, I spot that `bpf_patch_list_free(env)` is now called in the error path of `bpf_check`. Good hygiene.

This is immediately interesting. This isn't some obscure code path. The BPF verifier is fundamental, directly exposed to userspace through `bpf(BPF_PROG_LOAD, ...)`. The fact that it modifies instruction patching in `bpf_do_misc_fixups` – which is *everywhere* when loading BPF programs (divisions, may_goto, descriptor poking, tail calls, map lookups, etc.) – raises a flag. Historically, the instruction rewriting *within* the verifier has been a veritable minefield for security bugs: bounds issues, branch offset adjustments, memory corruptions, aux data mismatches, verifier bugs... the list goes on.

This is *definitely* worth fuzzing. The patch is changing the way instruction patching is done, which has historically been a source of a lot of problems! Let me use `grepper` and see where `bpf_do_misc_fixups` is called, and what config options guard `kernel/bpf/fixups.c` and `bpf_insn_array.c`. Gotta understand the context and control flow really well.



4/2 2026/10/02 05:48 tool grepper
0m Args:
{
  "Expression": "bpf_do_misc_fixups",
  "PathPrefix": "kernel/bpf"
}

Results:
{
  "Output": "kernel/bpf/fixups.c=69=static bool bpf_is_reg64(struct bpf_prog *prog, struct bpf_insn *insn)\n--\nkernel/bpf/fixups.c-84-\nkernel/bpf/fixups.c:85:\t/* address space casts converted to BPF_ALU, see bpf_do_misc_fixups() */\nkernel/bpf/fixups.c-86-\tif (is_addr_space_cast32(prog, insn))\n--\nkernel/bpf/fixups.c=2012=static int may_goto_expand(struct bpf_insn *insn_buf, int off, int stack_off,\n--\nkernel/bpf/fixups.c-2033- */\nkernel/bpf/fixups.c:2034:int bpf_do_misc_fixups(struct bpf_verifier_env *env)\nkernel/bpf/fixups.c-2035-{\n--\nkernel/bpf/verifier.c=22403=int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\n--\nkernel/bpf/verifier.c-22656-\tif (ret == 0)\nkernel/bpf/verifier.c:22657:\t\tret = bpf_do_misc_fixups(env);\nkernel/bpf/verifier.c-22658-\n"
}

5/2 2026/10/02 05:48 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 19092
							output: 28
							thoughts: 37
						

Thoughts:
Okay, let's dive into this. Here's how I'd approach that kernel task:

**Investigating BPF Compilation Options**

Right, so I need to figure out which kernel configuration options are responsible for compiling `fixups.c` and `bpf_insn_array.c`, both of which, as I recall, are within the `kernel/bpf/` directory. My instinct is to check the `Makefile` in that directory. That's usually the authoritative source for build dependencies and conditional compilation flags within a given subsystem. It's the first place I'd look to understand the exact conditions under which these specific `.c` files are included in the kernel image. I need to understand this to debug or modify these files.



6/2 2026/10/02 05:48 tool grepper
0m Args:
{
  "Expression": "fixups\\.o",
  "PathPrefix": "kernel/bpf"
}

Results:
{
  "Output": "kernel/bpf/Makefile=13=obj-$(CONFIG_BPF_SYSCALL) += bpf_local_storage.o bpf_task_storage.o\nkernel/bpf/Makefile:14:obj-$(CONFIG_BPF_SYSCALL) += fixups.o cfg.o states.o backtrack.o check_btf.o\nkernel/bpf/Makefile-15-obj-${CONFIG_BPF_LSM}\t  += bpf_inode_storage.o\n"
}

7/2 2026/10/02 05:48 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 19444
							output: 31
							thoughts: 46
						

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

**Investigating BPF Object Dependencies**

Okay, so `fixups.o` is definitely built with `CONFIG_BPF_SYSCALL`, that's a given. Now, I need to understand the dependencies and how this relates to the overall BPF infrastructure. My next step is to examine `bpf_insn_array.o` in `kernel/bpf/Makefile`. Knowing the interconnections in that directory will give me a better understanding. I'll check to see if it, too, is built with that config option or interacts with other key BPF components in this area. This will help me to understand how these elements fit into the larger picture of the kernel's BPF implementation and identify potential areas of concern or further exploration. Understanding these interactions is key to understanding how these sections work.



8/2 2026/10/02 05:49 tool grepper
0m Args:
{
  "Expression": "bpf_insn_array",
  "PathPrefix": "kernel/bpf"
}

Results:
{
  "Output": "kernel/bpf/Makefile=11=obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o bloom_filter.o\nkernel/bpf/Makefile:12:obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o ringbuf.o bpf_insn_array.o\nkernel/bpf/Makefile-13-obj-$(CONFIG_BPF_SYSCALL) += bpf_local_storage.o bpf_task_storage.o\n--\nkernel/bpf/bpf_insn_array.c-5-\nkernel/bpf/bpf_insn_array.c:6:struct bpf_insn_array {\nkernel/bpf/bpf_insn_array.c-7-\tstruct bpf_map map;\n--\nkernel/bpf/bpf_insn_array.c-9-\tlong *ips;\nkernel/bpf/bpf_insn_array.c:10:\tDECLARE_FLEX_ARRAY(struct bpf_insn_array_value, values);\nkernel/bpf/bpf_insn_array.c-11-};\n--\nkernel/bpf/bpf_insn_array.c-13-#define cast_insn_array(MAP_PTR) \\\nkernel/bpf/bpf_insn_array.c:14:\tcontainer_of((MAP_PTR), struct bpf_insn_array, map)\nkernel/bpf/bpf_insn_array.c-15-\n--\nkernel/bpf/bpf_insn_array.c=18=static inline u64 insn_array_alloc_size(u32 max_entries)\nkernel/bpf/bpf_insn_array.c-19-{\nkernel/bpf/bpf_insn_array.c:20:\tconst u64 base_size = sizeof(struct bpf_insn_array);\nkernel/bpf/bpf_insn_array.c:21:\tconst u64 entry_size = sizeof(struct bpf_insn_array_value);\nkernel/bpf/bpf_insn_array.c-22-\n--\nkernel/bpf/bpf_insn_array.c=26=static int insn_array_alloc_check(union bpf_attr *attr)\nkernel/bpf/bpf_insn_array.c-27-{\nkernel/bpf/bpf_insn_array.c:28:\tu32 value_size = sizeof(struct bpf_insn_array_value);\nkernel/bpf/bpf_insn_array.c-29-\n--\nkernel/bpf/bpf_insn_array.c=37=static void insn_array_free(struct bpf_map *map)\nkernel/bpf/bpf_insn_array.c-38-{\nkernel/bpf/bpf_insn_array.c:39:\tstruct bpf_insn_array *insn_array = cast_insn_array(map);\nkernel/bpf/bpf_insn_array.c-40-\n--\nkernel/bpf/bpf_insn_array.c=44=static struct bpf_map *insn_array_alloc(union bpf_attr *attr)\n--\nkernel/bpf/bpf_insn_array.c-46-\tu64 size = insn_array_alloc_size(attr-\u003emax_entries);\nkernel/bpf/bpf_insn_array.c:47:\tstruct bpf_insn_array *insn_array;\nkernel/bpf/bpf_insn_array.c-48-\n--\nkernel/bpf/bpf_insn_array.c=64=static void *insn_array_lookup_elem(struct bpf_map *map, void *key)\nkernel/bpf/bpf_insn_array.c-65-{\nkernel/bpf/bpf_insn_array.c:66:\tstruct bpf_insn_array *insn_array = cast_insn_array(map);\nkernel/bpf/bpf_insn_array.c-67-\tu32 index = *(u32 *)key;\n--\nkernel/bpf/bpf_insn_array.c=75=static long insn_array_update_elem(struct bpf_map *map, void *key, void *value, u64 map_flags)\nkernel/bpf/bpf_insn_array.c-76-{\nkernel/bpf/bpf_insn_array.c:77:\tstruct bpf_insn_array *insn_array = cast_insn_array(map);\nkernel/bpf/bpf_insn_array.c-78-\tu32 index = *(u32 *)key;\nkernel/bpf/bpf_insn_array.c:79:\tstruct bpf_insn_array_value val = {};\nkernel/bpf/bpf_insn_array.c-80-\n--\nkernel/bpf/bpf_insn_array.c=120=static int insn_array_map_direct_value_addr(const struct bpf_map *map, u64 *imm, u32 off)\nkernel/bpf/bpf_insn_array.c-121-{\nkernel/bpf/bpf_insn_array.c:122:\tstruct bpf_insn_array *insn_array = cast_insn_array(map);\nkernel/bpf/bpf_insn_array.c-123-\n--\nkernel/bpf/bpf_insn_array.c-133-\nkernel/bpf/bpf_insn_array.c:134:BTF_ID_LIST_SINGLE(insn_array_btf_ids, struct, bpf_insn_array)\nkernel/bpf/bpf_insn_array.c-135-\n--\nkernel/bpf/bpf_insn_array.c=157=static bool is_insn_array(const struct bpf_map *map)\n--\nkernel/bpf/bpf_insn_array.c-161-\nkernel/bpf/bpf_insn_array.c:162:static inline bool valid_offsets(const struct bpf_insn_array *insn_array,\nkernel/bpf/bpf_insn_array.c-163-\t\t\t\t const struct bpf_prog *prog)\n--\nkernel/bpf/bpf_insn_array.c-182-\nkernel/bpf/bpf_insn_array.c:183:int bpf_insn_array_init(struct bpf_map *map, const struct bpf_prog *prog)\nkernel/bpf/bpf_insn_array.c-184-{\nkernel/bpf/bpf_insn_array.c:185:\tstruct bpf_insn_array *insn_array = cast_insn_array(map);\nkernel/bpf/bpf_insn_array.c:186:\tstruct bpf_insn_array_value *values = insn_array-\u003evalues;\nkernel/bpf/bpf_insn_array.c-187-\tint i;\n--\nkernel/bpf/bpf_insn_array.c-215-\nkernel/bpf/bpf_insn_array.c:216:int bpf_insn_array_ready(struct bpf_map *map)\nkernel/bpf/bpf_insn_array.c-217-{\nkernel/bpf/bpf_insn_array.c:218:\tstruct bpf_insn_array *insn_array = cast_insn_array(map);\nkernel/bpf/bpf_insn_array.c-219-\tint i;\n--\nkernel/bpf/bpf_insn_array.c-230-\nkernel/bpf/bpf_insn_array.c:231:void bpf_insn_array_release(struct bpf_map *map)\nkernel/bpf/bpf_insn_array.c-232-{\nkernel/bpf/bpf_insn_array.c:233:\tstruct bpf_insn_array *insn_array = cast_insn_array(map);\nkernel/bpf/bpf_insn_array.c-234-\nkernel/bpf/bpf_insn_array.c:235:\t/* Paired with atomic_xchg() in bpf_insn_array_init(). */\nkernel/bpf/bpf_insn_array.c-236-\tatomic_set_release(\u0026insn_array-\u003eused, 0);\n--\nkernel/bpf/bpf_insn_array.c-238-\nkernel/bpf/bpf_insn_array.c:239:void bpf_insn_array_adjust(struct bpf_map *map, u32 first, u32 len)\nkernel/bpf/bpf_insn_array.c-240-{\nkernel/bpf/bpf_insn_array.c:241:\tstruct bpf_insn_array *insn_array = cast_insn_array(map);\nkernel/bpf/bpf_insn_array.c-242-\tint i;\n--\nkernel/bpf/bpf_insn_array.c-259- */\nkernel/bpf/bpf_insn_array.c:260:void bpf_insn_array_remap(struct bpf_map *map, const u32 *new_off, u32 cnt, u32 grow)\nkernel/bpf/bpf_insn_array.c-261-{\nkernel/bpf/bpf_insn_array.c:262:\tstruct bpf_insn_array *insn_array = cast_insn_array(map);\nkernel/bpf/bpf_insn_array.c-263-\tu32 off;\n--\nkernel/bpf/bpf_insn_array.c-273-\nkernel/bpf/bpf_insn_array.c:274:void bpf_insn_array_adjust_after_remove(struct bpf_map *map, u32 off, u32 len)\nkernel/bpf/bpf_insn_array.c-275-{\nkernel/bpf/bpf_insn_array.c:276:\tstruct bpf_insn_array *insn_array = cast_insn_array(map);\nkernel/bpf/bpf_insn_array.c-277-\tint i;\n--\nkernel/bpf/bpf_insn_array.c=297=void bpf_prog_update_insn_ptrs(struct bpf_prog *prog, u32 *offsets, void *image)\nkernel/bpf/bpf_insn_array.c-298-{\nkernel/bpf/bpf_insn_array.c:299:\tstruct bpf_insn_array *insn_array;\nkernel/bpf/bpf_insn_array.c-300-\tstruct bpf_map *map;\n--\nkernel/bpf/cfg.c=204=static int copy_insn_array(struct bpf_map *map, u32 start, u32 end, u32 *items)\nkernel/bpf/cfg.c-205-{\nkernel/bpf/cfg.c:206:\tstruct bpf_insn_array_value *value;\nkernel/bpf/cfg.c-207-\tu32 i;\n--\nkernel/bpf/fixups.c=226=static void adjust_insn_aux_data(struct bpf_verifier_env *env,\n--\nkernel/bpf/fixups.c-289-\t * actually the first instruction, so move it back. This also matches with the behavior\nkernel/bpf/fixups.c:290:\t * of bpf_insn_array_adjust(), which preserves xlated_off to point to the first new\nkernel/bpf/fixups.c-291-\t * instruction. For BPF_PATCH_MOVE_TARGET the original instruction is the last one,\n--\nkernel/bpf/fixups.c=314=static void adjust_insn_arrays(struct bpf_verifier_env *env, u32 first, u32 len)\n--\nkernel/bpf/fixups.c-321-\tfor (i = 0; i \u003c env-\u003einsn_array_map_cnt; i++)\nkernel/bpf/fixups.c:322:\t\tbpf_insn_array_adjust(env-\u003einsn_array_maps[i], first, len);\nkernel/bpf/fixups.c-323-}\n--\nkernel/bpf/fixups.c=325=static void adjust_insn_arrays_after_remove(struct bpf_verifier_env *env, u32 off, u32 len)\n--\nkernel/bpf/fixups.c-329-\tfor (i = 0; i \u003c env-\u003einsn_array_map_cnt; i++)\nkernel/bpf/fixups.c:330:\t\tbpf_insn_array_adjust_after_remove(env-\u003einsn_array_maps[i], off, len);\nkernel/bpf/fixups.c-331-}\n--\nkernel/bpf/fixups.c=586=int bpf_patch_list_commit(struct bpf_verifier_env *env)\n--\nkernel/bpf/fixups.c-680-\tfor (i = 0; i \u003c env-\u003einsn_array_map_cnt; i++)\nkernel/bpf/fixups.c:681:\t\tbpf_insn_array_remap(env-\u003einsn_array_maps[i], new_off, cnt, grow);\nkernel/bpf/fixups.c-682-\n--\nkernel/bpf/syscall.c=2904=static int bpf_prog_mark_insn_arrays_ready(struct bpf_prog *prog)\n--\nkernel/bpf/syscall.c-2912-\nkernel/bpf/syscall.c:2913:\t\terr = bpf_insn_array_ready(prog-\u003eaux-\u003eused_maps[i]);\nkernel/bpf/syscall.c-2914-\t\tif (err)\n--\nkernel/bpf/verifier.c=4865=static int check_map_kptr_access(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-4937- * Return the size of the memory region accessible from a pointer to map value.\nkernel/bpf/verifier.c:4938: * For INSN_ARRAY maps whole bpf_insn_array-\u003eips array is accessible.\nkernel/bpf/verifier.c-4939- */\n--\nkernel/bpf/verifier.c=19986=static int __add_used_map(struct bpf_verifier_env *env, struct bpf_map *map)\n--\nkernel/bpf/verifier.c-20027-\tif (map-\u003emap_type == BPF_MAP_TYPE_INSN_ARRAY) {\nkernel/bpf/verifier.c:20028:\t\terr = bpf_insn_array_init(map, env-\u003eprog);\nkernel/bpf/verifier.c-20029-\t\tif (err) {\n--\nkernel/bpf/verifier.c=20570=static void release_insn_arrays(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-20574-\tfor (i = 0; i \u003c env-\u003einsn_array_map_cnt; i++)\nkernel/bpf/verifier.c:20575:\t\tbpf_insn_array_release(env-\u003einsn_array_maps[i]);\nkernel/bpf/verifier.c-20576-}\n"
}

9/2 2026/10/02 05:49 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 23776
							output: 31
							thoughts: 29
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Investigating BPF Syscall Configuration**

Alright, I need to understand the configuration related to `BPF_SYSCALL`, and my first instinct is to dig into the Kconfig files. The most logical places to start are `init/Kconfig` and `kernel/bpf/Kconfig`. I'll want to review how that flag is defined, what dependencies it has, and perhaps where it's referenced. This will clarify if it’s a core BPF feature, or maybe gated behind some other subsystems. I'll be looking for clues about its implications for the kernel's overall functionality and security posture. It's a fundamental part of the BPF ecosystem, so understanding its configuration is paramount to getting a grip on how it works.



10/2 2026/10/02 05:49 tool grepper
0m Args:
{
  "Expression": "config BPF_SYSCALL",
  "PathPrefix": "kernel/bpf"
}

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

11/2 2026/10/02 05:49 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 24096
							output: 34
							thoughts: 49
						

Thoughts:
Here's my thought process, as requested:

**Examining Configuration Dependencies for eBPF JIT**

Okay, so I'm looking at eBPF and wondering if there are any further configuration options I need to consider, beyond what I've already set. Specifically, I need to know if I've missed anything to enable the eBPF JIT (Just-In-Time) compiler.

Hmm, I think `BPF_JIT` requires some other configurations, but I can't quite recall the details off the top of my head. I need to refresh my memory on the dependencies of `BPF_SYSCALL` – is it dependent on any other kernel features or not? The `kernel/bpf/Kconfig` file should give me the answers I'm looking for. Let me check that.



12/2 2026/10/02 05:49 tool read-file
0m Args:
{
  "File": "kernel/bpf/Kconfig",
  "FirstLine": 30,
  "LineCount": 30
}

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

13/2 2026/10/02 05:49 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 24778
							output: 230
							thoughts: 1229
						

Thoughts:
**BPF Patching Logic Triage: A Deep Dive**

Okay, let's break down this BPF patch implementation. My primary focus is on reachability and fuzzing potential, so I'm starting by identifying the key functions added or modified in the patch. I've pinpointed the following: `bpf_insn_array_remap` in `bpf_insn_array.c`, and in `fixups.c`, `bpf_patch_list_add`, `bpf_patch_list_free`, `bpf_patch_list_commit`, and `bpf_do_misc_fixups`. Notably, `bpf_check` in `verifier.c`, which is the entry point, calls `bpf_do_misc_fixups`, making the latter a critical point of interest. The others are helpers or supporting functions for the commit.

Now, considering these functions, `bpf_do_misc_fixups` stands out as a critical point. `bpf_patch_list_add` queues instructions, and `bpf_patch_list_commit` is the batch application engine. It's the one that replaces instructions, adjusts jumps/calls, reallocates aux data, and remaps subprograms, Linfo, insn_arrays, and func_ptrs. It's a complex function, and `bpf_insn_array_remap` updates offsets for `BPF_MAP_TYPE_INSN_ARRAY` during `patch_list_commit` runs. `bpf_patch_list_free` is the clean-up.

I've confirmed that these functions, being non-static, are visible externally and have distinct symbol addresses, as confirmed by inspecting their declarations in `include/linux/bpf_verifier.h` and `include/linux/bpf.h`.

Next, I've checked the required configuration options. `BPF_SYSCALL` is a given, as the `bpf()` syscall is involved. But I suspect that `BPF_JIT` might be relevant, given that `bpf_patch_list_commit` touches `poke_tab` in the `prog->aux` structure, and the JIT is used for JIT and poke descriptor logic in some cases. The presence of `BPF_SYSCALL` and `BPF_JIT` is typical in BPF testing environments. I've also checked `bpf_insn_array.c`'s dependencies, which are only the system call as expected.

Finally, I've re-evaluated my initial assessment. The reachability is solid: any BPF program loaded via `sys_bpf(BPF_PROG_LOAD, ...)` runs `bpf_check()`, which invokes `bpf_do_misc_fixups()`. Any patching (which happens frequently) triggers `bpf_patch_list_add` and `bpf_patch_list_commit`. Given the complex instruction patching, jump offset recomputation, aux data remapping, and memory allocation, this is definitely worth fuzzing.

My focus symbols are thus: `bpf_do_misc_fixups`, `bpf_patch_list_commit`, `bpf_patch_list_add`, `bpf_patch_list_free`, and `bpf_insn_array_remap`. The necessary enable configs are `BPF_SYSCALL` and `BPF_JIT`. All clear. I'm ready to move on.



14/2 2026/10/02 05:49 tool set-results
0m Args:
{
  "EnableConfigs": [
    "BPF_SYSCALL",
    "BPF_JIT"
  ],
  "FocusSymbols": [
    "bpf_do_misc_fixups",
    "bpf_patch_list_commit",
    "bpf_patch_list_add",
    "bpf_patch_list_free",
    "bpf_insn_array_remap"
  ],
  "Reasoning": "The patch replaces the incremental instruction patching in bpf_do_misc_fixups() with a deferred, batched instruction patching mechanism (bpf_patch_list_add and bpf_patch_list_commit). This involves complex jump target recalculations, instruction aux data remapping, subprog offset adjustments, and memory reallocations in the BPF verifier. The code is directly reachable from userspace via the bpf() system call when loading BPF programs. Fuzzing is critical to detect potential branch offset calculation bugs, out-of-bounds array access, or invariant violations during instruction rewriting.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL",
    "BPF_JIT"
  ],
  "FocusSymbols": [
    "bpf_do_misc_fixups",
    "bpf_patch_list_commit",
    "bpf_patch_list_add",
    "bpf_patch_list_free",
    "bpf_insn_array_remap"
  ],
  "Reasoning": "The patch replaces the incremental instruction patching in bpf_do_misc_fixups() with a deferred, batched instruction patching mechanism (bpf_patch_list_add and bpf_patch_list_commit). This involves complex jump target recalculations, instruction aux data remapping, subprog offset adjustments, and memory reallocations in the BPF verifier. The code is directly reachable from userspace via the bpf() system call when loading BPF programs. Fuzzing is critical to detect potential branch offset calculation bugs, out-of-bounds array access, or invariant violations during instruction rewriting.",
  "WorthFuzzing": true
}

15/1 2026/10/02 05:49 action If
1m Args:
{
  "WorthFuzzing": true
}

Results:
null

16/2 2026/10/02 05:49 agent kmsan-evaluator
1m Model:
core

Args:
null

Results:
{
  "KMSANReasoning": "The patch series replaces repeated individual instruction patching during bpf_do_misc_fixups() with a batched instruction patching mechanism (bpf_patch_list_add and bpf_patch_list_commit).\n\nAnalysis of memory initialization and info-leak risks:\n1. All allocated structures and arrays (pl-\u003epatches, pl-\u003einsns, new_off, intermediate insns, env-\u003einsn_aux_data, and reallocated bpf_prog) are completely initialized:\n   - pl-\u003epatches and pl-\u003einsns have their elements fully populated upon queueing in bpf_patch_list_add().\n   - new_off is allocated for cnt + 1 elements and fully filled in the loop covering 0 to cnt.\n   - insns is populated for every offset in [0, new_cnt) through contiguous partition slices.\n   - env-\u003einsn_aux_data is reallocated with __GFP_ZERO, zeroed via memset for new patch slots, and initialized through patch_list_adj_aux().\n   - Local variables in helper functions (e.g., insn_get_rel, patch_list_adj_insn) are explicitly assigned before use.\n2. No uninitialized memory or structure padding is exposed to userspace.\n3. Potential bugs in offset calculation, jump relocation, or buffer resizing would manifest as out-of-bounds memory accesses or invalid control flow, which are covered by KASAN.\n\nTherefore, the changes do not expose uninitialized memory risks that would necessitate a dedicated KMSAN fuzzing session.",
  "NeedsKMSAN": false
}

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

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

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

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

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

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

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


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

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit 4a0d28041e8f5b17363997575245b86c94c1991c
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Oct 2 05:48:19 2026 +0000

    syz-cluster: applied patch under review

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index 4bae3796c42f6..17932ff8b0ae9 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -4299,6 +4299,7 @@ int bpf_insn_array_ready(struct bpf_map *map);
 void bpf_insn_array_release(struct bpf_map *map);
 void bpf_insn_array_adjust(struct bpf_map *map, u32 first, u32 len);
 void bpf_insn_array_adjust_after_remove(struct bpf_map *map, u32 off, u32 len);
+void bpf_insn_array_remap(struct bpf_map *map, const u32 *new_off, u32 cnt, u32 grow);
 
 #ifdef CONFIG_BPF_SYSCALL
 void bpf_prog_update_insn_ptrs(struct bpf_prog *prog, u32 *offsets, void *image);
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 811342e3c0419..5a968539589f8 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -929,6 +929,27 @@ struct bpf_fd_array {
 	};
 };
 
+/* one deferred patch: insn 'off' is replaced by 'len' insns */
+struct bpf_insn_patch {
+	u32 off;
+	u32 len;
+	u32 start;		/* first insn of the patch in bpf_patch_list.insns */
+	struct bpf_insn orig;	/* the replaced insn when the patch was queued */
+};
+
+/*
+ * Patches queued by a rewrite pass, applied together by
+ * bpf_patch_list_commit() in O(prog->len + inserted insns).
+ */
+struct bpf_patch_list {
+	struct bpf_insn_patch *patches;
+	struct bpf_insn *insns;
+	u32 cnt;
+	u32 cap;
+	u32 insn_cnt;
+	u32 insn_cap;
+};
+
 /* single container for all structs
  * one verifier_env per bpf_check() call
  */
@@ -973,6 +994,7 @@ struct bpf_verifier_env {
 	bool signature;
 	u32 insn_aux_data_len;
 	struct bpf_insn_aux_data *insn_aux_data; /* array of per-insn state */
+	struct bpf_patch_list patch_list;
 	const struct bpf_line_info *prev_linfo;
 	struct bpf_verifier_log log;
 	struct bpf_diag *diag;
@@ -1834,5 +1856,9 @@ int bpf_jit_subprogs(struct bpf_verifier_env *env);
 int bpf_fixup_call_args(struct bpf_verifier_env *env);
 int bpf_do_misc_fixups(struct bpf_verifier_env *env);
 int bpf_insn_def32(struct bpf_prog *prog, struct bpf_insn *insn);
+struct bpf_insn *bpf_patch_list_add(struct bpf_verifier_env *env, u32 off,
+				    const struct bpf_insn *patch, u32 len);
+int bpf_patch_list_commit(struct bpf_verifier_env *env);
+void bpf_patch_list_free(struct bpf_verifier_env *env);
 
 #endif /* _LINUX_BPF_VERIFIER_H */
diff --git a/kernel/bpf/bpf_insn_array.c b/kernel/bpf/bpf_insn_array.c
index 92bbb71576ae3..74f07ced85cb0 100644
--- a/kernel/bpf/bpf_insn_array.c
+++ b/kernel/bpf/bpf_insn_array.c
@@ -253,6 +253,24 @@ void bpf_insn_array_adjust(struct bpf_map *map, u32 first, u32 len)
 	}
 }
 
+/*
+ * Move each offset to new_off[offset], new_off[] covers [0, cnt]. Offsets
+ * past the end of the old program only move by 'grow'.
+ */
+void bpf_insn_array_remap(struct bpf_map *map, const u32 *new_off, u32 cnt, u32 grow)
+{
+	struct bpf_insn_array *insn_array = cast_insn_array(map);
+	u32 off;
+	int i;
+
+	for (i = 0; i < map->max_entries; i++) {
+		off = insn_array->values[i].xlated_off;
+		if (off == INSN_DELETED)
+			continue;
+		insn_array->values[i].xlated_off = off <= cnt ? new_off[off] : off + grow;
+	}
+}
+
 void bpf_insn_array_adjust_after_remove(struct bpf_map *map, u32 off, u32 len)
 {
 	struct bpf_insn_array *insn_array = cast_insn_array(map);
diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
index 37cf130ebb57b..0e8c61ede9a5b 100644
--- a/kernel/bpf/fixups.c
+++ b/kernel/bpf/fixups.c
@@ -404,6 +404,301 @@ struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
 	return __bpf_patch_insn_data(env, off, patch, len, BPF_PATCH_KEEP_TARGET);
 }
 
+/*
+ * Queue the replacement of insn 'off' by 'patch[0..len)'. Nothing moves until
+ * bpf_patch_list_commit(), so a pass keeps addressing insns and their aux data
+ * by their index in env->prog. Patches must be queued in increasing order of
+ * 'off'. Jumps in the patch that leave it are relative to the patch placed at
+ * 'off', the same way as for bpf_patch_insn_data().
+ *
+ * Return the copy of the last insn of the patch, which stays valid until the
+ * next call.
+ */
+struct bpf_insn *bpf_patch_list_add(struct bpf_verifier_env *env, u32 off,
+				    const struct bpf_insn *patch, u32 len)
+{
+	struct bpf_patch_list *pl = &env->patch_list;
+	struct bpf_insn_patch *p;
+	struct bpf_insn *insns;
+	u32 cap;
+
+	if (bpf_rewrite_must_abort())
+		return ERR_PTR(-EINTR);
+
+	if (verifier_bug_if(!len || off >= env->prog->len ||
+			    (pl->cnt && off <= pl->patches[pl->cnt - 1].off),
+			    env, "insn %u patched out of order", off))
+		return ERR_PTR(-EFAULT);
+
+	/* Replacing one insn by one moves nothing, do it right away. */
+	if (len == 1) {
+		env->prog->insnsi[off] = *patch;
+		adjust_insn_aux_data(env, env->prog, off, 1, NULL,
+				     BPF_PATCH_KEEP_TARGET);
+		return &env->prog->insnsi[off];
+	}
+
+	if (pl->cnt == pl->cap) {
+		cap = max(pl->cap * 2, 16U);
+		p = kvrealloc(pl->patches, array_size(cap, sizeof(*p)),
+			      GFP_KERNEL_ACCOUNT);
+		if (!p)
+			return ERR_PTR(-ENOMEM);
+		pl->patches = p;
+		pl->cap = cap;
+	}
+
+	if (pl->insn_cnt + len > pl->insn_cap) {
+		cap = max3(pl->insn_cap * 2, pl->insn_cnt + len, 64U);
+		insns = kvrealloc(pl->insns, array_size(cap, sizeof(*insns)),
+				  GFP_KERNEL_ACCOUNT);
+		if (!insns)
+			return ERR_PTR(-ENOMEM);
+		pl->insns = insns;
+		pl->insn_cap = cap;
+	}
+
+	p = &pl->patches[pl->cnt++];
+	p->off = off;
+	p->len = len;
+	p->start = pl->insn_cnt;
+	p->orig = env->prog->insnsi[off];
+	memcpy(pl->insns + p->start, patch, len * sizeof(*patch));
+	pl->insn_cnt += len;
+
+	return &pl->insns[p->start + len - 1];
+}
+
+void bpf_patch_list_free(struct bpf_verifier_env *env)
+{
+	struct bpf_patch_list *pl = &env->patch_list;
+
+	kvfree(pl->patches);
+	kvfree(pl->insns);
+	memset(pl, 0, sizeof(*pl));
+}
+
+/* Extract the pc-relative operand of a jump, a subprog call or a ld_imm64 of a subprog. */
+static bool insn_get_rel(const struct bpf_insn *insn, s64 *rel, bool *is_imm)
+{
+	u8 code = insn->code;
+
+	if (bpf_pseudo_func(insn) || bpf_pseudo_call(insn)) {
+		*rel = insn->imm;
+		*is_imm = true;
+		return true;
+	}
+	if ((BPF_CLASS(code) != BPF_JMP && BPF_CLASS(code) != BPF_JMP32) ||
+	    BPF_OP(code) == BPF_CALL || BPF_OP(code) == BPF_EXIT)
+		return false;
+
+	*is_imm = code == (BPF_JMP32 | BPF_JA);
+	*rel = *is_imm ? insn->imm : insn->off;
+	return true;
+}
+
+/*
+ * insn is number 'idx' of the 'len' insns that replace insn 'off' of the old
+ * image (an insn that isn't patched is its own patch of length 1). It lands
+ * at 'pos' of the new image. 'new_off' maps the old image to the new one.
+ */
+static int patch_list_adj_insn(struct bpf_insn *insn, const u32 *new_off,
+			       u32 cnt, u32 off, u32 idx, u32 len, u32 pos)
+{
+	bool is_imm;
+	s64 rel, tgt;
+
+	if (!insn_get_rel(insn, &rel, &is_imm))
+		return 0;
+
+	/* relative to the first insn of the patch */
+	tgt = (s64)idx + rel + 1;
+	if (tgt >= 0 && tgt < len)
+		return 0;
+
+	/* the patch pushed what follows 'off' down by len - 1 */
+	tgt = tgt < 0 ? off + tgt : off + tgt - len + 1;
+	if (tgt < 0 || tgt > cnt)
+		return -EFAULT;
+
+	rel = (s64)new_off[tgt] - pos - 1;
+	if (is_imm) {
+		if (rel < S32_MIN || rel > S32_MAX)
+			return -ERANGE;
+		insn->imm = rel;
+	} else {
+		if (rel < S16_MIN || rel > S16_MAX)
+			return -ERANGE;
+		insn->off = rel;
+	}
+	return 0;
+}
+
+/*
+ * Fill the aux data of the 'p->len' insns at 'slot' from the aux data 'old'
+ * of the insn they replace, the same way as adjust_insn_aux_data() does.
+ */
+static void patch_list_adj_aux(struct bpf_prog *prog, struct bpf_insn_patch *p,
+			       struct bpf_insn *patch,
+			       const struct bpf_insn_aux_data *old,
+			       struct bpf_insn_aux_data *slot)
+{
+	struct bpf_insn_aux_data *last = slot + p->len - 1;
+	struct bpf_insn *orig = &p->orig;
+	u32 i;
+
+	*last = *old;
+	last->zext_dst = bpf_insn_def32(prog, &patch[p->len - 1]) >= 0;
+	memset(slot, 0, sizeof(*slot) * (p->len - 1));
+
+	for (i = 0; i < p->len - 1; i++) {
+		slot[i].seen = last->seen;
+		slot[i].zext_dst = bpf_insn_def32(prog, &patch[i]) >= 0;
+		if (!memcmp(&patch[i], orig, sizeof(*orig))) {
+			slot[i].non_stack_access = last->non_stack_access;
+			last->non_stack_access = false;
+		} else if (bpf_is_mem_insn(&patch[i])) {
+			slot[i].non_stack_access = true;
+		}
+	}
+
+	if (bpf_is_mem_insn(&patch[p->len - 1]) &&
+	    memcmp(&patch[p->len - 1], orig, sizeof(*orig)))
+		last->non_stack_access = true;
+
+	/* indirect jumps to the replaced insn land on the first new one */
+	if (last->indirect_target) {
+		slot[0].indirect_target = 1;
+		last->indirect_target = 0;
+	}
+}
+
+static u32 patch_list_remap(const u32 *new_off, u32 cnt, u32 grow, u32 off)
+{
+	return off <= cnt ? new_off[off] : off + grow;
+}
+
+/*
+ * Apply all queued patches in one go. Everything that names an insn of the
+ * old image follows it to the new one, a patched insn is named by the first
+ * insn of its patch, like with bpf_patch_insn_data().
+ */
+int bpf_patch_list_commit(struct bpf_verifier_env *env)
+{
+	struct bpf_patch_list *pl = &env->patch_list;
+	struct bpf_prog *prog = env->prog;
+	u32 cnt = prog->len, new_cnt, grow = 0, end, i, k, n;
+	struct bpf_insn_aux_data *data;
+	struct bpf_insn *insns = NULL;
+	struct bpf_insn_patch *p;
+	u32 *new_off = NULL;
+	int err = 0;
+
+	if (!pl->cnt)
+		goto out;
+
+	err = -ENOMEM;
+	new_off = kvmalloc_array(cnt + 1, sizeof(*new_off), GFP_KERNEL_ACCOUNT);
+	if (!new_off)
+		goto out;
+
+	for (i = 0, k = 0; i <= cnt; i++) {
+		new_off[i] = i + grow;
+		if (k < pl->cnt && pl->patches[k].off == i)
+			grow += pl->patches[k++].len - 1;
+	}
+	new_cnt = cnt + grow;
+
+	/*
+	 * Build the new image aside first, so that a jump that goes out of
+	 * range leaves the program untouched.
+	 */
+	insns = kvmalloc_array(new_cnt, sizeof(*insns), GFP_KERNEL_ACCOUNT);
+	if (!insns)
+		goto out;
+
+	for (i = 0, k = 0; i < cnt; i++) {
+		const struct bpf_insn *src = &prog->insnsi[i];
+		u32 len = 1;
+
+		if (k < pl->cnt && pl->patches[k].off == i) {
+			src = &pl->insns[pl->patches[k].start];
+			len = pl->patches[k++].len;
+		}
+
+		for (n = 0; n < len; n++) {
+			insns[new_off[i] + n] = src[n];
+			err = patch_list_adj_insn(&insns[new_off[i] + n], new_off,
+						  cnt, i, n, len, new_off[i] + n);
+			if (err == -ERANGE) {
+				verbose(env, "insn %d cannot be patched due to 16-bit range\n",
+					env->insn_aux_data[i].orig_idx);
+				goto out;
+			}
+			if (verifier_bug_if(err, env, "insn %u jumps out of the program", i))
+				goto out;
+		}
+	}
+
+	err = -ENOMEM;
+	data = vrealloc(env->insn_aux_data,
+			array_size(new_cnt, sizeof(*data)),
+			GFP_KERNEL_ACCOUNT | __GFP_ZERO);
+	if (!data)
+		goto out;
+	env->insn_aux_data = data;
+
+	prog = bpf_prog_realloc(prog, bpf_prog_size(new_cnt), GFP_USER);
+	if (!prog)
+		goto out;
+	env->prog = prog;
+
+	memcpy(prog->insnsi, insns, sizeof(*insns) * new_cnt);
+	prog->len = new_cnt;
+
+	/* Move aux data from the end, an insn never moves to a lower index. */
+	end = cnt;
+	for (k = pl->cnt; k-- > 0;) {
+		p = &pl->patches[k];
+		memmove(data + new_off[p->off + 1], data + p->off + 1,
+			sizeof(*data) * (end - p->off - 1));
+		patch_list_adj_aux(prog, p, &pl->insns[p->start], &data[p->off],
+				   &data[new_off[p->off]]);
+		end = p->off;
+	}
+	env->insn_aux_data_len = new_cnt;
+
+	/* NOTE: fake 'exit' subprog should be updated as well. */
+	for (i = 0; i <= env->subprog_cnt; i++)
+		env->subprog_info[i].start =
+			patch_list_remap(new_off, cnt, grow, env->subprog_info[i].start);
+
+	for (i = 0; i < prog->aux->nr_linfo; i++)
+		prog->aux->linfo[i].insn_off =
+			patch_list_remap(new_off, cnt, grow, prog->aux->linfo[i].insn_off);
+
+	for (i = 0; i < env->insn_array_map_cnt; i++)
+		bpf_insn_array_remap(env->insn_array_maps[i], new_off, cnt, grow);
+
+	for (i = 0; i < env->func_ptr_cnt; i++) {
+		if (env->func_ptrs[i].xlated_off == BPF_FUNC_PTR_DELETED)
+			continue;
+		env->func_ptrs[i].xlated_off =
+			patch_list_remap(new_off, cnt, grow, env->func_ptrs[i].xlated_off);
+	}
+
+	for (i = 0; i < prog->aux->size_poke_tab; i++)
+		prog->aux->poke_tab[i].insn_idx =
+			patch_list_remap(new_off, cnt, grow, prog->aux->poke_tab[i].insn_idx);
+
+	err = 0;
+out:
+	kvfree(insns);
+	kvfree(new_off);
+	bpf_patch_list_free(env);
+	return err;
+}
+
 /*
  * insn was moved down by delta insns inside its own patch. Operands relative
  * to the pc that point in front of the old position did not move with it.
@@ -1749,7 +2044,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 	struct bpf_insn *insn_buf = env->insn_buf;
 	struct bpf_prog *new_prog;
 	struct bpf_map *map_ptr;
-	int i, ret, cnt, delta = 0, cur_subprog = 0;
+	int i, ret, cnt, cur_subprog = 0;
 	struct bpf_subprog_info *subprogs = env->subprog_info;
 	u16 stack_depth = subprogs[cur_subprog].stack_depth;
 	u16 stack_depth_extra = 0;
@@ -1781,7 +2076,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			goto next_insn;
 		}
 
-		if (env->insn_aux_data[i + delta].needs_zext)
+		if (env->insn_aux_data[i].needs_zext)
 			/* Convert BPF_CLASS(insn->code) == BPF_ALU64 to 32-bit ALU */
 			insn->code = BPF_ALU | BPF_OP(insn->code) | BPF_SRC(insn->code);
 
@@ -1804,13 +2099,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 
 			cnt = patch - insn_buf;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -1895,13 +2186,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 				cnt = patch - insn_buf;
 			}
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -1925,13 +2212,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			*patch++ = BPF_MOV64_IMM(insn->dst_reg, 0);
 
 			cnt = patch - insn_buf;
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -1945,13 +2228,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 				return -EFAULT;
 			}
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -1964,7 +2243,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			bool issrc, isneg, isimm;
 			u32 off_reg;
 
-			aux = &env->insn_aux_data[i + delta];
+			aux = &env->insn_aux_data[i];
 			if (!aux->alu_state ||
 			    aux->alu_state == BPF_ALU_NON_POINTER)
 				goto next_insn;
@@ -1998,13 +2277,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 				*patch++ = BPF_ALU64_IMM(BPF_MUL, off_reg, -1);
 			cnt = patch - insn_buf;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2041,13 +2316,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			cnt = may_goto_expand(insn_buf, insn->off, stack_off_cnt,
 					      tail, ARRAY_SIZE(tail));
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta += cnt - 1;
-			env->prog = prog = new_prog;
-			insn = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		} else if (bpf_is_may_goto_insn(insn)) {
 			int stack_off = -stack_depth - 8;
@@ -2060,13 +2331,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			cnt = may_goto_expand(insn_buf, insn->off, stack_off,
 					      tail, ARRAY_SIZE(tail));
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta += cnt - 1;
-			env->prog = prog = new_prog;
-			insn = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2076,7 +2343,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 		     insn->src_reg == BPF_PSEUDO_MAP_IDX_VALUE)) {
 			struct bpf_map *map;
 
-			aux = &env->insn_aux_data[i + delta];
+			aux = &env->insn_aux_data[i];
 			map = env->used_maps[aux->map_index];
 			if (map->map_type != BPF_MAP_TYPE_PERCPU_ARRAY)
 				goto next_insn;
@@ -2097,13 +2364,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			cnt = 2;
 
 			i++;
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2112,19 +2375,15 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 		if (insn->src_reg == BPF_PSEUDO_CALL)
 			goto next_insn;
 		if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
-			ret = bpf_fixup_kfunc_call(env, insn, insn_buf, i + delta, &cnt);
+			ret = bpf_fixup_kfunc_call(env, insn, insn_buf, i, &cnt);
 			if (ret)
 				return ret;
 			if (cnt == 0)
 				goto next_insn;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta	 += cnt - 1;
-			env->prog = prog = new_prog;
-			insn	  = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2159,7 +2418,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			insn->imm = 0;
 			insn->code = BPF_JMP | BPF_TAIL_CALL;
 
-			aux = &env->insn_aux_data[i + delta];
+			aux = &env->insn_aux_data[i];
 			if (env->bpf_capable && !prog->blinding_requested &&
 			    prog->jit_requested &&
 			    !bpf_map_key_poisoned(aux) &&
@@ -2169,7 +2428,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 					.reason = BPF_POKE_REASON_TAIL_CALL,
 					.tail_call.map = aux->map_ptr_state.map_ptr,
 					.tail_call.key = bpf_map_key_immediate(aux),
-					.insn_idx = i + delta,
+					.insn_idx = i,
 				};
 
 				ret = bpf_jit_add_poke_descriptor(prog, &desc);
@@ -2205,17 +2464,13 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 								 map)->index_mask);
 			insn_buf[2] = *insn;
 			cnt = 3;
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
-		aux = &env->insn_aux_data[i + delta];
+		aux = &env->insn_aux_data[i];
 		if (aux->arg_prog) {
 			/* The verifier will process callback_fn as many times as necessary
 			 * with different maps and the register states prepared by
@@ -2239,18 +2494,14 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			insn_buf[2] = *insn;
 			cnt = 3;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto patch_call_imm;
 		}
 
 		/* bpf_per_cpu_ptr() and bpf_this_cpu_ptr() */
-		if (env->insn_aux_data[i + delta].call_with_percpu_alloc_ptr) {
+		if (env->insn_aux_data[i].call_with_percpu_alloc_ptr) {
 			/* patch with 'r1 = *(u64 *)(r1 + 0)' since for percpu data,
 			 * bpf_mem_alloc() returns a ptr to the percpu data ptr.
 			 */
@@ -2258,13 +2509,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			insn_buf[1] = *insn;
 			cnt = 2;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta += cnt - 1;
-			env->prog = prog = new_prog;
-			insn = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto patch_call_imm;
 		}
 
@@ -2282,7 +2529,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 		     insn->imm == BPF_FUNC_redirect_map    ||
 		     insn->imm == BPF_FUNC_for_each_map_elem ||
 		     insn->imm == BPF_FUNC_map_lookup_percpu_elem)) {
-			aux = &env->insn_aux_data[i + delta];
+			aux = &env->insn_aux_data[i];
 			if (bpf_map_ptr_poisoned(aux))
 				goto patch_call_imm;
 
@@ -2301,14 +2548,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 				if (bpf_map_is_percpu_map(map_ptr->map_type))
 					prog->jit_required = true;
 
-				new_prog = bpf_patch_insn_data(env, i + delta,
-							       insn_buf, cnt);
-				if (!new_prog)
-					return -ENOMEM;
-
-				delta    += cnt - 1;
-				env->prog = prog = new_prog;
-				insn      = new_prog->insnsi + i + delta;
+				insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+				if (IS_ERR(insn))
+					return PTR_ERR(insn);
 				goto next_insn;
 			}
 
@@ -2384,14 +2626,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 						  BPF_REG_0, 0);
 			cnt = 3;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
-						       cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2414,13 +2651,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			insn_buf[0] = BPF_ALU32_REG(BPF_XOR, BPF_REG_0, BPF_REG_0);
 			cnt = 1;
 #endif
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2433,13 +2666,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			insn_buf[2] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0);
 			cnt = 3;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 #endif
@@ -2467,13 +2696,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			insn_buf[cnt++] = BPF_JMP_A(1);
 			insn_buf[cnt++] = BPF_MOV64_IMM(BPF_REG_0, -EINVAL);
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2499,13 +2724,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 				cnt = 1;
 			}
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2525,13 +2746,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 				cnt = 2;
 			}
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2541,12 +2758,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			/* Load IP address from ctx - 16 */
 			insn_buf[0] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, -16);
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, 1);
-			if (!new_prog)
-				return -ENOMEM;
-
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, 1);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2596,13 +2810,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			insn_buf[10] = BPF_MOV64_IMM(BPF_REG_0, -ENOENT);
 			cnt = 11;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 
@@ -2614,13 +2824,9 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			insn_buf[1] = BPF_ATOMIC_OP(BPF_DW, BPF_XCHG, BPF_REG_1, BPF_REG_0, 0);
 			cnt = 2;
 
-			new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
-			if (!new_prog)
-				return -ENOMEM;
-
-			delta    += cnt - 1;
-			env->prog = prog = new_prog;
-			insn      = new_prog->insnsi + i + delta;
+			insn = bpf_patch_list_add(env, i, insn_buf, cnt);
+			if (IS_ERR(insn))
+				return PTR_ERR(insn);
 			goto next_insn;
 		}
 patch_call_imm:
@@ -2636,7 +2842,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 		}
 		insn->imm = BPF_CALL_IMM(fn->func);
 next_insn:
-		if (subprogs[cur_subprog + 1].start == i + delta + 1) {
+		if (subprogs[cur_subprog + 1].start == i + 1) {
 			subprogs[cur_subprog].stack_depth += stack_depth_extra;
 			subprogs[cur_subprog].stack_extra = stack_depth_extra;
 
@@ -2651,9 +2857,14 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 			stack_depth_extra = 0;
 		}
 		i++;
-		insn++;
+		insn = &prog->insnsi[i];
 	}
 
+	ret = bpf_patch_list_commit(env);
+	if (ret)
+		return ret;
+	prog = env->prog;
+
 	env->prog->aux->stack_depth = subprogs[0].stack_depth;
 	for (i = 0; i < env->subprog_cnt; i++) {
 		int delta = bpf_jit_supports_timed_may_goto() ? 2 : 1;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index b840b3eb9b229..7c1bed0c66c64 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -22753,6 +22753,7 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
 err_free_env:
 	bpf_free_subprog_jts(env);
 	vfree(env->insn_aux_data);
+	bpf_patch_list_free(env);
 	kvfree(env->fd_array);
 	bpf_stack_liveness_free(env);
 	kvfree(env->cfg.insn_postorder);
diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index 8a6d341b754ac..af107b0c1595e 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -90,6 +90,7 @@
 #include "verifier_netfilter_retcode.skel.h"
 #include "verifier_bpf_fastcall.skel.h"
 #include "verifier_or_jmp32_k.skel.h"
+#include "verifier_patch_list.skel.h"
 #include "verifier_percpu_addr.skel.h"
 #include "verifier_precision.skel.h"
 #include "verifier_prevent_map_lookup.skel.h"
@@ -273,6 +274,7 @@ void test_verifier_netfilter_ctx(void)        { RUN(verifier_netfilter_ctx); }
 void test_verifier_netfilter_retcode(void)    { RUN(verifier_netfilter_retcode); }
 void test_verifier_bpf_fastcall(void)         { RUN(verifier_bpf_fastcall); }
 void test_verifier_or_jmp32_k(void)           { RUN(verifier_or_jmp32_k); }
+void test_verifier_patch_list(void)           { RUN(verifier_patch_list); }
 void test_verifier_percpu_addr(void)          { RUN(verifier_percpu_addr); }
 void test_verifier_precision(void)            { RUN(verifier_precision); }
 void test_verifier_prevent_map_lookup(void)   { RUN(verifier_prevent_map_lookup); }
diff --git a/tools/testing/selftests/bpf/progs/verifier_patch_list.c b/tools/testing/selftests/bpf/progs/verifier_patch_list.c
new file mode 100644
index 0000000000000..268cebd179984
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_patch_list.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include "../../../include/linux/filter.h"
+#include "bpf_misc.h"
+
+/*
+ * bpf_do_misc_fixups() guards each division by a register against division
+ * by zero with a 4 insn patch. Jumps to a patched insn must land on the
+ * first insn of its patch, other jumps must follow the insns they target.
+ */
+
+SEC("raw_tp")
+__description("patch list: forward jumps to and over patched insns")
+__arch_x86_64
+__arch_arm64
+__success
+__xlated("0: call")
+__xlated("1: r1 = r0")
+__xlated("2: r0 = 7")
+__xlated("3: if r1 > 0x5 goto pc+1")
+__xlated("4: r0 = 9")
+__xlated("5: if r1 != 0x0 goto pc+2")
+__xlated("6: w0 ^= w0")
+__xlated("7: goto pc+1")
+__xlated("8: r0 /= r1")
+__xlated("9: if r0 > 0x3 goto pc+4")
+__xlated("10: if r1 != 0x0 goto pc+2")
+__xlated("11: w0 ^= w0")
+__xlated("12: goto pc+1")
+__xlated("13: r0 /= r1")
+__xlated("14: exit")
+__naked void patch_list_forward(void)
+{
+	asm volatile (
+	"call %[bpf_get_prandom_u32];"
+	"r1 = r0;"
+	"r0 = 7;"
+	"if r1 > 5 goto l0_%=;"
+	"r0 = 9;"
+"l0_%=:"
+	"r0 /= r1;"
+	"if r0 > 3 goto l1_%=;"
+	"r0 /= r1;"
+"l1_%=:"
+	"exit;"
+	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+SEC("raw_tp")
+__description("patch list: backward jump to a patched insn")
+__arch_x86_64
+__arch_arm64
+__success
+__xlated("0: call")
+__xlated("1: r1 = r0")
+__xlated("2: r2 = 0")
+__xlated("3: if r1 != 0x0 goto pc+2")
+__xlated("4: w0 ^= w0")
+__xlated("5: goto pc+1")
+__xlated("6: r0 /= r1")
+__xlated("7: r2 += 1")
+__xlated("8: if r2 < 0x3 goto pc-6")
+__xlated("9: exit")
+__naked void patch_list_backward(void)
+{
+	asm volatile (
+	"call %[bpf_get_prandom_u32];"
+	"r1 = r0;"
+	"r2 = 0;"
+"l0_%=:"
+	"r0 /= r1;"
+	"r2 += 1;"
+	"if r2 < 3 goto l0_%=;"
+	"exit;"
+	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+/* The jump of may_goto leaves its own patch and crosses another one. */
+SEC("raw_tp")
+__description("patch list: may_goto between patched insns")
+__arch_x86_64
+__success
+__xlated("0: *(u64 *)(r10 -16) = 65535")
+__xlated("1: *(u64 *)(r10 -8) = 0")
+__xlated("2: call")
+__xlated("3: r1 = r0")
+__xlated("4: if r1 != 0x0 goto pc+2")
+__xlated("5: w0 ^= w0")
+__xlated("6: goto pc+1")
+__xlated("7: r0 /= r1")
+__xlated("8: r12 = *(u64 *)(r10 -16)")
+__xlated("9: if r12 == 0x0 goto pc+9")
+__xlated("...")
+__xlated("15: if r1 != 0x0 goto pc+2")
+__xlated("16: w0 ^= w0")
+__xlated("17: goto pc+1")
+__xlated("18: r0 /= r1")
+__xlated("19: exit")
+__naked void patch_list_may_goto(void)
+{
+	asm volatile (
+	"call %[bpf_get_prandom_u32];"
+	"r1 = r0;"
+	"r0 /= r1;"
+	".8byte %[may_goto];"
+	"r0 /= r1;"
+	"exit;"
+	:
+	: __imm(bpf_get_prandom_u32),
+	  __imm_insn(may_goto, BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 1 /* offset */, 0))
+	: __clobber_all);
+}
+
+char _license[] SEC("license") = "GPL";

17/3 2026/10/02 05:49 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 16617
							output: 16
							thoughts: 1949
						

Thoughts:
**KMSAN Fuzzing Justification: Patch Series Evaluation**

Alright, let's dive into this patch series and see if it warrants a dedicated KMSAN fuzzing session, beyond our existing KASAN efforts. My focus is on identifying potential uninitialized memory exposures. The patch introduces `bpf_patch_list` for deferred instruction patching within the BPF verifier during `bpf_do_misc_fixups()`. Currently, the `bpf_do_misc_fixups` uses `bpf_patch_insn_data()` which reallocates and recomputes instructions repeatedly in O(N^2) or O(N*patches) time. This patch aims to optimize this process.

The new implementation involves queuing instruction replacements via `bpf_patch_list_add()`, which stores the replacement of instruction `off` with `patch[0..len)`. Later, `bpf_patch_list_commit()` applies all these queued patches at once, improving the complexity to O(prog->len + inserted insns). The patch also includes updates to related functions like `bpf_insn_array_remap()`, `patch_list_adj_insn()`, and `patch_list_adj_aux()`.

My primary concern is uninitialized memory. KMSAN's job is to catch reads of uninitialized memory (stack, heap, page allocations) and potential kernel-to-user memory leaks. I need to scrutinize this code for such risks.

First, let's examine `bpf_patch_list_add()`. It appears to be safely initializing the data structures: `p->off`, `p->len`, `p->start`, and `p->orig` are all assigned values before use. The `memcpy()` operation ensures that `pl->insns` is filled. Furthermore, `pl->cnt` and `pl->insn_cnt` serve to keep track of the valid data. So far, so good.

Now, on to `bpf_patch_list_commit()`. Here, a few things catch my eye. `new_off` is allocated with a size of `cnt + 1`, and the loop iterates from `0` to `<= cnt`. All `new_off[0..cnt]` elements appear to be initialized within the loop.

Next, a new instruction array, `insns`, is created. The code iterates through the original program and, for each instruction, either copies the original instruction or the replacement instruction from the patch list into `insns`. Crucially, I'm checking to make sure that *every* location in `insns` gets written to. After a careful inspection, it looks like every location `insns[0..new_cnt - 1]` is written to. In the case of no patch, `new_off[i + 1] = new_off[i] + 1`, and for each patch `new_off[i+1] = new_off[i] + len` ensuring the correct locations are covered.

Finally, `vrealloc()` is used to reallocate `env->insn_aux_data` with `GFP_KERNEL_ACCOUNT | __GFP_ZERO`. This is promising. I'll need to double check that `vrealloc` actually handles the `__GFP_ZERO` flag to zero out memory in case of a reallocation.

Based on this initial review, I see no immediate red flags in the provided snippets. The allocation and initialization patterns seem safe and well-managed, thus minimizing the risk of uninitialized memory reads. But I need to double-check that `vrealloc` does the zeroing, to be 100% sure.



18/3 2026/10/02 05:49 tool grepper
0m Args:
{
  "Expression": "vrealloc"
}

Results:
{
  "Output": "\nFull output is too long, showing 200 out of 321 lines.\nUse more precise expression if possible.\n\narch/powerpc/platforms/pseries/papr-rtas-common.c=47=static int papr_rtas_blob_extend(struct papr_rtas_blob *blob,\n--\narch/powerpc/platforms/pseries/papr-rtas-common.c-54-\narch/powerpc/platforms/pseries/papr-rtas-common.c:55:\tnew_ptr = kvrealloc(old_ptr, new_len, GFP_KERNEL_ACCOUNT);\narch/powerpc/platforms/pseries/papr-rtas-common.c-56-\tif (!new_ptr)\n--\narch/riscv/kernel/module-sections.c=113=int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,\n--\narch/riscv/kernel/module-sections.c-169-\t\t\tscratch_size = scratch_size_needed;\narch/riscv/kernel/module-sections.c:170:\t\t\tnew_scratch = kvrealloc(scratch, scratch_size, GFP_KERNEL);\narch/riscv/kernel/module-sections.c-171-\t\t\tif (!new_scratch) {\n--\narch/s390/mm/dump_pagetables.c=292=static int add_marker(unsigned long start, unsigned long end, const char *name)\n--\narch/s390/mm/dump_pagetables.c-297-\tnewsize = (markers_cnt + 2) * sizeof(*markers);\narch/s390/mm/dump_pagetables.c:298:\tnew = kvrealloc(markers, newsize, GFP_KERNEL);\narch/s390/mm/dump_pagetables.c-299-\tif (!new)\n--\ndrivers/gpu/drm/drm_exec.c=138=static int drm_exec_obj_locked(struct drm_exec *exec,\n--\ndrivers/gpu/drm/drm_exec.c-144-\ndrivers/gpu/drm/drm_exec.c:145:\t\ttmp = kvrealloc(exec-\u003eobjects, size + PAGE_SIZE, GFP_KERNEL);\ndrivers/gpu/drm/drm_exec.c-146-\t\tif (!tmp)\n--\nfs/ntfs/inode.c=3054=int ntfs_inode_add_attrlist(struct ntfs_inode *ni)\n--\nfs/ntfs/inode.c-3099-\nfs/ntfs/inode.c:3100:\t\taln = kvrealloc(al, al_len, GFP_NOFS);\nfs/ntfs/inode.c-3101-\t\tif (!aln) {\n--\nfs/ntfs/inode.c=3253=int ntfs_inode_close(struct ntfs_inode *ni)\n--\nfs/ntfs/inode.c-3289-\t\t\t/* Resize the memory buffer. */\nfs/ntfs/inode.c:3290:\t\t\ttmp_nis = kvrealloc(tmp_nis, base_ni-\u003enr_extents *\nfs/ntfs/inode.c-3291-\t\t\t\t\tsizeof(struct ntfs_inode *), GFP_NOFS);\n--\nfs/xfs/xfs_log_recover.c=2112=xlog_recover_add_to_cont_trans(\n--\nfs/xfs/xfs_log_recover.c-2146-\nfs/xfs/xfs_log_recover.c:2147:\tptr = kvrealloc(old_ptr, len + old_len, GFP_KERNEL);\nfs/xfs/xfs_log_recover.c-2148-\tif (!ptr)\n--\ninclude/linux/kasan.h=637=kasan_unpoison_vmap_areas(struct vm_struct **vms, int nr_vms,\n--\ninclude/linux/kasan.h-643-\ninclude/linux/kasan.h:644:void __kasan_vrealloc(const void *start, unsigned long old_size,\ninclude/linux/kasan.h-645-\t\tunsigned long new_size);\ninclude/linux/kasan.h-646-\ninclude/linux/kasan.h:647:static __always_inline void kasan_vrealloc(const void *start,\ninclude/linux/kasan.h-648-\t\t\t\t\tunsigned long old_size,\n--\ninclude/linux/kasan.h-651-\tif (kasan_enabled())\ninclude/linux/kasan.h:652:\t\t__kasan_vrealloc(start, old_size, new_size);\ninclude/linux/kasan.h-653-}\n--\ninclude/linux/kasan.h=680=kasan_unpoison_vmap_areas(struct vm_struct **vms, int nr_vms,\n--\ninclude/linux/kasan.h-683-\ninclude/linux/kasan.h:684:static inline void kasan_vrealloc(const void *start, unsigned long old_size,\ninclude/linux/kasan.h-685-\t\t\t\tunsigned long new_size) { }\n--\ninclude/linux/slab.h=1367=_kvmalloc_array_node_noprof(size_t n, size_t size, gfp_t flags, int node, kmalloc_token_t token)\n--\ninclude/linux/slab.h-1384-\ninclude/linux/slab.h:1385:void *kvrealloc_node_align_noprof(const void *p, DECL_TOKEN_PARAMS(size, token), unsigned long align,\ninclude/linux/slab.h-1386-\t\t\t\t  gfp_t flags, int nid) __realloc_size(2);\n--\ninclude/linux/slab.h-1388-/**\ninclude/linux/slab.h:1389: * kvrealloc_node_align - reallocate memory; contents remain unchanged\ninclude/linux/slab.h-1390- * @p: object to reallocate memory for\n--\ninclude/linux/slab.h-1395- *\ninclude/linux/slab.h:1396: * If @p is %NULL, kvrealloc() behaves exactly like kvmalloc(). If @size is 0\ninclude/linux/slab.h-1397- * and @p is not a %NULL pointer, the object pointed to is freed.\n--\ninclude/linux/slab.h-1414- */\ninclude/linux/slab.h:1415:void *kvrealloc_node_align(const void *p, size_t size, unsigned long align, gfp_t flags, int nid);\ninclude/linux/slab.h-1416-#endif\ninclude/linux/slab.h:1417:#define kvrealloc_node_align(p, size, align, flags, nid)\t\\\ninclude/linux/slab.h:1418:\talloc_hooks(kvrealloc_node_align_noprof(p, PASS_TOKEN_PARAMS(size, __kmalloc_token(size)), align, flags, nid))\ninclude/linux/slab.h:1419:#define kvrealloc_node(_p, _s, _f, _n)\t\tkvrealloc_node_align(_p, _s, 1, _f, _n)\ninclude/linux/slab.h:1420:#define kvrealloc(...)\t\t\t\tkvrealloc_node(__VA_ARGS__, NUMA_NO_NODE)\ninclude/linux/slab.h-1421-\n--\ninclude/linux/vmalloc.h=201=extern void *vcalloc_noprof(size_t n, size_t size) __alloc_size(1, 2);\n--\ninclude/linux/vmalloc.h-203-\ninclude/linux/vmalloc.h:204:void *__must_check vrealloc_node_align_noprof(const void *p, size_t size,\ninclude/linux/vmalloc.h-205-\t\tunsigned long align, gfp_t flags, int nid) __realloc_size(2);\ninclude/linux/vmalloc.h:206:#define vrealloc_node_noprof(_p, _s, _f, _nid)\t\\\ninclude/linux/vmalloc.h:207:\tvrealloc_node_align_noprof(_p, _s, 1, _f, _nid)\ninclude/linux/vmalloc.h:208:#define vrealloc_noprof(_p, _s, _f)\t\t\\\ninclude/linux/vmalloc.h:209:\tvrealloc_node_align_noprof(_p, _s, 1, _f, NUMA_NO_NODE)\ninclude/linux/vmalloc.h:210:#define vrealloc_node_align(...)\t\talloc_hooks(vrealloc_node_align_noprof(__VA_ARGS__))\ninclude/linux/vmalloc.h:211:#define vrealloc_node(...)\t\t\talloc_hooks(vrealloc_node_noprof(__VA_ARGS__))\ninclude/linux/vmalloc.h:212:#define vrealloc(...)\t\t\t\talloc_hooks(vrealloc_noprof(__VA_ARGS__))\ninclude/linux/vmalloc.h-213-\n--\nkernel/bpf/cfg.c=188=struct bpf_iarray *bpf_iarray_realloc(struct bpf_iarray *old, size_t n_elem)\n--\nkernel/bpf/cfg.c-192-\nkernel/bpf/cfg.c:193:\tnew = kvrealloc(old, new_size, GFP_KERNEL_ACCOUNT);\nkernel/bpf/cfg.c-194-\tif (!new) {\n--\nkernel/bpf/diagnostics.c=444=static void diag_append_history(struct bpf_verifier_env *env,\n--\nkernel/bpf/diagnostics.c-465-\tcap = min(log-\u003ecap ? log-\u003ecap * 2 : 64, max_events);\nkernel/bpf/diagnostics.c:466:\tevents = kvrealloc(log-\u003eevents, array_size(cap, sizeof(*events)), GFP_KERNEL_ACCOUNT);\nkernel/bpf/diagnostics.c-467-\tif (!events) {\n--\nkernel/bpf/fixups.c=360=static struct bpf_prog *__bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,\n--\nkernel/bpf/fixups.c-373-\tif (len \u003e 1) {\nkernel/bpf/fixups.c:374:\t\tnew_data = vrealloc(env-\u003einsn_aux_data,\nkernel/bpf/fixups.c-375-\t\t\t\t    array_size(env-\u003eprog-\u003elen + len - 1,\n--\nkernel/bpf/fixups.c=417=struct bpf_insn *bpf_patch_list_add(struct bpf_verifier_env *env, u32 off,\n--\nkernel/bpf/fixups.c-442-\t\tcap = max(pl-\u003ecap * 2, 16U);\nkernel/bpf/fixups.c:443:\t\tp = kvrealloc(pl-\u003epatches, array_size(cap, sizeof(*p)),\nkernel/bpf/fixups.c-444-\t\t\t      GFP_KERNEL_ACCOUNT);\n--\nkernel/bpf/fixups.c-452-\t\tcap = max3(pl-\u003einsn_cap * 2, pl-\u003einsn_cnt + len, 64U);\nkernel/bpf/fixups.c:453:\t\tinsns = kvrealloc(pl-\u003einsns, array_size(cap, sizeof(*insns)),\nkernel/bpf/fixups.c-454-\t\t\t\t  GFP_KERNEL_ACCOUNT);\n--\nkernel/bpf/fixups.c=586=int bpf_patch_list_commit(struct bpf_verifier_env *env)\n--\nkernel/bpf/fixups.c-643-\terr = -ENOMEM;\nkernel/bpf/fixups.c:644:\tdata = vrealloc(env-\u003einsn_aux_data,\nkernel/bpf/fixups.c-645-\t\t\tarray_size(new_cnt, sizeof(*data)),\n--\nkernel/bpf/states.c=116=static struct bpf_scc_visit *scc_visit_alloc(struct bpf_verifier_env *env,\n--\nkernel/bpf/states.c-127-\tnew_sz = sizeof(*info) + sizeof(struct bpf_scc_visit) * (num_visits + 1);\nkernel/bpf/states.c:128:\tinfo = kvrealloc(env-\u003escc_info[scc], new_sz, GFP_KERNEL_ACCOUNT);\nkernel/bpf/states.c-129-\tif (!info)\n--\nkernel/bpf/verifier.c=20446=static int add_func_ptr(struct bpf_verifier_env *env, struct bpf_map *map, u32 map_off,\n--\nkernel/bpf/verifier.c-20458-\tif (!(env-\u003efunc_ptr_cnt \u0026 (env-\u003efunc_ptr_cnt - 1))) {\nkernel/bpf/verifier.c:20459:\t\tptrs = kvrealloc(env-\u003efunc_ptrs,\nkernel/bpf/verifier.c-20460-\t\t\t\t array_size(max(2 * env-\u003efunc_ptr_cnt, 16U), sizeof(*ptrs)),\n--\nkernel/module/decompress.c=17=static int module_extend_max_pages(struct load_info *info, unsigned int extent)\n--\nkernel/module/decompress.c-21-\nkernel/module/decompress.c:22:\tnew_pages = kvrealloc(info-\u003epages,\nkernel/module/decompress.c-23-\t\t\t      size_mul(new_max, sizeof(*info-\u003epages)),\n--\nkernel/resource.c=496=int walk_system_ram_res_rev(u64 start, u64 end, void *arg,\n--\nkernel/resource.c-516-\nkernel/resource.c:517:\t\t\trams_new = kvrealloc(rams, (rams_size + 16) * sizeof(struct resource),\nkernel/resource.c-518-\t\t\t\t\t     GFP_KERNEL);\n--\nlib/test_vmalloc.c=44=__param(int, run_test_mask, 7,\n--\nlib/test_vmalloc.c-57-\t\t\"\\t\\tid: 2048, name: no_block_alloc_test\\n\"\nlib/test_vmalloc.c:58:\t\t\"\\t\\tid: 4096, name: vrealloc_test\\n\"\nlib/test_vmalloc.c-59-\t\t/* Add a new test case description here. */\n--\nlib/test_vmalloc.c=391=vm_map_ram_test(void)\n--\nlib/test_vmalloc.c-424-\nlib/test_vmalloc.c:425:static int vrealloc_test(void)\nlib/test_vmalloc.c-426-{\n--\nlib/test_vmalloc.c-432-\nlib/test_vmalloc.c:433:\t\tptr = vrealloc(NULL, PAGE_SIZE, GFP_KERNEL);\nlib/test_vmalloc.c-434-\t\tif (!ptr)\n--\nlib/test_vmalloc.c-439-\t\t/* Grow: beyond allocated pages, triggers full realloc. */\nlib/test_vmalloc.c:440:\t\ttmp = vrealloc(ptr, 4 * PAGE_SIZE, GFP_KERNEL);\nlib/test_vmalloc.c-441-\t\tif (!tmp)\n--\nlib/test_vmalloc.c-448-\t\t/* Shrink: crosses page boundary, frees tail pages. */\nlib/test_vmalloc.c:449:\t\ttmp = vrealloc(ptr, PAGE_SIZE, GFP_KERNEL);\nlib/test_vmalloc.c-450-\t\tif (!tmp)\n--\nlib/test_vmalloc.c-457-\t\t/* Shrink: within same page, no page freeing. */\nlib/test_vmalloc.c:458:\t\ttmp = vrealloc(ptr, PAGE_SIZE / 2, GFP_KERNEL);\nlib/test_vmalloc.c-459-\t\tif (!tmp)\n--\nlib/test_vmalloc.c-466-\t\t/* Grow: within allocated page, in-place, no realloc. */\nlib/test_vmalloc.c:467:\t\ttmp = vrealloc(ptr, PAGE_SIZE, GFP_KERNEL);\nlib/test_vmalloc.c-468-\t\tif (!tmp)\n--\nlib/test_vmalloc.c=491=static struct test_case_desc test_case_array[] = {\n--\nlib/test_vmalloc.c-503-\t{ \"no_block_alloc_test\", no_block_alloc_test, true },\nlib/test_vmalloc.c:504:\t{ \"vrealloc_test\", vrealloc_test, },\nlib/test_vmalloc.c-505-\t/* Add a new test case here. */\n--\nlib/tests/fortify_kunit.c=289=DEFINE_ALLOC_SIZE_TEST_PAIR(vmalloc)\n--\nlib/tests/fortify_kunit.c-325-\tchecker(((expected_pages) * PAGE_SIZE) * 2,\t\t\t\\\nlib/tests/fortify_kunit.c:326:\t\tkvrealloc(orig, ((alloc_pages) * PAGE_SIZE) * 2, gfp),\t\\\nlib/tests/fortify_kunit.c-327-\t\tkvfree(p));\t\t\t\t\t\t\\\n--\n\n"
}

19/3 2026/10/02 05:49 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 23834
							output: 37
							thoughts: 49
						

Thoughts:
Okay, let's dive into this.

**Examining BPF Instruction Data Patching with `vrealloc`**

Right, let's take a look at how the `__bpf_patch_insn_data` function utilizes `vrealloc`. I need to get a clear picture of its memory management strategy. My focus is on understanding potential subtleties in how the instruction data is reallocated during patching operations. Time to check the source, specifically, I'll be looking in `kernel/bpf/fixups.c` and zero in on roughly lines 360 through 400. That section should give me the details I need to evaluate the `vrealloc` usage in this context. I'm keen to ensure no memory leaks or unexpected allocation behavior arises.



20/3 2026/10/02 05:49 tool read-file
0m Args:
{
  "File": "kernel/bpf/fixups.c",
  "FirstLine": 360,
  "LineCount": 45
}

Results:
{
  "Contents": " 360:\tstatic struct bpf_prog *__bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,\n 361:\t\t\t\t\t\t      const struct bpf_insn *patch, u32 len,\n 362:\t\t\t\t\t\t      enum bpf_patch_mode mode)\n 363:\t{\n 364:\t\tstruct bpf_prog *new_prog;\n 365:\t\tstruct bpf_insn_aux_data *new_data = NULL;\n 366:\t\t/* The first slot whose targets move. */\n 367:\t\tu32 first = mode == BPF_PATCH_MOVE_TARGET ? off : off + 1;\n 368:\t\tstruct bpf_insn original_insn;\n 369:\t\n 370:\t\tif (bpf_rewrite_must_abort())\n 371:\t\t\treturn NULL;\n 372:\t\n 373:\t\tif (len \u003e 1) {\n 374:\t\t\tnew_data = vrealloc(env-\u003einsn_aux_data,\n 375:\t\t\t\t\t    array_size(env-\u003eprog-\u003elen + len - 1,\n 376:\t\t\t\t\t\t       sizeof(struct bpf_insn_aux_data)),\n 377:\t\t\t\t\t    GFP_KERNEL_ACCOUNT | __GFP_ZERO);\n 378:\t\t\tif (!new_data)\n 379:\t\t\t\treturn NULL;\n 380:\t\n 381:\t\t\tenv-\u003einsn_aux_data = new_data;\n 382:\t\t}\n 383:\t\n 384:\t\tmemcpy(\u0026original_insn, env-\u003eprog-\u003einsnsi + off, sizeof(struct bpf_insn));\n 385:\t\tnew_prog = bpf_patch_insn_single(env-\u003eprog, off, patch, len);\n 386:\t\tif (IS_ERR(new_prog)) {\n 387:\t\t\tif (PTR_ERR(new_prog) == -ERANGE)\n 388:\t\t\t\tverbose(env,\n 389:\t\t\t\t\t\"insn %d cannot be patched due to 16-bit range\\n\",\n 390:\t\t\t\t\tenv-\u003einsn_aux_data[off].orig_idx);\n 391:\t\t\treturn NULL;\n 392:\t\t}\n 393:\t\tadjust_insn_aux_data(env, new_prog, off, len, \u0026original_insn, mode);\n 394:\t\tadjust_subprog_starts(env, off, len);\n 395:\t\tadjust_insn_arrays(env, first, len);\n 396:\t\tbpf_adjust_func_ptrs(env, off, len);\n 397:\t\tadjust_poke_descs(new_prog, first, len);\n 398:\t\treturn new_prog;\n 399:\t}\n 400:\t\n 401:\tstruct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,\n 402:\t\t\t\t\t     const struct bpf_insn *patch, u32 len)\n 403:\t{\n 404:\t\treturn __bpf_patch_insn_data(env, off, patch, len, BPF_PATCH_KEEP_TARGET);\n"
}

21/3 2026/10/02 05:49 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 24931
							output: 32
							thoughts: 97
						

Thoughts:
Here's my thought process, as it stands:

**Examining `vrealloc` in the Context of BPF Patching**

Okay, so `__bpf_patch_insn_data` is employing `vrealloc` with both `GFP_KERNEL_ACCOUNT` and `__GFP_ZERO` flags. That immediately raises some questions. I need to understand what `vrealloc` *actually* does when given `__GFP_ZERO`. My understanding of memory allocation is pretty solid, but the interaction of zeroing with a re-allocation function warrants a closer look. I suspect it might have implications for security or performance that I should be aware of.

Therefore, the next logical step is to dive into the implementation of `vrealloc_node_align_noprof` – the function in which `vrealloc`'s behavior under the cited flags is revealed. I'll need to locate that function's definition. Let's start with a quick `grep` in `mm/vmalloc.c` to pinpoint its exact location. From there, I'll be able to trace the call paths and understand the zeroing behavior.



22/3 2026/10/02 05:49 tool grepper
0m Args:
{
  "Expression": "vrealloc_node_align_noprof",
  "PathPrefix": "mm/"
}

Results:
{
  "Output": "mm/nommu.c=120=EXPORT_SYMBOL(__vmalloc_noprof);\nmm/nommu.c-121-\nmm/nommu.c:122:void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align,\nmm/nommu.c-123-\t\t\t\t gfp_t flags, int node)\n--\nmm/slub.c=7099=EXPORT_SYMBOL(kvfree_sensitive);\nmm/slub.c-7100-\nmm/slub.c:7101:void *kvrealloc_node_align_noprof(const void *p, DECL_TOKEN_PARAMS(size, token), unsigned long align,\nmm/slub.c-7102-\t\t\t\t  gfp_t flags, int nid)\n--\nmm/slub.c-7106-\tif (is_vmalloc_addr(p))\nmm/slub.c:7107:\t\treturn vrealloc_node_align_noprof(p, size, align, flags, nid);\nmm/slub.c-7108-\n--\nmm/slub.c-7127-}\nmm/slub.c:7128:EXPORT_SYMBOL(kvrealloc_node_align_noprof);\nmm/slub.c-7129-\n--\nmm/vmalloc.c=4302=EXPORT_SYMBOL(vzalloc_node_noprof);\n--\nmm/vmalloc.c-4336- */\nmm/vmalloc.c:4337:void *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align,\nmm/vmalloc.c-4338-\t\t\t\t gfp_t flags, int nid)\n--\nmm/vmalloc.c-4461-}\nmm/vmalloc.c:4462:EXPORT_SYMBOL(vrealloc_node_align_noprof);\nmm/vmalloc.c-4463-\n"
}

23/3 2026/10/02 05:49 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 25812
							output: 35
							thoughts: 27
						

Thoughts:
Okay, let's dive into this. Here's how I'd approach that snippet, as though it were my own thought process:

**Diving into `mm/vmalloc.c` and `vrealloc`'s Zeroing Behavior**

Right, let's take a look at how `vrealloc` handles zeroing. My first instinct, given what I know about `vmalloc`'s mechanics, is to zero on realloc, but I want to confirm my understanding. I'm going to jump into `mm/vmalloc.c` and specifically pinpoint line number ~4337. I'm focusing on that general area of the file because that's where I *expect* to find the core logic for the allocation/reallocation/deallocation routines within the `vmalloc` system. Looking specifically for where the code addresses the memory operations as it expands or shrinks an allocated region seems a reasonable starting point. I suspect zeroing happens during extension, but I want to verify the exact mechanism and understand the conditionals involved, and especially whether zeroing occurs even in the case of a shrink operation (less likely, but good to confirm). Line 4337 is a logical place to start based on my memory of that code. I anticipate that this will be the heart of the realloc process and reveal if and when zeroing occurs.



24/3 2026/10/02 05:49 tool read-file
0m Args:
{
  "File": "mm/vmalloc.c",
  "FirstLine": 4337,
  "LineCount": 80
}

Results:
{
  "Contents": "4337:\tvoid *vrealloc_node_align_noprof(const void *p, size_t size, unsigned long align,\n4338:\t\t\t\t\t gfp_t flags, int nid)\n4339:\t{\n4340:\t\tstruct vm_struct *vm = NULL;\n4341:\t\tsize_t alloced_size = 0;\n4342:\t\tsize_t old_size = 0;\n4343:\t\tvoid *n;\n4344:\t\n4345:\t\tif (!size) {\n4346:\t\t\tvfree(p);\n4347:\t\t\treturn NULL;\n4348:\t\t}\n4349:\t\n4350:\t\tif (p) {\n4351:\t\t\tvm = find_vm_area(p);\n4352:\t\t\tif (unlikely(!vm)) {\n4353:\t\t\t\tWARN(1, \"Trying to vrealloc() nonexistent vm area (%p)\\n\", p);\n4354:\t\t\t\treturn NULL;\n4355:\t\t\t}\n4356:\t\n4357:\t\t\talloced_size = get_vm_area_size(vm);\n4358:\t\t\told_size = vm-\u003erequested_size;\n4359:\t\t\tif (WARN(alloced_size \u003c old_size,\n4360:\t\t\t\t \"vrealloc() has mismatched area vs requested sizes (%p)\\n\", p))\n4361:\t\t\t\treturn NULL;\n4362:\t\t\tif (WARN(!IS_ALIGNED((unsigned long)p, align),\n4363:\t\t\t\t \"will not reallocate with a bigger alignment (0x%lx)\\n\", align))\n4364:\t\t\t\treturn NULL;\n4365:\t\t\tif (unlikely(flags \u0026 __GFP_THISNODE) \u0026\u0026 nid != NUMA_NO_NODE \u0026\u0026\n4366:\t\t\t\t     nid != page_to_nid(vmalloc_to_page(p)))\n4367:\t\t\t\tgoto need_realloc;\n4368:\t\t} else {\n4369:\t\t\t/*\n4370:\t\t\t * If p is NULL, vrealloc behaves exactly like vmalloc.\n4371:\t\t\t * Skip the shrink and in-place grow paths.\n4372:\t\t\t */\n4373:\t\t\tgoto need_realloc;\n4374:\t\t}\n4375:\t\n4376:\t\tif (size \u003c= old_size) {\n4377:\t\t\tunsigned long new_nr_pages = PAGE_ALIGN(size) \u003e\u003e PAGE_SHIFT;\n4378:\t\n4379:\t\t\t/* Zero out \"freed\" memory, potentially for future realloc. */\n4380:\t\t\tif (want_init_on_free() || want_init_on_alloc(flags))\n4381:\t\t\t\tmemset((void *)p + size, 0, old_size - size);\n4382:\t\n4383:\t\t\t/*\n4384:\t\t\t * Free tail pages when shrink crosses a page boundary.\n4385:\t\t\t *\n4386:\t\t\t * Skip huge page allocations (page_order \u003e 0) as partial\n4387:\t\t\t * freeing would require splitting.\n4388:\t\t\t *\n4389:\t\t\t * Skip VM_FLUSH_RESET_PERMS, as direct-map permissions must\n4390:\t\t\t * be reset before pages are returned to the allocator.\n4391:\t\t\t *\n4392:\t\t\t * Skip VM_USERMAP, as remap_vmalloc_range_partial() validates\n4393:\t\t\t * mapping requests against the unchanged vm-\u003esize; freeing\n4394:\t\t\t * tail pages would cause vmalloc_to_page() to return NULL for\n4395:\t\t\t * the unmapped range.\n4396:\t\t\t *\n4397:\t\t\t * Skip if either GFP_NOFS or GFP_NOIO are used.\n4398:\t\t\t * kmemleak_free_part() internally allocates with\n4399:\t\t\t * GFP_KERNEL, which could trigger a recursive deadlock\n4400:\t\t\t * if we are under filesystem or I/O reclaim.\n4401:\t\t\t */\n4402:\t\t\tif (new_nr_pages \u003c vm-\u003enr_pages \u0026\u0026 !vm_area_page_order(vm) \u0026\u0026\n4403:\t\t\t    !(vm-\u003eflags \u0026 (VM_FLUSH_RESET_PERMS | VM_USERMAP)) \u0026\u0026\n4404:\t\t\t    gfp_has_io_fs(flags)) {\n4405:\t\t\t\tunsigned long addr = (unsigned long)kasan_reset_tag(p);\n4406:\t\t\t\tunsigned long old_nr_pages = vm-\u003enr_pages;\n4407:\t\n4408:\t\t\t\t/*\n4409:\t\t\t\t * Use the node lock to synchronize with concurrent\n4410:\t\t\t\t * readers (vmalloc_info_show).\n4411:\t\t\t\t */\n4412:\t\t\t\tstruct vmap_node *vn = addr_to_node(addr);\n4413:\t\n4414:\t\t\t\tspin_lock(\u0026vn-\u003ebusy.lock);\n4415:\t\t\t\tvm-\u003enr_pages = new_nr_pages;\n4416:\t\t\t\tspin_unlock(\u0026vn-\u003ebusy.lock);\n"
}

25/3 2026/10/02 05:49 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 27458
							output: 35
							thoughts: 22
						

Thoughts:
Okay, here's my interpretation of what's going on, thinking as if I'm immersed in this specific code context:

**Diving into mm/vmalloc.c - Line 4417 and Beyond**

Alright, I'm heading further into `mm/vmalloc.c`. My focus is zeroing in on the code around line 4417. Let's see what interesting tidbits and subtleties are hidden there. I know from experience that this area, especially surrounding vmalloc, can get pretty intricate with memory allocation details. I'm expecting to find allocation strategies, error handling, and perhaps some interesting optimizations relating to the virtual memory management within the kernel. I'm hoping to discover the deeper logic behind it and understand it fully.



26/3 2026/10/02 05:49 tool read-file
0m Args:
{
  "File": "mm/vmalloc.c",
  "FirstLine": 4417,
  "LineCount": 50
}

Results:
{
  "Contents": "4417:\t\n4418:\t\t\t\t/* Notify kmemleak of the reduced allocation size before unmapping. */\n4419:\t\t\t\tkmemleak_free_part((void *)addr +\n4420:\t\t\t\t\t\t   (new_nr_pages \u003c\u003c PAGE_SHIFT),\n4421:\t\t\t\t\t\t   (old_nr_pages - new_nr_pages)\n4422:\t\t\t\t\t\t\t\u003c\u003c PAGE_SHIFT);\n4423:\t\n4424:\t\t\t\tvunmap_range(addr + (new_nr_pages \u003c\u003c PAGE_SHIFT),\n4425:\t\t\t\t\t     addr + (old_nr_pages \u003c\u003c PAGE_SHIFT));\n4426:\t\n4427:\t\t\t\tvm_area_free_pages(vm, new_nr_pages, old_nr_pages);\n4428:\t\t\t}\n4429:\t\t\tvm-\u003erequested_size = size;\n4430:\t\t\tkasan_vrealloc(p, old_size, size);\n4431:\t\t\treturn (void *)p;\n4432:\t\t}\n4433:\t\n4434:\t\t/*\n4435:\t\t * We already have the bytes available in the allocation; use them.\n4436:\t\t */\n4437:\t\tif (size \u003c= vm-\u003enr_pages \u003c\u003c PAGE_SHIFT) {\n4438:\t\t\t/*\n4439:\t\t\t * No need to zero memory here, as unused memory will have\n4440:\t\t\t * already been zeroed at initial allocation time or during\n4441:\t\t\t * realloc shrink time.\n4442:\t\t\t */\n4443:\t\t\tvm-\u003erequested_size = size;\n4444:\t\t\tkasan_vrealloc(p, old_size, size);\n4445:\t\t\treturn (void *)p;\n4446:\t\t}\n4447:\t\n4448:\tneed_realloc:\n4449:\t\t/* TODO: Grow the vm_area, i.e. allocate and map additional pages. */\n4450:\t\tn = __vmalloc_node_noprof(size, align, flags, nid, __builtin_return_address(0));\n4451:\t\n4452:\t\tif (!n)\n4453:\t\t\treturn NULL;\n4454:\t\n4455:\t\tif (p) {\n4456:\t\t\tmemcpy(n, p, min(size, old_size));\n4457:\t\t\tvfree(p);\n4458:\t\t}\n4459:\t\n4460:\t\treturn n;\n4461:\t}\n4462:\tEXPORT_SYMBOL(vrealloc_node_align_noprof);\n4463:\t\n4464:\t#if defined(CONFIG_64BIT) \u0026\u0026 defined(CONFIG_ZONE_DMA32)\n4465:\t#define GFP_VMALLOC32 (GFP_DMA32 | GFP_KERNEL)\n4466:\t#elif defined(CONFIG_64BIT) \u0026\u0026 defined(CONFIG_ZONE_DMA)\n"
}

27/3 2026/10/02 05:49 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 28439
							output: 37
							thoughts: 596
						

Thoughts:
**My Analysis of Memory Allocation and BPF Instruction Patching**

Okay, so `vrealloc` is leveraging `__vmalloc_node_noprof`, and crucially, the `flags` passed to it include `__GFP_ZERO`. This means that when we're reallocating memory, we're guaranteed to get zeroed pages. That's a fundamental aspect to consider, especially in the context of security and potentially predictable data.

Now, let's dive into this `patch_list_adj_aux` function. I'm seeing it's handling the adjustment of auxiliary data associated with BPF instructions when we are patching them. The goal, as I understand it, is to meticulously update the aux data structure (`struct bpf_insn_aux_data`) to reflect the changes introduced by the patch.

Here's how I am going through it:

1.  **Initialization**: We grab a pointer to the last element of the auxiliary data (`last`). We then make a copy from the old data (`*old`) to this `last` element.
2.  **Zero-Extension Check**: The `zext_dst` field within the `last` structure is assigned based on the result of `bpf_insn_def32`. We are checking if the destination of the instruction performs a zero-extension or not.
3.  **Zeroing and Iteration**: We then zero out the remaining elements of the `slot` array, except for the last element which we copied. The logic iterates through the instructions in the patch ( `p->len - 1`).
4.  **Comparing Instructions**: Inside the loop, it checks if `patch[i]` is identical to the original instruction (`orig`). If they match, then `slot[i].non_stack_access` is derived from `last.non_stack_access` and `last.non_stack_access` is set to false. If `patch[i]` is a memory instruction, then `slot[i].non_stack_access` is set to true.
5.  **Handling the Last Instruction**: After the loop, the code checks if the last instruction in the patch (`patch[p->len - 1]`) is a memory instruction and is different from the original instruction. If so, `last->non_stack_access` is set to true.
6.  **Indirect Jumps**: If there are indirect jumps, we set the first slot's `indirect_target` flag to 1, and the last slot's flag to 0.

Essentially, this function is meticulously updating the aux data to ensure that subsequent analysis and execution of the BPF program correctly interprets the patched instructions. The `non_stack_access` flag is particularly interesting, indicating whether memory access is happening outside the stack which often has security implications. And the `indirect_target` flag is handling cases where the instructions are indirect jump targets.

I remember now that I need to look at how `adjust_insn_aux_data` in `fixups.c` did this previously for context. Let me check that.



28/3 2026/10/02 05:49 tool grepper
0m Args:
{
  "Expression": "adjust_insn_aux_data",
  "PathPrefix": "kernel/bpf/fixups.c"
}

Results:
{
  "Output": "kernel/bpf/fixups.c=217=enum bpf_patch_mode {\n--\nkernel/bpf/fixups.c-225- */\nkernel/bpf/fixups.c:226:static void adjust_insn_aux_data(struct bpf_verifier_env *env,\nkernel/bpf/fixups.c-227-\t\t\t\t struct bpf_prog *new_prog, u32 off, u32 cnt,\n--\nkernel/bpf/fixups.c=360=static struct bpf_prog *__bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,\n--\nkernel/bpf/fixups.c-392-\t}\nkernel/bpf/fixups.c:393:\tadjust_insn_aux_data(env, new_prog, off, len, \u0026original_insn, mode);\nkernel/bpf/fixups.c-394-\tadjust_subprog_starts(env, off, len);\n--\nkernel/bpf/fixups.c=417=struct bpf_insn *bpf_patch_list_add(struct bpf_verifier_env *env, u32 off,\n--\nkernel/bpf/fixups.c-435-\t\tenv-\u003eprog-\u003einsnsi[off] = *patch;\nkernel/bpf/fixups.c:436:\t\tadjust_insn_aux_data(env, env-\u003eprog, off, 1, NULL,\nkernel/bpf/fixups.c-437-\t\t\t\t     BPF_PATCH_KEEP_TARGET);\n--\nkernel/bpf/fixups.c=505=static int patch_list_adj_insn(struct bpf_insn *insn, const u32 *new_off,\n--\nkernel/bpf/fixups.c-538- * Fill the aux data of the 'p-\u003elen' insns at 'slot' from the aux data 'old'\nkernel/bpf/fixups.c:539: * of the insn they replace, the same way as adjust_insn_aux_data() does.\nkernel/bpf/fixups.c-540- */\n"
}

29/3 2026/10/02 05:49 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 30286
							output: 37
							thoughts: 26
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience, and written as if they were my own:

**Diving into `adjust_insn_aux_data` in `kernel/bpf/fixups.c`**

Alright, time to crack open `adjust_insn_aux_data`. I'm specifically interested in this function within the BPF fixups code. I suspect it's responsible for the intricate dance of handling auxiliary data modifications during BPF instruction transformations, likely related to things like relocations, argument passing, and maybe even some form of data alignment. I'm hoping to understand exactly *how* it's managing the relationships between the original BPF instruction and its associated aux data – what are the specific data structures it's manipulating, and what sort of context does it need to successfully execute? I'm anticipating some pointer arithmetic and potentially complex bitwise operations, given the low-level nature of this task. I need to see how the fixup process accurately represents what is going on at the instruction level. I'm keen to get a feel for the control flow; is it a simple lookup or something more complex and data-driven? The details here should paint a clear picture of how BPF enforces correctness during code modification, particularly as applied to the kernel.



30/3 2026/10/02 05:49 tool read-file
0m Args:
{
  "File": "kernel/bpf/fixups.c",
  "FirstLine": 225,
  "LineCount": 70
}

Results:
{
  "Contents": " 225:\t */\n 226:\tstatic void adjust_insn_aux_data(struct bpf_verifier_env *env,\n 227:\t\t\t\t\t struct bpf_prog *new_prog, u32 off, u32 cnt,\n 228:\t\t\t\t\t struct bpf_insn *original_insn,\n 229:\t\t\t\t\t enum bpf_patch_mode mode)\n 230:\t{\n 231:\t\tstruct bpf_insn_aux_data *data = env-\u003einsn_aux_data;\n 232:\t\tstruct bpf_insn *insn = new_prog-\u003einsnsi;\n 233:\t\tu32 old_seen = data[off].seen;\n 234:\t\tu32 prog_len;\n 235:\t\tint i;\n 236:\t\n 237:\t\t/* aux info at OFF always needs adjustment, no matter fast path\n 238:\t\t * (cnt == 1) is taken or not. There is no guarantee INSN at OFF is the\n 239:\t\t * original insn at old prog.\n 240:\t\t */\n 241:\t\tdata[off].zext_dst = bpf_insn_def32(new_prog, insn + off + cnt - 1) \u003e= 0;\n 242:\t\n 243:\t\tif (cnt == 1) {\n 244:\t\t\t/*\n 245:\t\t\t * A non-memory accessing insn could have been replaced by a\n 246:\t\t\t * memory accessing insn, systematically mark it for non-stack\n 247:\t\t\t * access\n 248:\t\t\t */\n 249:\t\t\tdata[off].non_stack_access = bpf_is_mem_insn(insn + off);\n 250:\t\t\treturn;\n 251:\t\t}\n 252:\t\tprog_len = new_prog-\u003elen;\n 253:\t\tenv-\u003einsn_aux_data_len = prog_len;\n 254:\t\n 255:\t\tmemmove(data + off + cnt - 1, data + off,\n 256:\t\t\tsizeof(struct bpf_insn_aux_data) * (prog_len - off - cnt + 1));\n 257:\t\tmemset(data + off, 0, sizeof(struct bpf_insn_aux_data) * (cnt - 1));\n 258:\t\tfor (i = off; i \u003c off + cnt - 1; i++) {\n 259:\t\t\t/* Expand insni[off]'s seen count to the patched range. */\n 260:\t\t\tdata[i].seen = old_seen;\n 261:\t\t\tdata[i].zext_dst = bpf_insn_def32(new_prog, insn + i) \u003e= 0;\n 262:\t\t\t/*\n 263:\t\t\t * In BPF_PATCH_MOVE_TARGET mode the original insn is the last\n 264:\t\t\t * slot by construction.\n 265:\t\t\t */\n 266:\t\t\tif (mode == BPF_PATCH_KEEP_TARGET \u0026\u0026\n 267:\t\t\t    !memcmp(insn + i, original_insn, sizeof(struct bpf_insn))) {\n 268:\t\t\t\tdata[i].non_stack_access =\n 269:\t\t\t\t\tdata[off + cnt - 1].non_stack_access;\n 270:\t\t\t\tdata[off + cnt - 1].non_stack_access = false;\n 271:\t\t\t} else if (bpf_is_mem_insn(insn + i)) {\n 272:\t\t\t\tdata[i].non_stack_access = true;\n 273:\t\t\t}\n 274:\t\t}\n 275:\t\n 276:\t\t/*\n 277:\t\t * Last slot instruction could be a newly generated\n 278:\t\t * BPF_ST/BPF_LDX/BPF_STX, systematically mark it for non-stack access\n 279:\t\t * if it is not the original instruction, otherwise keep the\n 280:\t\t * original marking\n 281:\t\t */\n 282:\t\tif (bpf_is_mem_insn(insn + off + cnt - 1) \u0026\u0026\n 283:\t\t    memcmp(insn + off + cnt - 1, original_insn, sizeof(struct bpf_insn)))\n 284:\t\t\tdata[off + cnt - 1].non_stack_access = true;\n 285:\t\n 286:\t\t/*\n 287:\t\t * The indirect_target flag of the original instruction was moved to the last of the\n 288:\t\t * new instructions by the above memmove and memset, but the indirect jump target is\n 289:\t\t * actually the first instruction, so move it back. This also matches with the behavior\n 290:\t\t * of bpf_insn_array_adjust(), which preserves xlated_off to point to the first new\n 291:\t\t * instruction. For BPF_PATCH_MOVE_TARGET the original instruction is the last one,\n 292:\t\t * so the flag already sits where needed.\n 293:\t\t */\n 294:\t\tif (mode == BPF_PATCH_KEEP_TARGET \u0026\u0026 data[off + cnt - 1].indirect_target) {\n"
}

31/3 2026/10/02 05:49 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 31967
							output: 21
							thoughts: 1915
						

Thoughts:
**Analyzing BPF Patch List Logic**

Okay, let's break this down. First, I see a pattern in `adjust_insn_aux_data` with data shifting and zeroing. Then, `patch_list_adj_aux` copies auxiliary data, sets a `zext_dst` flag, and zeroes out a portion of the structure. I need to be careful with the pointers involved here, so I'm paying close attention to the memory manipulation.

Now, in `bpf_patch_list_commit`, the core logic appears to be about applying a series of patches, working backward from the end. The `memmove` calls are shifting data around based on `new_off` calculated earlier. It’s moving auxiliary data, but the important thing is to understand the implications of the offsets. Let's see...

I'm thinking about a specific example. `cnt = 5`, with patches at indices 1 (length 3) and 3 (length 2). The loop's reverse order is crucial. In the last iteration, `p->off = 3` and `end = 5`. The `memmove` is moving `data[4]` to `data[new_off[4]]`. It seems that the original instruction at `data[3]` isn't touched, because it is passed as `old` to `patch_list_adj_aux` directly.

Wait, the potential for overlap between `new_off[p->off]` and `p->off` is important, especially when a patch might *shrink* a section, or grow it. It could overlap if all previous patches were after `p->off`, or if there was no growth before `p->off`.

Inside `patch_list_adj_aux`, the structure `last` is at `slot + p->len - 1`, and `slot` comes from `new_off[p->off]`. So `*last = *old` copies the data. Then comes `memset` that zero out `slot .. slot + p->len - 2`. It's safe since `*last = *old` copies the data *before* being zeroed out! And `new_off[p->off]` can equal `p->off`, but can't be smaller, so it's a valid copy regardless.

Considering `end`, in the first iteration, `end = cnt`, meaning the entire tail of the array is moved, freeing the slot used by the patch. In the next iteration, with `p_prev`, `end` is now `p->off`. So `memmove` is now moving the section `[p_prev->off + 1 .. p->off - 1]` to its new position, and again it is safe.

Then, there's the initial segment `[0 .. pl->patches[0].off]`. Because `new_off[i] == i` for this initial segment, this part of the data is already at its correct place in the `data` array.

So, it seems that everything is strictly accounted for. No gaps are created, and all moves and copies are valid.

Now, a very important point is: are any of these structures, including the patched instructions, exposed to user space? I need to know. The `bpf_patch_list` itself is internal, within `struct bpf_verifier_env`, which is only used within the kernel. The `patches` and `insns` are cleaned up. However, the BPF instructions themselves, `prog->insnsi`, *can* be copied to user space using `BPF_PROG_GET_INFO_BY_FD`. This is a very important detail. I need to review how the `xlated_prog_insns` is handled to make sure that the patches are correctly applied before exposing the instructions.



32/3 2026/10/02 05:50 tool grepper
0m Args:
{
  "Expression": "xlated_prog_insns"
}

Results:
{
  "Output": "include/uapi/linux/bpf.h=6812=struct bpf_prog_info {\n--\ninclude/uapi/linux/bpf.h-6818-\t__aligned_u64 jited_prog_insns;\ninclude/uapi/linux/bpf.h:6819:\t__aligned_u64 xlated_prog_insns;\ninclude/uapi/linux/bpf.h-6820-\t__u64 load_time;\t/* ns since boottime */\n--\nkernel/bpf/syscall.c=5116=static int bpf_prog_get_info_by_fd(struct file *file,\n--\nkernel/bpf/syscall.c-5200-\t\t\t\treturn -ENOMEM;\nkernel/bpf/syscall.c:5201:\t\t\tuinsns = u64_to_user_ptr(info.xlated_prog_insns);\nkernel/bpf/syscall.c-5202-\t\t\tulen = min_t(u32, info.xlated_prog_len, ulen);\n--\nkernel/bpf/syscall.c-5207-\t\t} else {\nkernel/bpf/syscall.c:5208:\t\t\tinfo.xlated_prog_insns = 0;\nkernel/bpf/syscall.c-5209-\t\t}\n--\ntools/bpf/bpftool/prog.c=90=static int prep_prog_info(struct bpf_prog_info *const info, enum dump_mode mode,\n--\ntools/bpf/bpftool/prog.c-136-\t} else {\ntools/bpf/bpftool/prog.c:137:\t\tholder.xlated_prog_insns = ptr_to_u64(ptr);\ntools/bpf/bpftool/prog.c-138-\t\tptr += holder.xlated_prog_len;\n--\ntools/bpf/bpftool/prog.c=702=prog_dump(struct bpf_prog_info *info, enum dump_mode mode,\n--\ntools/bpf/bpftool/prog.c-723-\t} else {\t/* DUMP_XLATED */\ntools/bpf/bpftool/prog.c:724:\t\tif (info-\u003exlated_prog_len == 0 || !info-\u003exlated_prog_insns) {\ntools/bpf/bpftool/prog.c-725-\t\t\tp_err(\"error retrieving insn dump: kernel.kptr_restrict set?\");\n--\ntools/bpf/bpftool/prog.c-727-\t\t}\ntools/bpf/bpftool/prog.c:728:\t\tbuf = u64_to_ptr(info-\u003exlated_prog_insns);\ntools/bpf/bpftool/prog.c-729-\t\tmember_len = info-\u003exlated_prog_len;\n--\ntools/include/uapi/linux/bpf.h=6812=struct bpf_prog_info {\n--\ntools/include/uapi/linux/bpf.h-6818-\t__aligned_u64 jited_prog_insns;\ntools/include/uapi/linux/bpf.h:6819:\t__aligned_u64 xlated_prog_insns;\ntools/include/uapi/linux/bpf.h-6820-\t__u64 load_time;\t/* ns since boottime */\n--\ntools/perf/util/bpf-utils.c=23=static const struct bpil_array_desc bpil_array_desc[] = {\n--\ntools/perf/util/bpf-utils.c-29-\t[PERF_BPIL_XLATED_INSNS] = {\ntools/perf/util/bpf-utils.c:30:\t\toffsetof(struct bpf_prog_info, xlated_prog_insns),\ntools/perf/util/bpf-utils.c-31-\t\toffsetof(struct bpf_prog_info, xlated_prog_len),\n--\ntools/testing/selftests/bpf/prog_tests/bpf_obj_id.c=6=void serial_test_bpf_obj_id(void)\n--\ntools/testing/selftests/bpf/prog_tests/bpf_obj_id.c-101-\t\tprog_infos[i].jited_prog_len = sizeof(jited_insns);\ntools/testing/selftests/bpf/prog_tests/bpf_obj_id.c:102:\t\tprog_infos[i].xlated_prog_insns = ptr_to_u64(xlated_insns);\ntools/testing/selftests/bpf/prog_tests/bpf_obj_id.c-103-\t\tprog_infos[i].xlated_prog_len = sizeof(xlated_insns);\n--\ntools/testing/selftests/bpf/prog_tests/bpf_obj_id.c-193-\t\tprog_infos[i].jited_prog_insns = 0;\ntools/testing/selftests/bpf/prog_tests/bpf_obj_id.c:194:\t\tprog_infos[i].xlated_prog_insns = 0;\ntools/testing/selftests/bpf/prog_tests/bpf_obj_id.c-195-\t\tcmp_res = memcmp(\u0026prog_info, \u0026prog_infos[i], info_len);\n--\ntools/testing/selftests/bpf/testing_helpers.c=460=int get_xlated_program(int fd_prog, struct bpf_insn **buf, __u32 *cnt)\n--\ntools/testing/selftests/bpf/testing_helpers.c-487-\tinfo.xlated_prog_len = xlated_prog_len;\ntools/testing/selftests/bpf/testing_helpers.c:488:\tinfo.xlated_prog_insns = (__u64)(unsigned long)*buf;\ntools/testing/selftests/bpf/testing_helpers.c-489-\tif (bpf_prog_get_info_by_fd(fd_prog, \u0026info, \u0026info_len)) {\n"
}

33/3 2026/10/02 05:50 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 36282
							output: 38
							thoughts: 1402
						

Thoughts:
**Ensuring Full Initialization of `prog->insnsi` in BPF Patching**

Okay, so I'm looking at this BPF code, specifically the `bpf_patch_list_commit` function. I need to make absolutely certain that every byte in `prog->insnsi` gets properly initialized after a patch operation. The reason I'm focusing on this is that I need to confirm that `copy_to_user` in `kernel/bpf/syscall.c` will not leak any uninitialized data when copying out the eBPF instructions.

First, I see that a new instruction array, `insns`, is allocated using `kvmalloc_array`. Then, a loop iterates through the original instructions (`prog->insnsi`) and applies patches if needed.  I want to make certain that every element in `insns` gets populated before the final `memcpy` to `prog->insnsi`.

The key is this `for` loop and the `new_off` array.  I need to verify that all of `insns` is getting written to during this phase. I see that `new_off` is constructed in a way that, for each original instruction `i`, the corresponding writes to `insns` cover the correct range:  `[new_off[i], new_off[i] + len)`.

I carefully track how `new_off` is calculated, considering both the patched and unpatched cases.  Essentially, the intervals defined by `new_off[i]` and `len` partition the new instruction array. Since `new_off[0]` is initialized to 0, this confirms that the entire `insns` buffer is written to.

Now, where does the data for these writes come from?  If it's not a patch, we're taking the values from the original `prog->insnsi` which we know are populated.  If it *is* a patch, the data comes from `pl->insns`, which itself is populated with a `memcpy` from the `patch` array in the `bpf_patch_list_add` function.  These are built using helpers like `BPF_MOV64_IMM` etc. within the fixup routines like `bpf_do_misc_fixups` and therefore complete `struct bpf_insn` values.

This means that `patch` is created by a set of routines that are responsible for emitting a fully-formed eBPF instruction. `patch_list_adj_insn` is then responsible for filling in the fields of those instructions, like `imm` and `off`. Therefore, I can be confident that `pl->insns` is also initialized.

Finally, `prog->insnsi` is overwritten with the contents of `insns`.  This `memcpy` is writing `sizeof(*insns) * new_cnt` which includes the entire array.

However, a quick check of the definition of `struct bpf_insn` is warranted to make certain that there's no padding within the structure that would lead to uninitialized memory. I need to verify its definition to ensure there are no surprises with respect to padding, which could lead to uninitialized memory being copied.



34/3 2026/10/02 05:50 tool grepper
0m Args:
{
  "Expression": "struct bpf_insn \\{",
  "PathPrefix": "include/uapi/linux/bpf.h"
}

Results:
{
  "Output": "include/uapi/linux/bpf.h=62=enum {\n--\ninclude/uapi/linux/bpf.h-79-\ninclude/uapi/linux/bpf.h:80:struct bpf_insn {\ninclude/uapi/linux/bpf.h-81-\t__u8\tcode;\t\t/* opcode */\n"
}

35/3 2026/10/02 05:50 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 38511
							output: 38
							thoughts: 12
						

Thoughts:
Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:

**Diving into the BPF Instruction Structure**

Alright, let's get down to brass tacks. I need to take a look at the `struct bpf_insn`. It's the bedrock, the fundamental building block of everything BPF. I know the basics, of course – opcode, source/destination registers, immediate values, offset – but I need to refresh my memory, confirm my assumptions, and potentially spot something I've missed. I need to understand it at a granular level, because I'm looking for either an inefficiency or to use it in an unconventional way. I need to understand what bits are where, how everything interacts, and how the various fields contribute to the overall instruction encoding. A deep dive is in order. It's time to refresh and scrutinize the foundation itself. There might be some juicy low hanging fruit there. Let's see what we can find.



36/3 2026/10/02 05:50 tool read-file
0m Args:
{
  "File": "include/uapi/linux/bpf.h",
  "FirstLine": 79,
  "LineCount": 20
}

Results:
{
  "Contents": "  79:\t\n  80:\tstruct bpf_insn {\n  81:\t\t__u8\tcode;\t\t/* opcode */\n  82:\t\t__u8\tdst_reg:4;\t/* dest register */\n  83:\t\t__u8\tsrc_reg:4;\t/* source register */\n  84:\t\t__s16\toff;\t\t/* signed offset */\n  85:\t\t__s32\timm;\t\t/* signed immediate constant */\n  86:\t};\n  87:\t\n  88:\t/* Deprecated: use struct bpf_lpm_trie_key_u8 (when the \"data\" member is needed for\n  89:\t * byte access) or struct bpf_lpm_trie_key_hdr (when using an alternative type for\n  90:\t * the trailing flexible array member) instead.\n  91:\t */\n  92:\tstruct bpf_lpm_trie_key {\n  93:\t\t__u32\tprefixlen;\t/* up to 32 for AF_INET, 128 for AF_INET6 */\n  94:\t\t__u8\tdata[0];\t/* Arbitrary size */\n  95:\t};\n  96:\t\n  97:\t/* Header for bpf_lpm_trie_key structs */\n  98:\tstruct bpf_lpm_trie_key_hdr {\n"
}

37/3 2026/10/02 05:50 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 39131
							output: 22
							thoughts: 264
						

Thoughts:
**My Assessment of the BPF Instruction Structure and Associated Data Structures**

Okay, let's break this down. First, the `bpf_insn` struct itself – code, destination register, source register, offset, immediate value. Eight bytes total. Simple enough, no padding. Good.

Now, I need to understand the ripple effects of this change. Specifically, I see that the `linfo` structure is involved: `prog->aux->linfo[i].insn_off = patch_list_remap(...)`. Right, `insn_off` is being updated.

Hold on, does `linfo` get exposed to userspace? That's the critical question. If it does, any change here needs careful consideration. Okay, let's check `bpf_prog_get_info_by_fd`. Yes, it's copied to user space if requested. And `insn_off` is just a `u32`. This `remap` operation updates the `linfo` in-place.

Now the alarm bells start ringing. I have to know *how* `linfo` was adjusted before this patch, or in other similar functions. I recall functions like `adjust_poke_descs`, `adjust_subprog_starts`, and `bpf_prog_realloc`. Time to dig deeper and see if any prior updates have bearing on this change! I'll need to analyze those areas of the code to make sure this `remap` operation won't cause any unexpected interactions or issues with the user-space interface.



38/3 2026/10/02 05:50 tool grepper
0m Args:
{
  "Expression": "linfo\\[.*insn_off"
}

Results:
{
  "Output": "include/linux/bpf.h=1808=struct bpf_prog_aux {\n--\ninclude/linux/bpf.h-1904-\t * one to one mapping to linfo:\ninclude/linux/bpf.h:1905:\t * jited_linfo[i] is the jited addr for the linfo[i]-\u003einsn_off.\ninclude/linux/bpf.h-1906-\t * Both the main and sub prog share the same jited_linfo.\n--\nkernel/bpf/check_btf.c=213=static int check_btf_line(struct bpf_verifier_env *env,\n--\nkernel/bpf/check_btf.c-275-\t\t *\nkernel/bpf/check_btf.c:276:\t\t * The linfo[0].insn_off == 0 check logically falls into\nkernel/bpf/check_btf.c-277-\t\t * the later \"missing bpf_line_info for func...\" case\nkernel/bpf/check_btf.c:278:\t\t * because the first linfo[0].insn_off must be the\nkernel/bpf/check_btf.c-279-\t\t * first sub also and the first sub must have\n--\nkernel/bpf/check_btf.c-281-\t\t */\nkernel/bpf/check_btf.c:282:\t\tif ((i \u0026\u0026 linfo[i].insn_off \u003c= prev_offset) ||\nkernel/bpf/check_btf.c:283:\t\t    linfo[i].insn_off \u003e= prog-\u003elen) {\nkernel/bpf/check_btf.c-284-\t\t\tverbose(env, \"Invalid line_info[%u].insn_off:%u (prev_offset:%u prog-\u003elen:%u)\\n\",\nkernel/bpf/check_btf.c:285:\t\t\t\ti, linfo[i].insn_off, prev_offset,\nkernel/bpf/check_btf.c-286-\t\t\t\tprog-\u003elen);\n--\nkernel/bpf/check_btf.c-290-\nkernel/bpf/check_btf.c:291:\t\tif (!prog-\u003einsnsi[linfo[i].insn_off].code) {\nkernel/bpf/check_btf.c-292-\t\t\tverbose(env,\n--\nkernel/bpf/check_btf.c-306-\t\tif (s != env-\u003esubprog_cnt) {\nkernel/bpf/check_btf.c:307:\t\t\tif (linfo[i].insn_off == sub[s].start) {\nkernel/bpf/check_btf.c-308-\t\t\t\tsub[s].linfo_idx = i;\nkernel/bpf/check_btf.c-309-\t\t\t\ts++;\nkernel/bpf/check_btf.c:310:\t\t\t} else if (sub[s].start \u003c linfo[i].insn_off) {\nkernel/bpf/check_btf.c-311-\t\t\t\tverbose(env, \"missing bpf_line_info for func#%u\\n\", s);\n--\nkernel/bpf/check_btf.c-316-\nkernel/bpf/check_btf.c:317:\t\tprev_offset = linfo[i].insn_off;\nkernel/bpf/check_btf.c-318-\t\tbpfptr_add(\u0026ulinfo, rec_size);\n--\nkernel/bpf/core.c=190=void bpf_prog_jit_attempt_done(struct bpf_prog *prog)\n--\nkernel/bpf/core.c-223- * jited_linfo[i] = prog-\u003ebpf_func +\nkernel/bpf/core.c:224: *\tinsn_to_jit_off[linfo[i].insn_off - insn_start - 1]\nkernel/bpf/core.c-225- */\nkernel/bpf/core.c=226=void bpf_prog_fill_jited_linfo(struct bpf_prog *prog,\n--\nkernel/bpf/core.c-238-\tlinfo = \u0026prog-\u003eaux-\u003elinfo[linfo_idx];\nkernel/bpf/core.c:239:\tinsn_start = linfo[0].insn_off;\nkernel/bpf/core.c-240-\tinsn_end = insn_start + prog-\u003elen;\n--\nkernel/bpf/core.c-246-\nkernel/bpf/core.c:247:\tfor (i = 1; i \u003c nr_linfo \u0026\u0026 linfo[i].insn_off \u003c insn_end; i++)\nkernel/bpf/core.c:248:\t\t/* The verifier ensures that linfo[i].insn_off is\nkernel/bpf/core.c-249-\t\t * strictly increasing\n--\nkernel/bpf/core.c-251-\t\tjited_linfo[i] = prog-\u003ebpf_func +\nkernel/bpf/core.c:252:\t\t\tinsn_to_jit_off[linfo[i].insn_off - insn_start - 1];\nkernel/bpf/core.c-253-}\n--\nkernel/bpf/core.c=436=static void bpf_adj_linfo(struct bpf_prog *prog, u32 off, u32 delta)\n--\nkernel/bpf/core.c-447-\tfor (i = 0; i \u003c nr_linfo; i++)\nkernel/bpf/core.c:448:\t\tif (off \u003c linfo[i].insn_off)\nkernel/bpf/core.c-449-\t\t\tbreak;\nkernel/bpf/core.c-450-\nkernel/bpf/core.c:451:\t/* Push all off \u003c linfo[i].insn_off by delta */\nkernel/bpf/core.c-452-\tfor (; i \u003c nr_linfo; i++)\nkernel/bpf/core.c:453:\t\tlinfo[i].insn_off += delta;\nkernel/bpf/core.c-454-}\n--\nkernel/bpf/core.c=3609=const struct bpf_line_info *bpf_find_linfo(const struct bpf_prog *prog, u32 insn_off)\n--\nkernel/bpf/core.c-3619-\tlinfo = prog-\u003eaux-\u003elinfo;\nkernel/bpf/core.c:3620:\t/* Loop invariant: linfo[l].insn_off \u003c= insns_off.\nkernel/bpf/core.c:3621:\t * linfo[0].insn_off == 0 which always satisfies above condition.\nkernel/bpf/core.c-3622-\t * Binary search is searching for rightmost linfo entry that satisfies\n--\nkernel/bpf/core.c-3629-\t\t/* (r - l + 1) / 2 means we break a tie to the right, so if:\nkernel/bpf/core.c:3630:\t\t * l=1, r=2, linfo[l].insn_off \u003c= insn_off, linfo[r].insn_off \u003e insn_off,\nkernel/bpf/core.c:3631:\t\t * then m=2, we see that linfo[m].insn_off \u003e insn_off, and so\nkernel/bpf/core.c-3632-\t\t * r becomes 1 and we exit the loop with correct l==1.\n--\nkernel/bpf/core.c-3636-\t\tm = l + (r - l + 1) / 2;\nkernel/bpf/core.c:3637:\t\tif (linfo[m].insn_off \u003c= insn_off)\nkernel/bpf/core.c-3638-\t\t\tl = m;\n--\nkernel/bpf/core.c=3646=int bpf_prog_get_file_line(struct bpf_prog *prog, unsigned long ip, const char **filep,\n--\nkernel/bpf/core.c-3666-\nkernel/bpf/core.c:3667:\tinsn_start = linfo[0].insn_off;\nkernel/bpf/core.c-3668-\tinsn_end = insn_start + len;\n--\nkernel/bpf/core.c-3671-\tfor (int i = 0; i \u003c nr_linfo \u0026\u0026\nkernel/bpf/core.c:3672:\t     linfo[i].insn_off \u003e= insn_start \u0026\u0026 linfo[i].insn_off \u003c insn_end; i++) {\nkernel/bpf/core.c-3673-\t\tif (jited_linfo[i] \u003e= (void *)ip)\n--\nkernel/bpf/fixups.c=586=int bpf_patch_list_commit(struct bpf_verifier_env *env)\n--\nkernel/bpf/fixups.c-676-\tfor (i = 0; i \u003c prog-\u003eaux-\u003enr_linfo; i++)\nkernel/bpf/fixups.c:677:\t\tprog-\u003eaux-\u003elinfo[i].insn_off =\nkernel/bpf/fixups.c:678:\t\t\tpatch_list_remap(new_off, cnt, grow, prog-\u003eaux-\u003elinfo[i].insn_off);\nkernel/bpf/fixups.c-679-\n--\nkernel/bpf/fixups.c=848=static int bpf_adj_linfo_after_remove(struct bpf_verifier_env *env, u32 off,\n--\nkernel/bpf/fixups.c-862-\tfor (i = 0; i \u003c nr_linfo; i++)\nkernel/bpf/fixups.c:863:\t\tif (linfo[i].insn_off \u003e= off)\nkernel/bpf/fixups.c-864-\t\t\tbreak;\n--\nkernel/bpf/fixups.c-868-\tfor (; i \u003c nr_linfo; i++)\nkernel/bpf/fixups.c:869:\t\tif (linfo[i].insn_off \u003c off + cnt)\nkernel/bpf/fixups.c-870-\t\t\tl_cnt++;\n--\nkernel/bpf/fixups.c-878-\tif (prog-\u003elen != off \u0026\u0026 l_cnt \u0026\u0026\nkernel/bpf/fixups.c:879:\t    (i == nr_linfo || linfo[i].insn_off != off + cnt)) {\nkernel/bpf/fixups.c-880-\t\tl_cnt--;\nkernel/bpf/fixups.c:881:\t\tlinfo[--i].insn_off = off + cnt;\nkernel/bpf/fixups.c-882-\t}\n--\nkernel/bpf/fixups.c-892-\nkernel/bpf/fixups.c:893:\t/* pull all linfo[i].insn_off \u003e= off + cnt in by cnt */\nkernel/bpf/fixups.c-894-\tfor (i = l_off; i \u003c nr_linfo; i++)\nkernel/bpf/fixups.c:895:\t\tlinfo[i].insn_off -= cnt;\nkernel/bpf/fixups.c-896-\n--\ntools/testing/selftests/bpf/prog_tests/btf.c=6696=static int test_get_linfo(const struct prog_info_raw_test *test,\n--\ntools/testing/selftests/bpf/prog_tests/btf.c-6822-\ntools/testing/selftests/bpf/prog_tests/btf.c:6823:\tCHECK(linfo[0].insn_off, \"linfo[0].insn_off:%u\",\ntools/testing/selftests/bpf/prog_tests/btf.c:6824:\t      linfo[0].insn_off);\ntools/testing/selftests/bpf/prog_tests/btf.c-6825-\tfor (i = 1; i \u003c cnt; i++) {\n--\ntools/testing/selftests/bpf/prog_tests/btf.c-6832-\t\t\t((i + dead_insns) * test-\u003eline_info_rec_size);\ntools/testing/selftests/bpf/prog_tests/btf.c:6833:\t\tif (CHECK(linfo[i].insn_off \u003c= linfo[i - 1].insn_off,\ntools/testing/selftests/bpf/prog_tests/btf.c:6834:\t\t\t  \"linfo[%u].insn_off:%u \u003c= linfo[%u].insn_off:%u\",\ntools/testing/selftests/bpf/prog_tests/btf.c:6835:\t\t\t  i, linfo[i].insn_off,\ntools/testing/selftests/bpf/prog_tests/btf.c:6836:\t\t\t  i - 1, linfo[i - 1].insn_off)) {\ntools/testing/selftests/bpf/prog_tests/btf.c-6837-\t\t\terr = -1;\n"
}

39/3 2026/10/02 05:50 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 43123
							output: 21
							thoughts: 37
						

Thoughts:
Okay, here's my take on that line of questioning:

**Unveiling Memory Initialization in `bpf_prog_realloc`**

Right, let's dive in. The question at hand is whether `bpf_prog_realloc` introduces any uninitialized memory. That's a critical point for security, performance, and general code robustness when dealing with BPF programs. The best way to address this is to go straight to the source and scrutinize the implementation of `bpf_prog_realloc`. That's where we'll find the definitive answer, and I'm ready to get down to it.



40/3 2026/10/02 05:50 tool grepper
0m Args:
{
  "Expression": "bpf_prog_realloc"
}

Results:
{
  "Output": "include/linux/filter.h=1207=struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flags);\ninclude/linux/filter.h:1208:struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,\ninclude/linux/filter.h-1209-\t\t\t\t  gfp_t gfp_extra_flags);\n--\nkernel/bpf/core.c=226=void bpf_prog_fill_jited_linfo(struct bpf_prog *prog,\n--\nkernel/bpf/core.c-254-\nkernel/bpf/core.c:255:struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,\nkernel/bpf/core.c-256-\t\t\t\t  gfp_t gfp_extra_flags)\n--\nkernel/bpf/core.c=456=struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,\n--\nkernel/bpf/core.c-484-\t */\nkernel/bpf/core.c:485:\tprog_adj = bpf_prog_realloc(prog, bpf_prog_size(insn_adj_cnt),\nkernel/bpf/core.c-486-\t\t\t\t    GFP_USER);\n--\nkernel/bpf/fixups.c=586=int bpf_patch_list_commit(struct bpf_verifier_env *env)\n--\nkernel/bpf/fixups.c-650-\nkernel/bpf/fixups.c:651:\tprog = bpf_prog_realloc(prog, bpf_prog_size(new_cnt), GFP_USER);\nkernel/bpf/fixups.c-652-\tif (!prog)\n--\nnet/core/filter.c=1266=static struct bpf_prog *bpf_migrate_filter(struct bpf_prog *fp)\n--\nnet/core/filter.c-1298-\told_fp = fp;\nnet/core/filter.c:1299:\tfp = bpf_prog_realloc(old_fp, bpf_prog_size(new_len), 0);\nnet/core/filter.c-1300-\tif (!fp) {\n--\ntools/testing/selftests/bpf/test_verifier.c=422=static void bpf_fill_big_prog_with_loop_1(struct bpf_test *self)\n--\ntools/testing/selftests/bpf/test_verifier.c-426-\t * error, which happened upon BPF program reallocation.\ntools/testing/selftests/bpf/test_verifier.c:427:\t * Reallocation is handled by core.c:bpf_prog_realloc, which\ntools/testing/selftests/bpf/test_verifier.c-428-\t * reuses old memory if page boundary is not crossed. The\n"
}

41/3 2026/10/02 05:50 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 44096
							output: 36
							thoughts: 24
						

Thoughts:
Okay, let's dive into this. I'm going to pull up `bpf_prog_realloc` within `kernel/bpf/core.c`. Let's see what's going on in there. I'm expecting some pretty intricate memory management stuff, as the name implies, and likely some crucial interactions with the BPF program structure itself. This is where the rubber meets the road when it comes to dynamically resizing BPF programs, which is a key capability for adapting to changing conditions or injecting new functionality at runtime. I need to understand this very precisely, as improper reallocations in the BPF world are a recipe for instability or security vulnerabilities. My main concerns are around atomicity, resource locking, and ensuring that any program state is properly handled during the re-sizing operation. Let's see how they've handled potential race conditions and memory leaks.



42/3 2026/10/02 05:50 tool read-file
0m Args:
{
  "File": "kernel/bpf/core.c",
  "FirstLine": 254,
  "LineCount": 40
}

Results:
{
  "Contents": " 254:\t\n 255:\tstruct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,\n 256:\t\t\t\t\t  gfp_t gfp_extra_flags)\n 257:\t{\n 258:\t\tgfp_t gfp_flags = bpf_memcg_flags(GFP_KERNEL | __GFP_ZERO | gfp_extra_flags);\n 259:\t\tstruct bpf_prog *fp;\n 260:\t\tu32 pages;\n 261:\t\n 262:\t\tsize = round_up(size, PAGE_SIZE);\n 263:\t\tpages = size / PAGE_SIZE;\n 264:\t\tif (pages \u003c= fp_old-\u003epages)\n 265:\t\t\treturn fp_old;\n 266:\t\n 267:\t\tfp = __vmalloc(size, gfp_flags);\n 268:\t\tif (fp) {\n 269:\t\t\tmemcpy(fp, fp_old, fp_old-\u003epages * PAGE_SIZE);\n 270:\t\t\tfp-\u003epages = pages;\n 271:\t\t\tfp-\u003eaux-\u003eprog = fp;\n 272:\t\n 273:\t\t\t/* We keep fp-\u003eaux from fp_old around in the new\n 274:\t\t\t * reallocated structure.\n 275:\t\t\t */\n 276:\t\t\tfp_old-\u003eaux = NULL;\n 277:\t\t\tfp_old-\u003estats = NULL;\n 278:\t\t\tfp_old-\u003eactive = NULL;\n 279:\t\t\t__bpf_prog_free(fp_old);\n 280:\t\t}\n 281:\t\n 282:\t\treturn fp;\n 283:\t}\n 284:\t\n 285:\tvoid __bpf_prog_free(struct bpf_prog *fp)\n 286:\t{\n 287:\t\tif (fp-\u003eaux) {\n 288:\t#ifdef CONFIG_BPF_SYSCALL\n 289:\t\t\tbpf_prog_stream_free(fp);\n 290:\t#endif\n 291:\t\t\tmutex_destroy(\u0026fp-\u003eaux-\u003eused_maps_mutex);\n 292:\t\t\tmutex_destroy(\u0026fp-\u003eaux-\u003edst_mutex);\n 293:\t\t\tmutex_destroy(\u0026fp-\u003eaux-\u003est_ops_assoc_mutex);\n"
}

43/3 2026/10/02 05:50 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 44955
							output: 42
							thoughts: 305
						

Thoughts:
**Analyzing BPF Instruction Array Remapping**

Okay, so I'm digging into how BPF instruction arrays are managed and reallocated. My initial observation is that `bpf_prog_realloc` seems to zero the memory it allocates using `__GFP_ZERO`. That's good, predictable behavior. And then, during a commit operation within `bpf_patch_list_commit`, there's a simple `memcpy` to copy the new instructions, followed by updating the program length. Pretty standard memory management pattern so far.

Now, I need to get to the core problem, so let's look closer at `bpf_insn_array_remap`. This function is pivotal for handling instruction array updates, and the logic here looks interesting. It iterates through the map's entries. Inside the loop it's checking the translated offset (`xlated_off`) of each instruction and potentially re-mapping it based on `new_off` or `grow` value, depending on whether it is less than or greater than `cnt` which appears to represent a count of valid entries. If the offset equals `INSN_DELETED` it continues, it is skipping over removed instructions.

I need to understand what `bpf_insn_array` is and how it relates to this remapping. I'll need to check the definition. Let me quickly jump to `kernel/bpf/bpf_insn_array.c`, specifically around line 253. I'm hoping to get a clearer picture of the data structure and how the `values` array and `xlated_off` relate.



44/3 2026/10/02 05:50 tool read-file
0m Args:
{
  "File": "kernel/bpf/bpf_insn_array.c",
  "FirstLine": 240,
  "LineCount": 45
}

Results:
{
  "Contents": " 240:\t{\n 241:\t\tstruct bpf_insn_array *insn_array = cast_insn_array(map);\n 242:\t\tint i;\n 243:\t\n 244:\t\tif (len \u003c= 1)\n 245:\t\t\treturn;\n 246:\t\n 247:\t\tfor (i = 0; i \u003c map-\u003emax_entries; i++) {\n 248:\t\t\tif (insn_array-\u003evalues[i].xlated_off \u003c first)\n 249:\t\t\t\tcontinue;\n 250:\t\t\tif (insn_array-\u003evalues[i].xlated_off == INSN_DELETED)\n 251:\t\t\t\tcontinue;\n 252:\t\t\tinsn_array-\u003evalues[i].xlated_off += len - 1;\n 253:\t\t}\n 254:\t}\n 255:\t\n 256:\t/*\n 257:\t * Move each offset to new_off[offset], new_off[] covers [0, cnt]. Offsets\n 258:\t * past the end of the old program only move by 'grow'.\n 259:\t */\n 260:\tvoid bpf_insn_array_remap(struct bpf_map *map, const u32 *new_off, u32 cnt, u32 grow)\n 261:\t{\n 262:\t\tstruct bpf_insn_array *insn_array = cast_insn_array(map);\n 263:\t\tu32 off;\n 264:\t\tint i;\n 265:\t\n 266:\t\tfor (i = 0; i \u003c map-\u003emax_entries; i++) {\n 267:\t\t\toff = insn_array-\u003evalues[i].xlated_off;\n 268:\t\t\tif (off == INSN_DELETED)\n 269:\t\t\t\tcontinue;\n 270:\t\t\tinsn_array-\u003evalues[i].xlated_off = off \u003c= cnt ? new_off[off] : off + grow;\n 271:\t\t}\n 272:\t}\n 273:\t\n 274:\tvoid bpf_insn_array_adjust_after_remove(struct bpf_map *map, u32 off, u32 len)\n 275:\t{\n 276:\t\tstruct bpf_insn_array *insn_array = cast_insn_array(map);\n 277:\t\tint i;\n 278:\t\n 279:\t\tfor (i = 0; i \u003c map-\u003emax_entries; i++) {\n 280:\t\t\tif (insn_array-\u003evalues[i].xlated_off \u003c off)\n 281:\t\t\t\tcontinue;\n 282:\t\t\tif (insn_array-\u003evalues[i].xlated_off == INSN_DELETED)\n 283:\t\t\t\tcontinue;\n 284:\t\t\tif (insn_array-\u003evalues[i].xlated_off \u003c off + len)\n"
}

45/3 2026/10/02 05:50 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 46422
							output: 359
							thoughts: 4674
						

Thoughts:
**Analysis of BPF Instruction Patching and Memory Safety**

Okay, so I'm diving into this BPF code, specifically the changes around how instructions get patched during fixups. My initial focus is on how various offsets and data structures are updated when a "patch list" approach is used, as opposed to the older, instruction-by-instruction patching. I'm checking, mostly, how `env->subprog_info`, `env->func_ptrs`, and `prog->aux->poke_tab` are handled with the remapping. I noticed the loop remapping these offsets with `patch_list_remap()`, ensuring that those are updated correctly when the instruction stream is modified.

Now, looking at `bpf_do_misc_fixups` in `fixups.c`, the key change seems to be replacing the older `bpf_patch_insn_data()` calls with `bpf_patch_list_add()`. This is because, now, instead of modifying the program immediately, the patches are queued up. Previously, the `i + delta` offset was a critical value in a growing program. Now, since the original program is not modified until `bpf_patch_list_commit(env)`, it's just `i`. If `len == 1`, it patches in place, but if `len > 1`, it queues the patch. I need to make sure that the `insn` pointer is properly pointing at the right instructions during the patching process, because `insn` previously was derived directly by computing with `delta`, and now it's getting set directly to `&prog->insnsi[i]`. So, I need to check how `insn` is updated throughout the entire process, especially at `next_insn`.

So, the next part is verifying the correct usage of the `insn` pointer in the cases where the `goto` is used to jump to `patch_call_imm`. This is important because the code accesses and modifies `insn->imm` at that point. If `bpf_patch_list_add` does not advance the `insn` pointer to the right spot, there could be a bug. The code checks `insn->imm` in the `patch_call_imm` label, where it should be the very last instruction of the patch. The `insn` returned by `bpf_patch_list_add` is the *last* instruction of the patch! So, when the code goes to `next_insn`, it should also be advancing the `insn` pointer to the right place.

Now, my main concern turns to memory safety: I'm meticulously going through this code, to make sure it doesn't introduce any new risks of reading uninitialized memory, or memory leaks. I want to determine if `NeedsKMSAN=true` or `false`.

I am making a detailed analysis of all the data structures being allocated or modified (`bpf_insn_patch`, `bpf_patch_list`, `new_off`, `insns`, `env->insn_aux_data`, and program structures). For each of these, I am ensuring that all the fields are initialized *before* they are read. For instance, the elements in `pl->insns` up to `pl->insn_cnt` are written with `memcpy`.

All the structure fields like `new_off` are properly initialized (looped from `0` to `cnt`). `insns` are initialized via nested loops, writing from indexes `0` to `new_cnt - 1` (thus every index is written to). `env->insn_aux_data` is allocated via `vrealloc` and is zeroed with `__GFP_ZERO`. The values of existing elements' `insn_off`, `xlated_off`, `insn_idx` are updated by `patch_list_remap()`. `patch_list_adj_insn`, called on existing insns, performs checks that ensure that `rel` and `is_imm` variables are initialized before use.

I am also making sure that there aren't any potential edge cases in the code (e.g. OOB access). I checked the returns of `insn_get_rel()` in `patch_list_adj_insn` and verified that errors are handled properly. I am also checking that any logical bugs would be caught by KASAN.

I am satisfied that all of the memory-related operations are handled correctly.

Given the nature of the patch, including: the detailed initialization of all data structures, the thorough handling of potential errors and bounds checking, the focus on fixing issues related to potentially incorrect instruction offsets and program execution, I am determining that there is no risk in exposing or introducing new uninitialized memory


46/3 2026/10/02 05:51 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch series replaces repeated individual instruction patching during bpf_do_misc_fixups() with a batched instruction patching mechanism (bpf_patch_list_add and bpf_patch_list_commit).\n\nAnalysis of memory initialization and info-leak risks:\n1. All allocated structures and arrays (pl-\u003epatches, pl-\u003einsns, new_off, intermediate insns, env-\u003einsn_aux_data, and reallocated bpf_prog) are completely initialized:\n   - pl-\u003epatches and pl-\u003einsns have their elements fully populated upon queueing in bpf_patch_list_add().\n   - new_off is allocated for cnt + 1 elements and fully filled in the loop covering 0 to cnt.\n   - insns is populated for every offset in [0, new_cnt) through contiguous partition slices.\n   - env-\u003einsn_aux_data is reallocated with __GFP_ZERO, zeroed via memset for new patch slots, and initialized through patch_list_adj_aux().\n   - Local variables in helper functions (e.g., insn_get_rel, patch_list_adj_insn) are explicitly assigned before use.\n2. No uninitialized memory or structure padding is exposed to userspace.\n3. Potential bugs in offset calculation, jump relocation, or buffer resizing would manifest as out-of-bounds memory accesses or invalid control flow, which are covered by KASAN.\n\nTherefore, the changes do not expose uninitialized memory risks that would necessitate a dedicated KMSAN fuzzing session.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch series replaces repeated individual instruction patching during bpf_do_misc_fixups() with a batched instruction patching mechanism (bpf_patch_list_add and bpf_patch_list_commit).\n\nAnalysis of memory initialization and info-leak risks:\n1. All allocated structures and arrays (pl-\u003epatches, pl-\u003einsns, new_off, intermediate insns, env-\u003einsn_aux_data, and reallocated bpf_prog) are completely initialized:\n   - pl-\u003epatches and pl-\u003einsns have their elements fully populated upon queueing in bpf_patch_list_add().\n   - new_off is allocated for cnt + 1 elements and fully filled in the loop covering 0 to cnt.\n   - insns is populated for every offset in [0, new_cnt) through contiguous partition slices.\n   - env-\u003einsn_aux_data is reallocated with __GFP_ZERO, zeroed via memset for new patch slots, and initialized through patch_list_adj_aux().\n   - Local variables in helper functions (e.g., insn_get_rel, patch_list_adj_insn) are explicitly assigned before use.\n2. No uninitialized memory or structure padding is exposed to userspace.\n3. Potential bugs in offset calculation, jump relocation, or buffer resizing would manifest as out-of-bounds memory accesses or invalid control flow, which are covered by KASAN.\n\nTherefore, the changes do not expose uninitialized memory risks that would necessitate a dedicated KMSAN fuzzing session.",
  "NeedsKMSAN": false
}

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