AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/07 21:37 flow patch-triage
1m Args:
null

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL"
  ],
  "FocusSymbols": [
    "bpf_insn_def32",
    "bpf_compute_live_registers",
    "bpf_opt_subreg_zext_lo32_rnd_hi32",
    "bpf_do_misc_fixups"
  ],
  "KMSANReasoning": "The patch modifies the BPF verifier's internal logic for tracking 32-bit subregister zero-extension and register liveness. It refactors how zero-extension requirements are computed by replacing the `subreg_def` field in `struct bpf_reg_state` with a liveness-based approach using `struct insn_live_regs`. These changes are purely algorithmic and do not introduce new kernel structures copied to user space, nor do they add complex data structures with potentially uninitialized fields. Any bugs introduced by these changes would manifest as verifier logic errors, potentially leading to out-of-bounds accesses or memory corruption in BPF programs, which are effectively caught by KASAN. There is no risk of uninitialized kernel memory being leaked or used in control flow, so a dedicated KMSAN fuzzing session is not required.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch modifies the BPF verifier to track 32-bit register zero-extension using liveness analysis instead of the `subreg_def` field in `bpf_reg_state`. This is a significant functional change to the core BPF verifier logic, affecting how 32-bit register values are zero-extended to 64-bit. This change is reachable via the `bpf()` syscall when loading BPF programs and is definitely worth fuzzing.",
  "WorthFuzzing": true
}

1/1 2026/08/07 21:37 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit c811bb7277c87779b883f585fbd26208510a1533\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Fri Aug 7 21:37:06 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h\nindex a2a40caca0a0e..2c74d676ede97 100644\n--- a/include/linux/bpf_verifier.h\n+++ b/include/linux/bpf_verifier.h\n@@ -162,11 +162,6 @@ struct bpf_reg_state {\n \t * pointing to bpf_func_state.\n \t */\n \tu32 frameno;\n-\t/* Tracks subreg definition. The stored value is the insn_idx of the\n-\t * writing insn. This is safe because subreg_def is used before any insn\n-\t * patching which only happens after main verification finished.\n-\t */\n-\ts32 subreg_def;\n \t/* if (!precise \u0026\u0026 SCALAR_VALUE) min/max/tnum don't affect safety */\n \tbool precise;\n };\n@@ -1637,7 +1632,6 @@ struct bpf_kfunc_desc_tab {\n };\n \n /* Functions exported from verifier.c, used by fixups.c */\n-bool bpf_is_reg64(struct bpf_insn *insn, u32 regno, struct bpf_reg_state *reg, enum bpf_reg_arg_type t);\n void bpf_clear_insn_aux_data(struct bpf_verifier_env *env, int start, int len);\n void bpf_mark_subprog_exc_cb(struct bpf_verifier_env *env, int subprog);\n bool bpf_allow_tail_call_in_subprogs(struct bpf_verifier_env *env);\n@@ -1661,5 +1655,6 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env);\n 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 \n #endif /* _LINUX_BPF_VERIFIER_H */\ndiff --git a/kernel/bpf/backtrack.c b/kernel/bpf/backtrack.c\nindex 2f473ad4fd7c1..40bd04421a991 100644\n--- a/kernel/bpf/backtrack.c\n+++ b/kernel/bpf/backtrack.c\n@@ -285,6 +285,7 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,\n \t\tverbose(env, \"stack=%s before \", env-\u003etmp_str_buf);\n \t\tverbose(env, \"%d: \", idx);\n \t\tbpf_verbose_insn(env, insn);\n+\t\tverbose(env, \"\\n\");\n \t}\n \n \t/* If there is a history record that some registers gained range at this insn,\ndiff --git a/kernel/bpf/disasm.c b/kernel/bpf/disasm.c\nindex 0391b3bc00734..50b3ca5149a0a 100644\n--- a/kernel/bpf/disasm.c\n+++ b/kernel/bpf/disasm.c\n@@ -139,7 +139,7 @@ static void print_bpf_end_insn(bpf_insn_print_t verbose,\n \t\t\t       void *private_data,\n \t\t\t       const struct bpf_insn *insn)\n {\n-\tverbose(private_data, \"(%02x) r%d = %s%d r%d\\n\",\n+\tverbose(private_data, \"(%02x) r%d = %s%d r%d\",\n \t\tinsn-\u003ecode, insn-\u003edst_reg,\n \t\tBPF_SRC(insn-\u003ecode) == BPF_TO_BE ? \"be\" : \"le\",\n \t\tinsn-\u003eimm, insn-\u003edst_reg);\n@@ -149,7 +149,7 @@ static void print_bpf_bswap_insn(bpf_insn_print_t verbose,\n \t\t\t       void *private_data,\n \t\t\t       const struct bpf_insn *insn)\n {\n-\tverbose(private_data, \"(%02x) r%d = bswap%d r%d\\n\",\n+\tverbose(private_data, \"(%02x) r%d = bswap%d r%d\",\n \t\tinsn-\u003ecode, insn-\u003edst_reg,\n \t\tinsn-\u003eimm, insn-\u003edst_reg);\n }\n@@ -197,19 +197,19 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,\n \t\t\telse\n \t\t\t\tprint_bpf_end_insn(verbose, cbs-\u003eprivate_data, insn);\n \t\t} else if (BPF_OP(insn-\u003ecode) == BPF_NEG) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) %c%d = -%c%d\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) %c%d = -%c%d\",\n \t\t\t\tinsn-\u003ecode, class == BPF_ALU ? 'w' : 'r',\n \t\t\t\tinsn-\u003edst_reg, class == BPF_ALU ? 'w' : 'r',\n \t\t\t\tinsn-\u003edst_reg);\n \t\t} else if (is_addr_space_cast(insn)) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r%d = addr_space_cast(r%d, %u, %u)\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r%d = addr_space_cast(r%d, %u, %u)\",\n \t\t\t\tinsn-\u003ecode, insn-\u003edst_reg,\n \t\t\t\tinsn-\u003esrc_reg, ((u32)insn-\u003eimm) \u003e\u003e 16, (u16)insn-\u003eimm);\n \t\t} else if (is_mov_percpu_addr(insn)) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r%d = \u0026(void __percpu *)(r%d)\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r%d = \u0026(void __percpu *)(r%d)\",\n \t\t\t\tinsn-\u003ecode, insn-\u003edst_reg, insn-\u003esrc_reg);\n \t\t} else if (BPF_SRC(insn-\u003ecode) == BPF_X) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) %c%d %s %s%c%d\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) %c%d %s %s%c%d\",\n \t\t\t\tinsn-\u003ecode, class == BPF_ALU ? 'w' : 'r',\n \t\t\t\tinsn-\u003edst_reg,\n \t\t\t\tis_sdiv_smod(insn) ? bpf_alu_sign_string[BPF_OP(insn-\u003ecode) \u003e\u003e 4]\n@@ -218,7 +218,7 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,\n \t\t\t\tclass == BPF_ALU ? 'w' : 'r',\n \t\t\t\tinsn-\u003esrc_reg);\n \t\t} else {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) %c%d %s %d\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) %c%d %s %d\",\n \t\t\t\tinsn-\u003ecode, class == BPF_ALU ? 'w' : 'r',\n \t\t\t\tinsn-\u003edst_reg,\n \t\t\t\tis_sdiv_smod(insn) ? bpf_alu_sign_string[BPF_OP(insn-\u003ecode) \u003e\u003e 4]\n@@ -227,7 +227,7 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,\n \t\t}\n \t} else if (class == BPF_STX) {\n \t\tif (BPF_MODE(insn-\u003ecode) == BPF_MEM)\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) *(%s *)(r%d %+d) = r%d\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) *(%s *)(r%d %+d) = r%d\",\n \t\t\t\tinsn-\u003ecode,\n \t\t\t\tbpf_ldst_string[BPF_SIZE(insn-\u003ecode) \u003e\u003e 3],\n \t\t\t\tinsn-\u003edst_reg,\n@@ -235,7 +235,7 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,\n \t\telse if (BPF_MODE(insn-\u003ecode) == BPF_ATOMIC \u0026\u0026\n \t\t\t (insn-\u003eimm == BPF_ADD || insn-\u003eimm == BPF_AND ||\n \t\t\t  insn-\u003eimm == BPF_OR || insn-\u003eimm == BPF_XOR)) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) lock *(%s *)(r%d %+d) %s r%d\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) lock *(%s *)(r%d %+d) %s r%d\",\n \t\t\t\tinsn-\u003ecode,\n \t\t\t\tbpf_ldst_string[BPF_SIZE(insn-\u003ecode) \u003e\u003e 3],\n \t\t\t\tinsn-\u003edst_reg, insn-\u003eoff,\n@@ -246,7 +246,7 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,\n \t\t\t    insn-\u003eimm == (BPF_AND | BPF_FETCH) ||\n \t\t\t    insn-\u003eimm == (BPF_OR | BPF_FETCH) ||\n \t\t\t    insn-\u003eimm == (BPF_XOR | BPF_FETCH))) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r%d = atomic%s_fetch_%s((%s *)(r%d %+d), r%d)\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r%d = atomic%s_fetch_%s((%s *)(r%d %+d), r%d)\",\n \t\t\t\tinsn-\u003ecode, insn-\u003esrc_reg,\n \t\t\t\tBPF_SIZE(insn-\u003ecode) == BPF_DW ? \"64\" : \"\",\n \t\t\t\tbpf_atomic_alu_string[BPF_OP(insn-\u003eimm) \u003e\u003e 4],\n@@ -254,7 +254,7 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,\n \t\t\t\tinsn-\u003edst_reg, insn-\u003eoff, insn-\u003esrc_reg);\n \t\t} else if (BPF_MODE(insn-\u003ecode) == BPF_ATOMIC \u0026\u0026\n \t\t\t   insn-\u003eimm == BPF_CMPXCHG) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r0 = atomic%s_cmpxchg((%s *)(r%d %+d), r0, r%d)\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r0 = atomic%s_cmpxchg((%s *)(r%d %+d), r0, r%d)\",\n \t\t\t\tinsn-\u003ecode,\n \t\t\t\tBPF_SIZE(insn-\u003ecode) == BPF_DW ? \"64\" : \"\",\n \t\t\t\tbpf_ldst_string[BPF_SIZE(insn-\u003ecode) \u003e\u003e 3],\n@@ -262,44 +262,44 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,\n \t\t\t\tinsn-\u003esrc_reg);\n \t\t} else if (BPF_MODE(insn-\u003ecode) == BPF_ATOMIC \u0026\u0026\n \t\t\t   insn-\u003eimm == BPF_XCHG) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r%d = atomic%s_xchg((%s *)(r%d %+d), r%d)\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r%d = atomic%s_xchg((%s *)(r%d %+d), r%d)\",\n \t\t\t\tinsn-\u003ecode, insn-\u003esrc_reg,\n \t\t\t\tBPF_SIZE(insn-\u003ecode) == BPF_DW ? \"64\" : \"\",\n \t\t\t\tbpf_ldst_string[BPF_SIZE(insn-\u003ecode) \u003e\u003e 3],\n \t\t\t\tinsn-\u003edst_reg, insn-\u003eoff, insn-\u003esrc_reg);\n \t\t} else if (BPF_MODE(insn-\u003ecode) == BPF_ATOMIC \u0026\u0026\n \t\t\t   insn-\u003eimm == BPF_LOAD_ACQ) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r%d = load_acquire((%s *)(r%d %+d))\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r%d = load_acquire((%s *)(r%d %+d))\",\n \t\t\t\tinsn-\u003ecode, insn-\u003edst_reg,\n \t\t\t\tbpf_ldst_string[BPF_SIZE(insn-\u003ecode) \u003e\u003e 3],\n \t\t\t\tinsn-\u003esrc_reg, insn-\u003eoff);\n \t\t} else if (BPF_MODE(insn-\u003ecode) == BPF_ATOMIC \u0026\u0026\n \t\t\t   insn-\u003eimm == BPF_STORE_REL) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) store_release((%s *)(r%d %+d), r%d)\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) store_release((%s *)(r%d %+d), r%d)\",\n \t\t\t\tinsn-\u003ecode,\n \t\t\t\tbpf_ldst_string[BPF_SIZE(insn-\u003ecode) \u003e\u003e 3],\n \t\t\t\tinsn-\u003edst_reg, insn-\u003eoff, insn-\u003esrc_reg);\n \t\t} else {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"BUG_%02x\\n\", insn-\u003ecode);\n+\t\t\tverbose(cbs-\u003eprivate_data, \"BUG_%02x\", insn-\u003ecode);\n \t\t}\n \t} else if (class == BPF_ST) {\n \t\tif (BPF_MODE(insn-\u003ecode) == BPF_MEM) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) *(%s *)(r%d %+d) = %d\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) *(%s *)(r%d %+d) = %d\",\n \t\t\t\tinsn-\u003ecode,\n \t\t\t\tbpf_ldst_string[BPF_SIZE(insn-\u003ecode) \u003e\u003e 3],\n \t\t\t\tinsn-\u003edst_reg,\n \t\t\t\tinsn-\u003eoff, insn-\u003eimm);\n \t\t} else if (BPF_MODE(insn-\u003ecode) == 0xc0 /* BPF_NOSPEC, no UAPI */) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) nospec\\n\", insn-\u003ecode);\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) nospec\", insn-\u003ecode);\n \t\t} else {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"BUG_st_%02x\\n\", insn-\u003ecode);\n+\t\t\tverbose(cbs-\u003eprivate_data, \"BUG_st_%02x\", insn-\u003ecode);\n \t\t}\n \t} else if (class == BPF_LDX) {\n \t\tif (BPF_MODE(insn-\u003ecode) != BPF_MEM \u0026\u0026 BPF_MODE(insn-\u003ecode) != BPF_MEMSX) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"BUG_ldx_%02x\\n\", insn-\u003ecode);\n+\t\t\tverbose(cbs-\u003eprivate_data, \"BUG_ldx_%02x\", insn-\u003ecode);\n \t\t\treturn;\n \t\t}\n-\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r%d = *(%s *)(r%d %+d)\\n\",\n+\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r%d = *(%s *)(r%d %+d)\",\n \t\t\tinsn-\u003ecode, insn-\u003edst_reg,\n \t\t\tBPF_MODE(insn-\u003ecode) == BPF_MEM ?\n \t\t\t\t bpf_ldst_string[BPF_SIZE(insn-\u003ecode) \u003e\u003e 3] :\n@@ -307,12 +307,12 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,\n \t\t\tinsn-\u003esrc_reg, insn-\u003eoff);\n \t} else if (class == BPF_LD) {\n \t\tif (BPF_MODE(insn-\u003ecode) == BPF_ABS) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r0 = *(%s *)skb[%d]\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r0 = *(%s *)skb[%d]\",\n \t\t\t\tinsn-\u003ecode,\n \t\t\t\tbpf_ldst_string[BPF_SIZE(insn-\u003ecode) \u003e\u003e 3],\n \t\t\t\tinsn-\u003eimm);\n \t\t} else if (BPF_MODE(insn-\u003ecode) == BPF_IND) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r0 = *(%s *)skb[r%d + %d]\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r0 = *(%s *)skb[r%d + %d]\",\n \t\t\t\tinsn-\u003ecode,\n \t\t\t\tbpf_ldst_string[BPF_SIZE(insn-\u003ecode) \u003e\u003e 3],\n \t\t\t\tinsn-\u003esrc_reg, insn-\u003eimm);\n@@ -332,12 +332,12 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,\n \t\t\tif (is_ptr \u0026\u0026 !allow_ptr_leaks)\n \t\t\t\timm = 0;\n \n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r%d = %s\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) r%d = %s\",\n \t\t\t\tinsn-\u003ecode, insn-\u003edst_reg,\n \t\t\t\t__func_imm_name(cbs, insn, imm,\n \t\t\t\t\t\ttmp, sizeof(tmp)));\n \t\t} else {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"BUG_ld_%02x\\n\", insn-\u003ecode);\n+\t\t\tverbose(cbs-\u003eprivate_data, \"BUG_ld_%02x\", insn-\u003ecode);\n \t\t\treturn;\n \t\t}\n \t} else if (class == BPF_JMP32 || class == BPF_JMP) {\n@@ -347,35 +347,35 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,\n \t\t\tchar tmp[64];\n \n \t\t\tif (insn-\u003esrc_reg == BPF_PSEUDO_CALL) {\n-\t\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) call pc%s\\n\",\n+\t\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) call pc%s\",\n \t\t\t\t\tinsn-\u003ecode,\n \t\t\t\t\t__func_get_name(cbs, insn,\n \t\t\t\t\t\t\ttmp, sizeof(tmp)));\n \t\t\t} else {\n \t\t\t\tstrcpy(tmp, \"unknown\");\n-\t\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) call %s#%d\\n\", insn-\u003ecode,\n+\t\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) call %s#%d\", insn-\u003ecode,\n \t\t\t\t\t__func_get_name(cbs, insn,\n \t\t\t\t\t\t\ttmp, sizeof(tmp)),\n \t\t\t\t\tinsn-\u003eimm);\n \t\t\t}\n \t\t} else if (insn-\u003ecode == (BPF_JMP | BPF_JA)) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) goto pc%+d\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) goto pc%+d\",\n \t\t\t\tinsn-\u003ecode, insn-\u003eoff);\n \t\t} else if (insn-\u003ecode == (BPF_JMP | BPF_JA | BPF_X)) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) gotox r%d\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) gotox r%d\",\n \t\t\t\tinsn-\u003ecode, insn-\u003edst_reg);\n \t\t} else if (insn-\u003ecode == (BPF_JMP | BPF_JCOND) \u0026\u0026\n \t\t\t   insn-\u003esrc_reg == BPF_MAY_GOTO) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) may_goto pc%+d\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) may_goto pc%+d\",\n \t\t\t\tinsn-\u003ecode, insn-\u003eoff);\n \t\t} else if (insn-\u003ecode == (BPF_JMP32 | BPF_JA)) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) gotol pc%+d\\n\",\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) gotol pc%+d\",\n \t\t\t\tinsn-\u003ecode, insn-\u003eimm);\n \t\t} else if (insn-\u003ecode == (BPF_JMP | BPF_EXIT)) {\n-\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) exit\\n\", insn-\u003ecode);\n+\t\t\tverbose(cbs-\u003eprivate_data, \"(%02x) exit\", insn-\u003ecode);\n \t\t} else if (BPF_SRC(insn-\u003ecode) == BPF_X) {\n \t\t\tverbose(cbs-\u003eprivate_data,\n-\t\t\t\t\"(%02x) if %c%d %s %c%d goto pc%+d\\n\",\n+\t\t\t\t\"(%02x) if %c%d %s %c%d goto pc%+d\",\n \t\t\t\tinsn-\u003ecode, class == BPF_JMP32 ? 'w' : 'r',\n \t\t\t\tinsn-\u003edst_reg,\n \t\t\t\tbpf_jmp_string[BPF_OP(insn-\u003ecode) \u003e\u003e 4],\n@@ -383,14 +383,14 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,\n \t\t\t\tinsn-\u003esrc_reg, insn-\u003eoff);\n \t\t} else {\n \t\t\tverbose(cbs-\u003eprivate_data,\n-\t\t\t\t\"(%02x) if %c%d %s 0x%x goto pc%+d\\n\",\n+\t\t\t\t\"(%02x) if %c%d %s 0x%x goto pc%+d\",\n \t\t\t\tinsn-\u003ecode, class == BPF_JMP32 ? 'w' : 'r',\n \t\t\t\tinsn-\u003edst_reg,\n \t\t\t\tbpf_jmp_string[BPF_OP(insn-\u003ecode) \u003e\u003e 4],\n \t\t\t\t(u32)insn-\u003eimm, insn-\u003eoff);\n \t\t}\n \t} else {\n-\t\tverbose(cbs-\u003eprivate_data, \"(%02x) %s\\n\",\n+\t\tverbose(cbs-\u003eprivate_data, \"(%02x) %s\",\n \t\t\tinsn-\u003ecode, bpf_class_string[class]);\n \t}\n }\ndiff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c\nindex a0bddada7964e..661e2d13a6042 100644\n--- a/kernel/bpf/fixups.c\n+++ b/kernel/bpf/fixups.c\n@@ -20,6 +20,26 @@ static bool is_cmpxchg_insn(const struct bpf_insn *insn)\n \t       insn-\u003eimm == BPF_CMPXCHG;\n }\n \n+/* Returns true if 'insn' is an address space cast instruction translated as BPF_ALU op */\n+static bool is_addr_space_cast32(struct bpf_prog *prog, const struct bpf_insn *insn)\n+{\n+\tstruct bpf_map *arena = (struct bpf_map *)prog-\u003eaux-\u003earena;\n+\n+\tif (insn-\u003ecode != (BPF_ALU64 | BPF_MOV | BPF_X) || insn-\u003eoff != BPF_ADDR_SPACE_CAST)\n+\t\treturn false;\n+\n+\t/* cast from as(1) to as(0) */\n+\tif (insn-\u003eimm == 1)\n+\t\treturn true;\n+\n+\t/* cast from as(0) to as(1) */\n+\tif (insn-\u003eimm == 1 \u003c\u003c 16)\n+\t\treturn arena \u0026\u0026 arena-\u003emap_flags \u0026 BPF_F_NO_USER_CONV;\n+\n+\t/* non-BPF_F_NO_USER_CONV cast from as(0) to as(1) should be handled by JIT */\n+\treturn false;\n+}\n+\n /* Return the regno defined by the insn, or -1. */\n static int insn_def_regno(const struct bpf_insn *insn)\n {\n@@ -44,15 +64,60 @@ static int insn_def_regno(const struct bpf_insn *insn)\n \t}\n }\n \n-/* Return TRUE if INSN has defined any 32-bit value explicitly. */\n-static bool insn_has_def32(struct bpf_insn *insn)\n+/*\n+ * For use only in combination with insn_def_regno() \u003e= 0.\n+ * Returns TRUE if the destination register operates on 64-bit,\n+ * otherwise return FALSE.\n+ */\n+static bool bpf_is_reg64(struct bpf_prog *prog, struct bpf_insn *insn)\n {\n-\tint dst_reg = insn_def_regno(insn);\n+\tu8 class = BPF_CLASS(insn-\u003ecode);\n+\tu8 mode = BPF_MODE(insn-\u003ecode);\n+\tu8 size = BPF_SIZE(insn-\u003ecode);\n+\tu8 op = BPF_OP(insn-\u003ecode);\n+\tbool mode_mem;\n+\n+\t/* subregister endiness swap */\n+\tif ((class == BPF_ALU || class == BPF_ALU64) \u0026\u0026 op == BPF_END \u0026\u0026 insn-\u003eimm != 64)\n+\t\treturn false;\n+\n+\t/* w0 += 1 */\n+\tif (class == BPF_ALU \u0026\u0026 op != BPF_END)\n+\t\treturn false;\n+\n+\t/* address space casts converted to BPF_ALU, see bpf_do_misc_fixups() */\n+\tif (is_addr_space_cast32(prog, insn))\n+\t\treturn false;\n \n-\tif (dst_reg == -1)\n+\t/* non 64-bit, non signed extended loads */\n+\tmode_mem = mode == BPF_MEM || mode == BPF_PROBE_MEM || mode == BPF_PROBE_MEM32;\n+\tif (class == BPF_LDX \u0026\u0026 mode_mem \u0026\u0026 size != BPF_DW)\n \t\treturn false;\n \n-\treturn !bpf_is_reg64(insn, dst_reg, NULL, DST_OP);\n+\t/* atomics, see insn_def_regno() */\n+\tif (class == BPF_STX \u0026\u0026 size != BPF_DW)\n+\t\treturn false;\n+\n+\t/* both LD_IND and LD_ABS return 32-bit data. */\n+\tif (class == BPF_LD \u0026\u0026 (mode == BPF_IND || mode == BPF_ABS))\n+\t\treturn false;\n+\n+\t/* Conservatively return true at default. */\n+\treturn true;\n+}\n+\n+/*\n+ * Return the 32-bit subregister defined by INSN, or -1 if INSN does not\n+ * explicitly define a 32-bit value.\n+ */\n+int bpf_insn_def32(struct bpf_prog *prog, struct bpf_insn *insn)\n+{\n+\tint dst_reg = insn_def_regno(insn);\n+\n+\tif (dst_reg \u003c 0 || bpf_is_reg64(prog, insn))\n+\t\treturn -1;\n+\n+\treturn dst_reg;\n }\n \n static int kfunc_desc_cmp_by_imm_off(const void *a, const void *b)\n@@ -169,7 +234,7 @@ static void adjust_insn_aux_data(struct bpf_verifier_env *env,\n \t * (cnt == 1) is taken or not. There is no guarantee INSN at OFF is the\n \t * original insn at old prog.\n \t */\n-\tdata[off].zext_dst = insn_has_def32(insn + off + cnt - 1);\n+\tdata[off].zext_dst = bpf_insn_def32(new_prog, insn + off + cnt - 1) \u003e= 0;\n \n \tif (cnt == 1)\n \t\treturn;\n@@ -181,7 +246,7 @@ static void adjust_insn_aux_data(struct bpf_verifier_env *env,\n \tfor (i = off; i \u003c off + cnt - 1; i++) {\n \t\t/* Expand insni[off]'s seen count to the patched range. */\n \t\tdata[i].seen = old_seen;\n-\t\tdata[i].zext_dst = insn_has_def32(insn + i);\n+\t\tdata[i].zext_dst = bpf_insn_def32(new_prog, insn + i) \u003e= 0;\n \t}\n \n \t/*\n@@ -616,11 +681,7 @@ int bpf_opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,\n \t\t\tif (load_reg == -1)\n \t\t\t\tcontinue;\n \n-\t\t\t/* NOTE: arg \"reg\" (the fourth one) is only used for\n-\t\t\t *       BPF_STX + SRC_OP, so it is safe to pass NULL\n-\t\t\t *       here.\n-\t\t\t */\n-\t\t\tif (bpf_is_reg64(\u0026insn, load_reg, NULL, DST_OP)) {\n+\t\t\tif (bpf_is_reg64(env-\u003eprog, \u0026insn)) {\n \t\t\t\tif (class == BPF_LD \u0026\u0026\n \t\t\t\t    BPF_MODE(code) == BPF_IMM)\n \t\t\t\t\ti++;\n@@ -1513,15 +1574,12 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t}\n \n \tfor (i = 0; i \u003c insn_cnt;) {\n-\t\tif (insn-\u003ecode == (BPF_ALU64 | BPF_MOV | BPF_X) \u0026\u0026 insn-\u003eimm) {\n-\t\t\tif ((insn-\u003eoff == BPF_ADDR_SPACE_CAST \u0026\u0026 insn-\u003eimm == 1) ||\n-\t\t\t    (((struct bpf_map *)env-\u003eprog-\u003eaux-\u003earena)-\u003emap_flags \u0026 BPF_F_NO_USER_CONV)) {\n-\t\t\t\t/* convert to 32-bit mov that clears upper 32-bit */\n-\t\t\t\tinsn-\u003ecode = BPF_ALU | BPF_MOV | BPF_X;\n-\t\t\t\t/* clear off and imm, so it's a normal 'wX = wY' from JIT pov */\n-\t\t\t\tinsn-\u003eoff = 0;\n-\t\t\t\tinsn-\u003eimm = 0;\n-\t\t\t} /* cast from as(0) to as(1) should be handled by JIT */\n+\t\tif (is_addr_space_cast32(env-\u003eprog, insn)) {\n+\t\t\t/* convert to 32-bit mov that clears upper 32-bit */\n+\t\t\tinsn-\u003ecode = BPF_ALU | BPF_MOV | BPF_X;\n+\t\t\t/* clear off and imm, so it's a normal 'wX = wY' from JIT pov */\n+\t\t\tinsn-\u003eoff = 0;\n+\t\t\tinsn-\u003eimm = 0;\n \t\t\tgoto next_insn;\n \t\t}\n \ndiff --git a/kernel/bpf/liveness.c b/kernel/bpf/liveness.c\nindex 0aadfbae0acc5..ef9a5a9228872 100644\n--- a/kernel/bpf/liveness.c\n+++ b/kernel/bpf/liveness.c\n@@ -497,7 +497,6 @@ static void print_instance(struct bpf_verifier_env *env, struct func_instance *i\n \t\tpos = env-\u003elog.end_pos;\n \t\tverbose(env, \"%3d: \", insn_idx);\n \t\tbpf_verbose_insn(env, \u0026insns[insn_idx]);\n-\t\tbpf_vlog_reset(\u0026env-\u003elog, env-\u003elog.end_pos - 1); /* remove \\n */\n \t\tinsn_pos = env-\u003elog.end_pos;\n \t\tverbose(env, \"%*c;\", bpf_vlog_alignment(insn_pos - pos), ' ');\n \t\tpos = env-\u003elog.end_pos;\n@@ -1043,7 +1042,6 @@ static void arg_track_log(struct bpf_verifier_env *env, struct bpf_insn *insn, i\n \t\tif (!printed) {\n \t\t\tverbose(env, \"%3d: \", idx);\n \t\t\tbpf_verbose_insn(env, insn);\n-\t\t\tbpf_vlog_reset(\u0026env-\u003elog, env-\u003elog.end_pos - 1);\n \t\t\tprinted = true;\n \t\t}\n \t\tverbose(env, \"\\tr%d: \", i); verbose_arg_track(env, \u0026at_in[i]);\n@@ -1058,7 +1056,6 @@ static void arg_track_log(struct bpf_verifier_env *env, struct bpf_insn *insn, i\n \t\tif (!printed) {\n \t\t\tverbose(env, \"%3d: \", idx);\n \t\t\tbpf_verbose_insn(env, insn);\n-\t\t\tbpf_vlog_reset(\u0026env-\u003elog, env-\u003elog.end_pos - 1);\n \t\t\tprinted = true;\n \t\t}\n \t\tverbose(env, \"\\tsa%d: \", i); verbose_arg_track(env, \u0026at_in[ai]);\n@@ -1070,7 +1067,6 @@ static void arg_track_log(struct bpf_verifier_env *env, struct bpf_insn *insn, i\n \t\tif (!printed) {\n \t\t\tverbose(env, \"%3d: \", idx);\n \t\t\tbpf_verbose_insn(env, insn);\n-\t\t\tbpf_vlog_reset(\u0026env-\u003elog, env-\u003elog.end_pos - 1);\n \t\t\tprinted = true;\n \t\t}\n \t\tverbose(env, \"\\tfp%+d: \", -(i + 1) * 8); verbose_arg_track(env, \u0026at_stack_in[i]);\n@@ -1545,6 +1541,7 @@ static void print_subprog_arg_access(struct bpf_verifier_env *env,\n \n \t\tverbose(env, \"%3d: \", idx);\n \t\tbpf_verbose_insn(env, \u0026insns[idx]);\n+\t\tverbose(env, \"\\n\");\n \n \t\t/* Collect what needs printing */\n \t\tif (is_ldx_stx_call \u0026\u0026\n@@ -2050,29 +2047,38 @@ int bpf_compute_subprog_arg_access(struct bpf_verifier_env *env)\n \n /* Each field is a register bitmask */\n struct insn_live_regs {\n-\tu16 use;\t/* registers read by instruction */\n-\tu16 def;\t/* registers written by instruction */\n-\tu16 in;\t\t/* registers that may be alive before instruction */\n-\tu16 out;\t/* registers that may be alive after instruction */\n+\tu32 use;\t/* registers read by instruction */\n+\tu32 def;\t/* registers written by instruction */\n+\tu32 in;\t\t/* registers that may be alive before instruction */\n+\tu32 out;\t/* registers that may be alive after instruction */\n };\n \n /* Bitmask with 1s for all caller saved registers */\n #define ALL_CALLER_SAVED_REGS ((1u \u003c\u003c CALLER_SAVED_REGS) - 1)\n \n+static inline u32 reg32_mask(u32 n) { return BIT(n); }\n+static inline u32 reg64_mask(u32 n) { return BIT(n) | BIT(n + 16); }\n+static inline u32 mask_widen(u32 m) { return m | (m \u003c\u003c 16); }\n+static inline u16 mask_lo(u32 m) { return (u16)m; }\n+static inline u16 mask_hi(u32 m) { return (u16)(m \u003e\u003e 16); }\n+\n /* Compute info-\u003e{use,def} fields for the instruction */\n static void compute_insn_live_regs(struct bpf_verifier_env *env,\n \t\t\t\t   struct bpf_insn *insn,\n \t\t\t\t   struct insn_live_regs *info)\n {\n \tstruct bpf_call_summary cs;\n-\tu8 class = BPF_CLASS(insn-\u003ecode);\n-\tu8 code = BPF_OP(insn-\u003ecode);\n-\tu8 mode = BPF_MODE(insn-\u003ecode);\n-\tu16 src = BIT(insn-\u003esrc_reg);\n-\tu16 dst = BIT(insn-\u003edst_reg);\n-\tu16 r0  = BIT(0);\n-\tu16 def = 0;\n-\tu16 use = 0xffff;\n+\tconst u8 class = BPF_CLASS(insn-\u003ecode);\n+\tconst u8 code = BPF_OP(insn-\u003ecode);\n+\tconst u8 mode = BPF_MODE(insn-\u003ecode);\n+\tconst u8 size = BPF_SIZE(insn-\u003ecode);\n+\tconst u32 src = reg64_mask(insn-\u003esrc_reg);\n+\tconst u32 dst = reg64_mask(insn-\u003edst_reg);\n+\tconst u32 src32 = mask_lo(src);\n+\tconst u32 dst32 = mask_lo(dst);\n+\tconst u32 r0  = reg64_mask(0);\n+\tu32 def = 0;\n+\tu32 use = U32_MAX;\n \n \tswitch (class) {\n \tcase BPF_LD:\n@@ -2083,8 +2089,8 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,\n \t\t\t\tuse = 0;\n \t\t\t}\n \t\t\tbreak;\n-\t\tcase BPF_LD | BPF_ABS:\n-\t\tcase BPF_LD | BPF_IND:\n+\t\tcase BPF_ABS:\n+\t\tcase BPF_IND:\n \t\t\t/* stick with defaults */\n \t\t\tbreak;\n \t\t}\n@@ -2092,7 +2098,15 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,\n \tcase BPF_LDX:\n \t\tswitch (mode) {\n \t\tcase BPF_MEM:\n+\t\t\t/* a narrow load still redefines the whole register */\n+\t\t\tdef = dst;\n+\t\t\tuse = src;\n+\t\t\tbreak;\n \t\tcase BPF_MEMSX:\n+\t\t\t/*\n+\t\t\t * sign extension defines the whole register;\n+\t\t\t * src holds a pointer, hence is used as 64-bit.\n+\t\t\t */\n \t\t\tdef = dst;\n \t\t\tuse = src;\n \t\t\tbreak;\n@@ -2110,12 +2124,19 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,\n \t\tswitch (mode) {\n \t\tcase BPF_MEM:\n \t\t\tdef = 0;\n-\t\t\tuse = dst | src;\n+\t\t\tuse = dst | (size == BPF_DW ? src : src32);\n \t\t\tbreak;\n-\t\tcase BPF_ATOMIC:\n+\t\tcase BPF_ATOMIC: {\n+\t\t\t/*\n+\t\t\t * dst holds a pointer and is always used as 64-bit;\n+\t\t\t * the value operand and r0 are read as 32-bit for BPF_W atomics.\n+\t\t\t */\n+\t\t\tu32 srcv = size == BPF_DW ? src : src32;\n+\t\t\tu32 r0v  = size == BPF_DW ? r0 : mask_lo(r0);\n+\n \t\t\tswitch (insn-\u003eimm) {\n \t\t\tcase BPF_CMPXCHG:\n-\t\t\t\tuse = r0 | dst | src;\n+\t\t\t\tuse = r0v | dst | srcv;\n \t\t\t\tdef = r0;\n \t\t\t\tbreak;\n \t\t\tcase BPF_LOAD_ACQ:\n@@ -2124,10 +2145,10 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,\n \t\t\t\tbreak;\n \t\t\tcase BPF_STORE_REL:\n \t\t\t\tdef = 0;\n-\t\t\t\tuse = dst | src;\n+\t\t\t\tuse = dst | srcv;\n \t\t\t\tbreak;\n \t\t\tdefault:\n-\t\t\t\tuse = dst | src;\n+\t\t\t\tuse = dst | srcv;\n \t\t\t\tif (insn-\u003eimm \u0026 BPF_FETCH)\n \t\t\t\t\tdef = src;\n \t\t\t\telse\n@@ -2135,6 +2156,7 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,\n \t\t\t}\n \t\t\tbreak;\n \t\t}\n+\t\t}\n \t\tbreak;\n \tcase BPF_ALU:\n \tcase BPF_ALU64:\n@@ -2148,14 +2170,14 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,\n \t\t\tif (BPF_SRC(insn-\u003ecode) == BPF_K)\n \t\t\t\tuse = 0;\n \t\t\telse\n-\t\t\t\tuse = src;\n+\t\t\t\tuse = class == BPF_ALU64 ? src : src32;\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tdef = dst;\n \t\t\tif (BPF_SRC(insn-\u003ecode) == BPF_K)\n-\t\t\t\tuse = dst;\n+\t\t\t\tuse = class == BPF_ALU64 ? dst : dst32;\n \t\t\telse\n-\t\t\t\tuse = dst | src;\n+\t\t\t\tuse = class == BPF_ALU64 ? (dst | src) : (dst32 | src32);\n \t\t}\n \t\tbreak;\n \tcase BPF_JMP:\n@@ -2181,13 +2203,14 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,\n \t\t\tuse = def \u0026 ~BIT(BPF_REG_0);\n \t\t\tif (bpf_get_call_summary(env, insn, \u0026cs))\n \t\t\t\tuse = GENMASK(min_t(u8, cs.num_params, MAX_BPF_FUNC_REG_ARGS), 1);\n+\t\t\tdef = mask_widen(def);\n+\t\t\tuse = mask_widen(use);\n \t\t\tbreak;\n \t\tdefault:\n \t\t\tdef = 0;\n-\t\t\tif (BPF_SRC(insn-\u003ecode) == BPF_K)\n-\t\t\t\tuse = dst;\n-\t\t\telse\n-\t\t\t\tuse = dst | src;\n+\t\t\tuse = class == BPF_JMP ? dst : dst32;\n+\t\t\tif (BPF_SRC(insn-\u003ecode) == BPF_X)\n+\t\t\t\tuse |= class == BPF_JMP ? src : src32;\n \t\t}\n \t\tbreak;\n \t}\n@@ -2209,6 +2232,7 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)\n \tstruct bpf_insn *insns = env-\u003eprog-\u003einsnsi;\n \tstruct insn_live_regs *state;\n \tint insn_cnt = env-\u003eprog-\u003elen;\n+\tu64 pos, insn_pos;\n \tint err = 0, i, j;\n \tbool changed;\n \n@@ -2252,8 +2276,8 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)\n \t\t\tint insn_idx = env-\u003ecfg.insn_postorder[i];\n \t\t\tstruct insn_live_regs *live = \u0026state[insn_idx];\n \t\t\tstruct bpf_iarray *succ;\n-\t\t\tu16 new_out = 0;\n-\t\t\tu16 new_in = 0;\n+\t\t\tu32 new_out = 0;\n+\t\t\tu32 new_in = 0;\n \n \t\t\tsucc = bpf_insn_successors(env, insn_idx);\n \t\t\tfor (int s = 0; s \u003c succ-\u003ecnt; ++s)\n@@ -2267,8 +2291,20 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)\n \t\t}\n \t}\n \n-\tfor (i = 0; i \u003c insn_cnt; ++i)\n-\t\tinsn_aux[i].live_regs_before = state[i].in;\n+\tfor (i = 0; i \u003c insn_cnt; ++i) {\n+\t\tint def32 = bpf_insn_def32(env-\u003eprog, \u0026insns[i]);\n+\t\tu32 out = state[i].out;\n+\t\tu32 in = state[i].in;\n+\n+\t\tinsn_aux[i].live_regs_before = mask_lo(in) | mask_hi(in);\n+\t\t/*\n+\t\t * On architectures where 32-bit operations do not reset upper halves\n+\t\t * of the registers, the verifier needs to zero extend a destination\n+\t\t * register if an instruction defines a 32-bit subregister and the\n+\t\t * upper half of that register is alive after the instruction.\n+\t\t */\n+\t\tinsn_aux[i].zext_dst = def32 \u003e= 0 \u0026\u0026 (mask_hi(out) \u0026 BIT(def32));\n+\t}\n \n \tif (env-\u003elog.level \u0026 BPF_LOG_LEVEL2) {\n \t\tverbose(env, \"Live regs before insn:\\n\");\n@@ -2284,7 +2320,12 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)\n \t\t\t\telse\n \t\t\t\t\tverbose(env, \".\");\n \t\t\tverbose(env, \" \");\n+\t\t\tpos = env-\u003elog.end_pos;\n \t\t\tbpf_verbose_insn(env, \u0026insns[i]);\n+\t\t\tinsn_pos = env-\u003elog.end_pos;\n+\t\t\tif (insn_aux[i].zext_dst)\n+\t\t\t\tverbose(env, \"%*c; zext\", bpf_vlog_alignment(insn_pos - pos), ' ');\n+\t\t\tverbose(env, \"\\n\");\n \t\t\tif (bpf_is_ldimm64(\u0026insns[i]))\n \t\t\t\ti++;\n \t\t}\ndiff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c\nindex e6233c0081d10..14f7516458749 100644\n--- a/kernel/bpf/verifier.c\n+++ b/kernel/bpf/verifier.c\n@@ -2132,12 +2132,9 @@ static int reg_bounds_sanity_check(struct bpf_verifier_env *env,\n /* Mark a register as having a completely unknown (scalar) value. */\n void bpf_mark_reg_unknown_imprecise(struct bpf_reg_state *reg)\n {\n-\ts32 subreg_def = reg-\u003esubreg_def;\n-\n \tmemset(reg, 0, sizeof(*reg));\n \treg-\u003etype = SCALAR_VALUE;\n \treg-\u003evar_off = tnum_unknown;\n-\treg-\u003esubreg_def = subreg_def;\n \t__mark_reg_unbounded(reg);\n }\n \n@@ -2213,7 +2210,6 @@ static int mark_btf_ld_reg(struct bpf_verifier_env *env,\n \t}\n }\n \n-#define DEF_NOT_SUBREG\t(0)\n static void init_reg_state(struct bpf_verifier_env *env,\n \t\t\t   struct bpf_func_state *state)\n {\n@@ -2222,7 +2218,6 @@ static void init_reg_state(struct bpf_verifier_env *env,\n \n \tfor (i = 0; i \u003c MAX_BPF_REG; i++) {\n \t\tbpf_mark_reg_not_init(env, \u0026regs[i]);\n-\t\tregs[i].subreg_def = DEF_NOT_SUBREG;\n \t}\n \n \t/* frame pointer */\n@@ -3058,120 +3053,14 @@ static void mark_stack_slots_scratched(struct bpf_verifier_env *env,\n \t\tmark_stack_slot_scratched(env, spi - i);\n }\n \n-/* This function is supposed to be used by the following 32-bit optimization\n- * code only. It returns TRUE if the source or destination register operates\n- * on 64-bit, otherwise return FALSE.\n- */\n-bool bpf_is_reg64(struct bpf_insn *insn,\n-\t      u32 regno, struct bpf_reg_state *reg, enum bpf_reg_arg_type t)\n-{\n-\tu8 code, class, op;\n-\n-\tcode = insn-\u003ecode;\n-\tclass = BPF_CLASS(code);\n-\top = BPF_OP(code);\n-\tif (class == BPF_JMP) {\n-\t\t/* BPF_EXIT for \"main\" will reach here. Return TRUE\n-\t\t * conservatively.\n-\t\t */\n-\t\tif (op == BPF_EXIT)\n-\t\t\treturn true;\n-\t\tif (op == BPF_CALL) {\n-\t\t\t/* BPF to BPF call will reach here because of marking\n-\t\t\t * caller saved clobber with DST_OP_NO_MARK for which we\n-\t\t\t * don't care the register def because they are anyway\n-\t\t\t * marked as NOT_INIT already.\n-\t\t\t */\n-\t\t\tif (insn-\u003esrc_reg == BPF_PSEUDO_CALL)\n-\t\t\t\treturn false;\n-\t\t\t/* Helper call will reach here because of arg type\n-\t\t\t * check, conservatively return TRUE.\n-\t\t\t */\n-\t\t\tif (t == SRC_OP)\n-\t\t\t\treturn true;\n-\n-\t\t\treturn false;\n-\t\t}\n-\t}\n-\n-\tif (class == BPF_ALU64 \u0026\u0026 op == BPF_END \u0026\u0026 (insn-\u003eimm == 16 || insn-\u003eimm == 32))\n-\t\treturn false;\n-\n-\tif (class == BPF_ALU64 || class == BPF_JMP ||\n-\t    (class == BPF_ALU \u0026\u0026 op == BPF_END \u0026\u0026 insn-\u003eimm == 64))\n-\t\treturn true;\n-\n-\tif (class == BPF_ALU || class == BPF_JMP32)\n-\t\treturn false;\n-\n-\tif (class == BPF_LDX) {\n-\t\tif (t != SRC_OP)\n-\t\t\treturn BPF_SIZE(code) == BPF_DW || BPF_MODE(code) == BPF_MEMSX;\n-\t\t/* LDX source must be ptr. */\n-\t\treturn true;\n-\t}\n-\n-\tif (class == BPF_STX) {\n-\t\t/* BPF_STX (including atomic variants) has one or more source\n-\t\t * operands, one of which is a ptr. Check whether the caller is\n-\t\t * asking about it.\n-\t\t */\n-\t\tif (t == SRC_OP \u0026\u0026 reg-\u003etype != SCALAR_VALUE)\n-\t\t\treturn true;\n-\t\treturn BPF_SIZE(code) == BPF_DW;\n-\t}\n-\n-\tif (class == BPF_LD) {\n-\t\tu8 mode = BPF_MODE(code);\n-\n-\t\t/* LD_IMM64 */\n-\t\tif (mode == BPF_IMM)\n-\t\t\treturn true;\n-\n-\t\t/* Both LD_IND and LD_ABS return 32-bit data. */\n-\t\tif (t != SRC_OP)\n-\t\t\treturn  false;\n-\n-\t\t/* Implicit ctx ptr. */\n-\t\tif (regno == BPF_REG_6)\n-\t\t\treturn true;\n-\n-\t\t/* Explicit source could be any width. */\n-\t\treturn true;\n-\t}\n-\n-\tif (class == BPF_ST)\n-\t\t/* The only source register for BPF_ST is a ptr. */\n-\t\treturn true;\n-\n-\t/* Conservatively return true at default. */\n-\treturn true;\n-}\n-\n-static void mark_insn_zext(struct bpf_verifier_env *env,\n-\t\t\t   struct bpf_reg_state *reg)\n-{\n-\ts32 def_idx = reg-\u003esubreg_def;\n-\n-\tif (def_idx == DEF_NOT_SUBREG)\n-\t\treturn;\n-\n-\tenv-\u003einsn_aux_data[def_idx - 1].zext_dst = true;\n-\t/* The dst will be zero extended, so won't be sub-register anymore. */\n-\treg-\u003esubreg_def = DEF_NOT_SUBREG;\n-}\n-\n static int __check_reg_arg(struct bpf_verifier_env *env, struct bpf_reg_state *regs, u32 regno,\n \t\t\t   enum bpf_reg_arg_type t)\n {\n-\tstruct bpf_insn *insn = env-\u003eprog-\u003einsnsi + env-\u003einsn_idx;\n \tstruct bpf_reg_state *reg;\n-\tbool rw64;\n \n \tmark_reg_scratched(env, regno);\n \n \treg = \u0026regs[regno];\n-\trw64 = bpf_is_reg64(insn, regno, reg, t);\n \tif (t == SRC_OP) {\n \t\t/* check whether register used as source operand can be read */\n \t\tif (reg-\u003etype == NOT_INIT) {\n@@ -3182,9 +3071,6 @@ static int __check_reg_arg(struct bpf_verifier_env *env, struct bpf_reg_state *r\n \t\tif (regno == BPF_REG_FP)\n \t\t\treturn 0;\n \n-\t\tif (rw64)\n-\t\t\tmark_insn_zext(env, reg);\n-\n \t\treturn 0;\n \t} else {\n \t\t/* check whether register used as dest operand can be written to */\n@@ -3192,7 +3078,6 @@ static int __check_reg_arg(struct bpf_verifier_env *env, struct bpf_reg_state *r\n \t\t\tverbose(env, \"frame pointer is read only\\n\");\n \t\t\treturn -EACCES;\n \t\t}\n-\t\treg-\u003esubreg_def = rw64 ? DEF_NOT_SUBREG : env-\u003einsn_idx + 1;\n \t\tif (t == DST_OP)\n \t\t\tmark_reg_unknown(env, regs, regno);\n \t}\n@@ -3848,11 +3733,6 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,\n \n \t\t\tif (size \u003c= spill_size \u0026\u0026\n \t\t\t    bpf_stack_narrow_access_ok(off, size, spill_size)) {\n-\t\t\t\t/* The earlier check_reg_arg() has decided the\n-\t\t\t\t * subreg_def for this insn.  Save it first.\n-\t\t\t\t */\n-\t\t\t\ts32 subreg_def = state-\u003eregs[dst_regno].subreg_def;\n-\n \t\t\t\tif (env-\u003ebpf_capable \u0026\u0026 size == 4 \u0026\u0026 spill_size == 4 \u0026\u0026\n \t\t\t\t    get_reg_width(reg) \u003c= 32)\n \t\t\t\t\t/* Ensure stack slot has an ID to build a relation\n@@ -3860,7 +3740,6 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,\n \t\t\t\t\t */\n \t\t\t\t\tassign_scalar_id_before_mov(env, reg);\n \t\t\t\tstate-\u003eregs[dst_regno] = *reg;\n-\t\t\t\tstate-\u003eregs[dst_regno].subreg_def = subreg_def;\n \n \t\t\t\t/* Break the relation on a narrowing fill.\n \t\t\t\t * coerce_reg_to_size will adjust the boundaries.\n@@ -6311,12 +6190,6 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b\n \t\t\t} else {\n \t\t\t\tmark_reg_known_zero(env, regs,\n \t\t\t\t\t\t    value_regno);\n-\t\t\t\t/* A load of ctx field could have different\n-\t\t\t\t * actual load size with the one encoded in the\n-\t\t\t\t * insn. When the dst is PTR, it is for sure not\n-\t\t\t\t * a sub-register.\n-\t\t\t\t */\n-\t\t\t\tregs[value_regno].subreg_def = DEF_NOT_SUBREG;\n \t\t\t\tif (base_type(info.reg_type) == PTR_TO_BTF_ID) {\n \t\t\t\t\tregs[value_regno].btf = info.btf;\n \t\t\t\t\tregs[value_regno].btf_id = info.btf_id;\n@@ -7415,10 +7288,6 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat\n \t\t\tif (spi \u003c 0)\n \t\t\t\treturn spi;\n \n-\t\t\t/*\n-\t\t\t * For CONST_PTR_TO_DYNPTR, reg is already scratched by check_reg_arg\n-\t\t\t * in check_helper_call and mark_btf_func_reg_size in check_kfunc_call.\n-\t\t\t */\n \t\t\tmark_stack_slots_scratched(env, spi, BPF_DYNPTR_NR_SLOTS);\n \n \t\t\treg = \u0026state-\u003estack[spi].spilled_ptr;\n@@ -9522,7 +9391,6 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n \t\t/* All non-void global functions return a 64-bit SCALAR_VALUE. */\n \t\tif (!subprog_returns_void(env, subprog)) {\n \t\t\tmark_reg_unknown(env, caller-\u003eregs, BPF_REG_0);\n-\t\t\tcaller-\u003eregs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG;\n \t\t}\n \n \t\tif (env-\u003esubprog_info[subprog].might_throw) {\n@@ -10542,9 +10410,6 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn\n \t}\n \tinvalidate_outgoing_stack_args(env, cur_func(env));\n \n-\t/* helper call returns 64-bit value. */\n-\tregs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG;\n-\n \t/* update return register (already marked as written above) */\n \tret_type = fn-\u003eret_type;\n \tret_flag = type_flag(ret_type);\n@@ -10784,30 +10649,6 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn\n \treturn 0;\n }\n \n-/* mark_btf_func_reg_size() is used when the reg size is determined by\n- * the BTF func_proto's return value size and argument.\n- */\n-static void __mark_btf_func_reg_size(struct bpf_verifier_env *env, struct bpf_reg_state *regs,\n-\t\t\t\t     u32 regno, size_t reg_size)\n-{\n-\tstruct bpf_reg_state *reg = \u0026regs[regno];\n-\n-\tif (regno == BPF_REG_0) {\n-\t\t/* Function return value */\n-\t\treg-\u003esubreg_def = reg_size == sizeof(u64) ?\n-\t\t\tDEF_NOT_SUBREG : env-\u003einsn_idx + 1;\n-\t} else if (reg_size == sizeof(u64)) {\n-\t\t/* Function argument */\n-\t\tmark_insn_zext(env, reg);\n-\t}\n-}\n-\n-static void mark_btf_func_reg_size(struct bpf_verifier_env *env, u32 regno,\n-\t\t\t\t   size_t reg_size)\n-{\n-\treturn __mark_btf_func_reg_size(env, cur_regs(env), regno, reg_size);\n-}\n-\n static bool is_kfunc_acquire(struct bpf_call_arg_meta *meta)\n {\n \treturn meta-\u003ekfunc_flags \u0026 KF_ACQUIRE;\n@@ -13026,7 +12867,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n \tstruct bpf_call_arg_meta meta;\n \tstruct bpf_insn_aux_data *insn_aux;\n \tint err, insn_idx = *insn_idx_p;\n-\tconst struct btf_param *args;\n \tu32 i, nargs, ptr_type_id;\n \tstruct bpf_kfunc_desc *desc;\n \tstruct btf *desc_btf;\n@@ -13078,7 +12918,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n \t\t\tverbose(env, \"failed to mark s32 range for retval in forked state for lock\\n\");\n \t\t\treturn err;\n \t\t}\n-\t\t__mark_btf_func_reg_size(env, regs, BPF_REG_0, sizeof(u32));\n \t} else if (!insn-\u003eoff \u0026\u0026 insn-\u003eimm == special_kfunc_list[KF___bpf_trap]) {\n \t\tverbose(env, \"unexpected __bpf_trap() due to uninitialized variable?\\n\");\n \t\treturn -EFAULT;\n@@ -13231,7 +13070,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n \t\tu32 regno = caller_saved[i];\n \n \t\tbpf_mark_reg_not_init(env, \u0026regs[regno]);\n-\t\tregs[regno].subreg_def = DEF_NOT_SUBREG;\n \t}\n \tinvalidate_outgoing_stack_args(env, cur_func(env));\n \n@@ -13253,7 +13091,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n \t\tif (meta.btf == btf_vmlinux \u0026\u0026 (meta.func_id == special_kfunc_list[KF_bpf_res_spin_lock] ||\n \t\t    meta.func_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave]))\n \t\t\t__mark_reg_const_zero(env, \u0026regs[BPF_REG_0]);\n-\t\tmark_btf_func_reg_size(env, BPF_REG_0, t-\u003esize);\n \t} else if (btf_type_is_ptr(t)) {\n \t\tptr_type = btf_type_skip_modifiers(desc_btf, t-\u003etype, \u0026ptr_type_id);\n \t\terr = check_special_kfunc(env, \u0026meta, regs, insn_aux, ptr_type, desc_btf);\n@@ -13344,7 +13181,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n \t\t\t/* For mark_ptr_or_null_reg, see 93c230e3f5bd6 */\n \t\t\tregs[BPF_REG_0].id = ++env-\u003eid_gen;\n \t\t}\n-\t\tmark_btf_func_reg_size(env, BPF_REG_0, sizeof(void *));\n \t\tif (is_kfunc_acquire(\u0026meta)) {\n \t\t\tid = acquire_reference(env, insn_idx, 0);\n \t\t\tif (id \u003c 0)\n@@ -13381,18 +13217,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n \t\t\tcaller_info-\u003estack_arg_cnt = stack_arg_cnt;\n \t}\n \n-\targs = (const struct btf_param *)(meta.func_proto + 1);\n-\tfor (i = 0; i \u003c min_t(int, nargs, MAX_BPF_FUNC_REG_ARGS); i++) {\n-\t\tu32 regno = i + 1;\n-\n-\t\tt = btf_type_skip_modifiers(desc_btf, args[i].type, NULL);\n-\t\tif (btf_type_is_ptr(t))\n-\t\t\tmark_btf_func_reg_size(env, regno, sizeof(void *));\n-\t\telse\n-\t\t\t/* scalar. ensured by check_kfunc_args() */\n-\t\t\tmark_btf_func_reg_size(env, regno, t-\u003esize);\n-\t}\n-\n \tif (bpf_is_iter_next_kfunc(\u0026meta)) {\n \t\terr = process_iter_next_call(env, insn_idx, \u0026meta);\n \t\tif (err)\n@@ -14886,14 +14710,14 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env,\n \t\tif (dst_reg-\u003etype != PTR_TO_ARENA)\n \t\t\t*dst_reg = *src_reg;\n \n-\t\tdst_reg-\u003esubreg_def = env-\u003einsn_idx + 1;\n-\n-\t\tif (BPF_CLASS(insn-\u003ecode) == BPF_ALU64)\n+\t\tif (BPF_CLASS(insn-\u003ecode) == BPF_ALU64) {\n \t\t\t/*\n \t\t\t * 32-bit operations zero upper bits automatically.\n \t\t\t * 64-bit operations need to be converted to 32.\n \t\t\t */\n \t\t\taux-\u003eneeds_zext = true;\n+\t\t\taux-\u003ezext_dst = true;\n+\t\t}\n \n \t\t/* Any arithmetic operations are allowed on arena pointers */\n \t\treturn 0;\n@@ -15089,18 +14913,14 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)\n \t\t\t\tif (insn-\u003eimm) {\n \t\t\t\t\t/* off == BPF_ADDR_SPACE_CAST */\n \t\t\t\t\tmark_reg_unknown(env, regs, insn-\u003edst_reg);\n-\t\t\t\t\tif (insn-\u003eimm == 1) { /* cast from as(1) to as(0) */\n+\t\t\t\t\tif (insn-\u003eimm == 1) /* cast from as(1) to as(0) */\n \t\t\t\t\t\tdst_reg-\u003etype = PTR_TO_ARENA;\n-\t\t\t\t\t\t/* PTR_TO_ARENA is 32-bit */\n-\t\t\t\t\t\tdst_reg-\u003esubreg_def = env-\u003einsn_idx + 1;\n-\t\t\t\t\t}\n \t\t\t\t} else if (insn-\u003eoff == 0) {\n \t\t\t\t\t/* case: R1 = R2\n \t\t\t\t\t * copy register state to dest reg\n \t\t\t\t\t */\n \t\t\t\t\tassign_scalar_id_before_mov(env, src_reg);\n \t\t\t\t\t*dst_reg = *src_reg;\n-\t\t\t\t\tdst_reg-\u003esubreg_def = DEF_NOT_SUBREG;\n \t\t\t\t} else {\n \t\t\t\t\t/* case: R1 = (s8, s16 s32)R2 */\n \t\t\t\t\tif (is_pointer_value(env, insn-\u003esrc_reg)) {\n@@ -15118,7 +14938,6 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)\n \t\t\t\t\t\tif (!no_sext)\n \t\t\t\t\t\t\tclear_scalar_id(dst_reg);\n \t\t\t\t\t\tcoerce_reg_to_size_sx(dst_reg, insn-\u003eoff \u003e\u003e 3);\n-\t\t\t\t\t\tdst_reg-\u003esubreg_def = DEF_NOT_SUBREG;\n \t\t\t\t\t} else {\n \t\t\t\t\t\tmark_reg_unknown(env, regs, insn-\u003edst_reg);\n \t\t\t\t\t}\n@@ -15143,7 +14962,6 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)\n \t\t\t\t\t\t */\n \t\t\t\t\t\tif (!is_src_reg_u32)\n \t\t\t\t\t\t\tclear_scalar_id(dst_reg);\n-\t\t\t\t\t\tdst_reg-\u003esubreg_def = env-\u003einsn_idx + 1;\n \t\t\t\t\t} else {\n \t\t\t\t\t\t/* case: W1 = (s8, s16)W2 */\n \t\t\t\t\t\tbool no_sext = reg_umax(src_reg) \u003c (1ULL \u003c\u003c (insn-\u003eoff - 1));\n@@ -15153,7 +14971,6 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)\n \t\t\t\t\t\t*dst_reg = *src_reg;\n \t\t\t\t\t\tif (!no_sext)\n \t\t\t\t\t\t\tclear_scalar_id(dst_reg);\n-\t\t\t\t\t\tdst_reg-\u003esubreg_def = env-\u003einsn_idx + 1;\n \t\t\t\t\t\tcoerce_subreg_to_size_sx(dst_reg, insn-\u003eoff \u003e\u003e 3);\n \t\t\t\t\t}\n \t\t\t\t} else {\n@@ -16022,12 +15839,8 @@ static void sync_linked_regs(struct bpf_verifier_env *env, struct bpf_verifier_s\n \t\t\tcontinue;\n \t\tif ((!(reg-\u003eid \u0026 BPF_ADD_CONST) \u0026\u0026 !(known_reg-\u003eid \u0026 BPF_ADD_CONST)) ||\n \t\t    reg-\u003edelta == known_reg-\u003edelta) {\n-\t\t\ts32 saved_subreg_def = reg-\u003esubreg_def;\n-\n \t\t\t*reg = *known_reg;\n-\t\t\treg-\u003esubreg_def = saved_subreg_def;\n \t\t} else {\n-\t\t\ts32 saved_subreg_def = reg-\u003esubreg_def;\n \t\t\ts32 saved_off = reg-\u003edelta;\n \t\t\tu32 saved_id = reg-\u003eid;\n \n@@ -16037,12 +15850,11 @@ static void sync_linked_regs(struct bpf_verifier_env *env, struct bpf_verifier_s\n \t\t\t/* reg = known_reg; reg += delta */\n \t\t\t*reg = *known_reg;\n \t\t\t/*\n-\t\t\t * Must preserve off, id and subreg_def flag,\n-\t\t\t * otherwise another sync_linked_regs() will be incorrect.\n+\t\t\t * Must preserve off and id, otherwise another sync_linked_regs()\n+\t\t\t * will be incorrect.\n \t\t\t */\n \t\t\treg-\u003edelta = saved_off;\n \t\t\treg-\u003eid = saved_id;\n-\t\t\treg-\u003esubreg_def = saved_subreg_def;\n \n \t\t\tscalar32_min_max_add(reg, \u0026fake_reg);\n \t\t\tscalar_min_max_add(reg, \u0026fake_reg);\n@@ -16477,8 +16289,6 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)\n \t * Already marked as written above.\n \t */\n \tmark_reg_unknown(env, regs, BPF_REG_0);\n-\t/* ld_abs load up to 32-bit skb data. */\n-\tregs[BPF_REG_0].subreg_def = env-\u003einsn_idx + 1;\n \t/*\n \t * See bpf_gen_ld_abs() which emits a hidden BPF_EXIT with r0=0\n \t * which must be explored by the verifier when in a subprog.\n@@ -16793,7 +16603,6 @@ bool bpf_get_call_summary(struct bpf_verifier_env *env, struct bpf_insn *call,\n \tint i;\n \n \tif (bpf_helper_call(call)) {\n-\n \t\tif (bpf_get_helper_proto(env, call-\u003eimm, \u0026fn) \u003c 0)\n \t\t\t/* error would be reported later */\n \t\t\treturn false;\n@@ -17538,6 +17347,7 @@ static int do_check(struct bpf_verifier_env *env)\n \t\t\tenv-\u003eprev_log_pos = env-\u003elog.end_pos;\n \t\t\tverbose(env, \"%d: \", env-\u003einsn_idx);\n \t\t\tbpf_verbose_insn(env, insn);\n+\t\t\tverbose(env, \"\\n\");\n \t\t\tenv-\u003eprev_insn_print_pos = env-\u003elog.end_pos - env-\u003eprev_log_pos;\n \t\t\tenv-\u003eprev_log_pos = env-\u003elog.end_pos;\n \t\t}\ndiff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c\nindex 5e7cb8b36fef2..5579173a61e3b 100644\n--- a/tools/bpf/bpftool/xlated_dumper.c\n+++ b/tools/bpf/bpftool/xlated_dumper.c\n@@ -107,14 +107,7 @@ print_insn_for_graph(void *private_data, const char *fmt, ...)\n \n \tp = buf;\n \twhile (*p != '\\0') {\n-\t\tif (*p == '\\n') {\n-\t\t\tmemmove(p + 3, p, strlen(buf) + 1 - (p - buf));\n-\t\t\t/* Align each instruction dump row left. */\n-\t\t\t*p++ = '\\\\';\n-\t\t\t*p++ = 'l';\n-\t\t\t/* Output multiline concatenation. */\n-\t\t\t*p++ = '\\\\';\n-\t\t} else if (*p == '\u003c' || *p == '\u003e' || *p == '|' || *p == '\u0026') {\n+\t\tif (*p == '\u003c' || *p == '\u003e' || *p == '|' || *p == '\u0026') {\n \t\t\tmemmove(p + 1, p, strlen(buf) + 1 - (p - buf));\n \t\t\t/* Escape special character. */\n \t\t\t*p++ = '\\\\';\n@@ -129,16 +122,10 @@ print_insn_for_graph(void *private_data, const char *fmt, ...)\n static void __printf(2, 3)\n print_insn_json(void *private_data, const char *fmt, ...)\n {\n-\tunsigned int l = strlen(fmt);\n-\tchar chomped_fmt[l];\n \tva_list args;\n \n \tva_start(args, fmt);\n-\tif (l \u003e 0) {\n-\t\tstrncpy(chomped_fmt, fmt, l - 1);\n-\t\tchomped_fmt[l - 1] = '\\0';\n-\t}\n-\tjsonw_vprintf_enquote(json_wtr, chomped_fmt, args);\n+\tjsonw_vprintf_enquote(json_wtr, fmt, args);\n \tva_end(args);\n }\n \n@@ -351,6 +338,7 @@ void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,\n \n \t\tprintf(\"%4u: \", i);\n \t\tprint_bpf_insn(\u0026cbs, insn + i, true);\n+\t\tprintf(\"\\n\");\n \n \t\tif (opcodes) {\n \t\t\tprintf(\"       \");\n@@ -417,6 +405,7 @@ void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,\n \n \t\tprintf(\"%u: \", insn_off);\n \t\tprint_bpf_insn(\u0026cbs, cur, true);\n+\t\tprintf(\"\\\\l\\\\\\n\");\n \n \t\tif (opcodes) {\n \t\t\tprintf(\"\\\\ \\\\ \\\\ \\\\ \");\ndiff --git a/tools/testing/selftests/bpf/disasm_helpers.c b/tools/testing/selftests/bpf/disasm_helpers.c\nindex f529f1c8c171d..30221352568d7 100644\n--- a/tools/testing/selftests/bpf/disasm_helpers.c\n+++ b/tools/testing/selftests/bpf/disasm_helpers.c\n@@ -55,10 +55,9 @@ struct bpf_insn *disasm_insn(struct bpf_insn *insn, char *buf, size_t buf_sz)\n \t * for each instruction (FF stands for instruction `code` byte).\n \t * Remove the prefix inplace, and also simplify call instructions.\n \t * E.g.: \"(85) call foo#10\" -\u003e \"call foo\".\n-\t * Also remove newline in the end (the 'max(strlen(buf) - 1, 0)' thing).\n \t */\n \tpfx_end = buf + 5;\n-\tsfx_start = buf + max((int)strlen(buf) - 1, 0);\n+\tsfx_start = buf + (int)strlen(buf);\n \tif (strncmp(pfx_end, \"call \", 5) == 0 \u0026\u0026 (tmp = strrchr(buf, '#')))\n \t\tsfx_start = tmp;\n \tlen = sfx_start - pfx_end;\ndiff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c\nindex b79bafca68f7a..0baa74618fa03 100644\n--- a/tools/testing/selftests/bpf/prog_tests/verifier.c\n+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c\n@@ -126,6 +126,7 @@\n #include \"verifier_jit_inline.skel.h\"\n #include \"irq.skel.h\"\n #include \"verifier_ctx_ptr_param.skel.h\"\n+#include \"verifier_zext.skel.h\"\n \n #define MAX_ENTRIES 11\n \n@@ -281,6 +282,7 @@ void test_irq(void)\t\t\t      { RUN(irq); }\n void test_verifier_mtu(void)\t\t      { RUN(verifier_mtu); }\n void test_verifier_jit_inline(void)               { RUN(verifier_jit_inline); }\n void test_verifier_ctx_ptr_param(void)       { RUN(verifier_ctx_ptr_param); }\n+void test_verifier_zext(void)                 { RUN_TESTS(verifier_zext); }\n \n static int init_test_val_map(struct bpf_object *obj, char *map_name)\n {\ndiff --git a/tools/testing/selftests/bpf/progs/verifier_zext.c b/tools/testing/selftests/bpf/progs/verifier_zext.c\nnew file mode 100644\nindex 0000000000000..8f2362da91d69\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/verifier_zext.c\n@@ -0,0 +1,392 @@\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 \u003cbpf_arena_common.h\u003e\n+#include \u003cbpf/bpf_core_read.h\u003e\n+#include \"bpf_misc.h\"\n+\n+struct {\n+\t__uint(type, BPF_MAP_TYPE_ARENA);\n+\t__uint(map_flags, BPF_F_MMAPABLE | BPF_F_NO_USER_CONV);\n+\t__uint(max_entries, 1);\n+} arena SEC(\".maps\");\n+\n+extern long bpf_kfunc_call_test4(signed char a, short b, int c, long d) __ksym;\n+\n+/* to retain debug info for BTF generation */\n+void __kfunc_btf_root(void)\n+{\n+\tbpf_kfunc_call_test4(0, 0, 0, 0);\n+\tbpf_arena_alloc_pages(0, 0, 0, 0, 0);\n+\tbpf_rdonly_cast(0, 0);\n+}\n+\n+SEC(\"socket\")\n+__flag(BPF_F_TEST_STATE_FREQ)\n+__flag(BPF_F_TEST_RND_HI32)\n+__success __retval(0)\n+__naked void zext_lost_across_checkpoint(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\t\t\t\t\\\n+\tcall %[bpf_ktime_get_ns];\t\t\t\t\t\t\t\\\n+\tr8 = r0;\t\t\t\t\t\t\t\t\t\\\n+\tr6 = 0xdeadbeefcafebabe ll;\t/* inject some value for r6's upper half */\t\\\n+\tif r8 != 0 goto 1f;\t\t/* fall-through cached first, branch pruned */\t\\\n+\tr6 = 32;\t\t\t/* full 64-bit def */\t\t\t\t\\\n+\tgoto 2f;\t\t\t\t\t\t\t\t\t\\\n+1:\tw6 = 32;\t\t\t/* 32-bit def, zext mark lost */\t\t\\\n+2:\tr0 = r6;\t\t\t/* buggy verifier believed upper 32 bits are 0 */ \\\n+\t\t\t\t\t/* and thus did not zero extended w6 = 32. */\t\\\n+\tr0 \u003e\u003e= 32;\t\t\t\t\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\t\t\t\t\\\n+\"\t:\n+\t: __imm(bpf_ktime_get_ns)\n+\t: __clobber_all);\n+}\n+\n+/* 32-bit ALU result read as 64-bit -\u003e zext */\n+SEC(\"socket\")\n+__success __log_level(2)\n+__msg(\"w1 = w0{{ +}}; zext\")\n+__naked void zext_alu32_hi_used(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tcall %[bpf_get_prandom_u32];\t\t\t\\\n+\tw1 = w0;\t\t\t\t\t\\\n+\tr0 = r1;\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t:\n+\t: __imm(bpf_get_prandom_u32)\n+\t: __clobber_all);\n+}\n+\n+/* 32-bit ALU result read only as 32-bit -\u003e no zext */\n+SEC(\"socket\")\n+__success __log_level(2)\n+__not_msg(\"; zext\")\n+__naked void no_zext_alu32_hi_unused(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tcall %[bpf_get_prandom_u32];\t\t\t\\\n+\tw1 = w0;\t\t/* MOV */\t\t\\\n+\tw2 = w1;\t\t\t\t\t\\\n+\tw2 += w1;\t\t/* ALU32, BPF_X */\t\\\n+\tw2 += 1;\t\t/* ALU32, BPF_K */\t\\\n+\tw2 = w2;\t\t/* keep w2 alive for previous instruction */ \\\n+\tr0 = 0;\t\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t:\n+\t: __imm(bpf_get_prandom_u32)\n+\t: __clobber_all);\n+}\n+\n+/* 64-bit definition is never zero extended */\n+SEC(\"socket\")\n+__success __log_level(2)\n+__not_msg(\"r1 = r0{{.*}}; zext\")\n+__naked void no_zext_mov64(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tcall %[bpf_get_prandom_u32];\t\t\t\\\n+\tr1 = r0;\t\t\t\t\t\\\n+\tr0 = r1;\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t:\n+\t: __imm(bpf_get_prandom_u32)\n+\t: __clobber_all);\n+}\n+\n+/* Narrow load result read as 64-bit -\u003e zext */\n+SEC(\"socket\")\n+__success __log_level(2)\n+__msg(\"r1 = *(u32 *)(r10 -8){{ +}}; zext\")\n+__naked void zext_narrow_load_hi_used(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tr0 = 0;\t\t\t\t\t\t\\\n+\t*(u64 *)(r10 - 8) = r0;\t\t\t\t\\\n+\tr1 = *(u32 *)(r10 - 8);\t\t\t\t\\\n+\tr0 = r1;\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t::: __clobber_all);\n+}\n+\n+/* 32-bit atomic fetch result read as 64-bit -\u003e zext */\n+SEC(\"socket\")\n+__success __log_level(2)\n+__msg(\"r1 = atomic_fetch_add((u32 *)(r10 -8), r1){{ +}}; zext\")\n+__naked void zext_atomic_fetch32_hi_used(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tr1 = 0;\t\t\t\t\t\t\\\n+\t*(u64 *)(r10 - 8) = r1;\t\t\t\t\\\n+\tw1 = 1;\t\t\t\t\t\t\\\n+\t.8byte %[fetch_add32];\t\t\t\t\\\n+\tr0 = r1;\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t:\n+\t: __imm_insn(fetch_add32,\n+\t\t     BPF_ATOMIC_OP(BPF_W, BPF_ADD | BPF_FETCH, BPF_REG_10, BPF_REG_1, -8))\n+\t: __clobber_all);\n+}\n+\n+/* 32-bit atomic cmpxchg result (r0) read as 64-bit -\u003e zext */\n+SEC(\"socket\")\n+__success __log_level(2)\n+__msg(\"r0 = atomic_cmpxchg((u32 *)(r10 -8), r0, r1){{ +}}; zext\")\n+__naked void zext_cmpxchg32_hi_used(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tr1 = 0;\t\t\t\t\t\t\\\n+\t*(u64 *)(r10 - 8) = r1;\t\t\t\t\\\n+\tw0 = 0;\t\t\t\t\t\t\\\n+\tw1 = 1;\t\t\t\t\t\t\\\n+\t.8byte %[cmpxchg32];\t\t\t\t\\\n+\tr2 = r0;\t\t\t\t\t\\\n+\tr0 = r2;\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t:\n+\t: __imm_insn(cmpxchg32,\n+\t\t     BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, BPF_REG_10, BPF_REG_1, -8))\n+\t: __clobber_all);\n+}\n+\n+/* 32-bit def before a branch, upper half used on one branch -\u003e zext */\n+SEC(\"socket\")\n+__success __log_level(2)\n+__msg(\"w6 = 32{{ +}}; zext\")\n+__naked void zext_cfg_hi_used_one_branch(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tcall %[bpf_get_prandom_u32];\t\t\t\\\n+\tw6 = 32;\t\t\t\t\t\\\n+\tif r0 == 0 goto 1f;\t\t\t\t\\\n+\tr0 = r6;\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+1:\tr0 = 0;\t\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t:\n+\t: __imm(bpf_get_prandom_u32)\n+\t: __clobber_all);\n+}\n+\n+/* r1's upper half is dead, so 'w1 = 1' must NOT be marked for zero extension. */\n+SEC(\"socket\")\n+__success __log_level(2)\n+__not_msg(\"w1 = 1{{.*}}; zext\")\n+__naked void no_zext_other_reg_hi_used(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tcall %[bpf_get_prandom_u32];\t\t\t\\\n+\tr6 = r0;\t\t\t\t\t\\\n+\tr6 \u003c\u003c= 32;\t\t\t\t\t\\\n+\tw1 = 1;\t\t\t\t\t\t\\\n+\tr0 = r6;\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t:\n+\t: __imm(bpf_get_prandom_u32)\n+\t: __clobber_all);\n+}\n+\n+/* LD_ABS defines r0; when r0 is read as 64-bit it must be zero extended */\n+SEC(\"socket\")\n+__success __log_level(2)\n+__msg(\"r0 = *(u8 *)skb[0]{{.*}}; zext\")\n+__naked void zext_ld_abs_hi_used(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tr6 = r1;\t\t\t\t\t\\\n+\tr0 = *(u8 *)skb[0];\t\t\t\t\\\n+\tr7 = r0;\t\t\t\t\t\\\n+\tr0 = r7;\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t::: __clobber_all);\n+}\n+\n+/* Helper parameters are read as 64-bit (call_use_mask() fallback) */\n+SEC(\"socket\")\n+__success __log_level(2)\n+__msg(\"w2 = 1{{ +}}; zext\")\n+__naked void helper_param_read_as_64bit(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tr1 = r10;\t\t\t\t\t\\\n+\tr1 += -8;\t\t\t\t\t\\\n+\tw2 = 1;\t\t\t\t\t\t\\\n+\tcall %[bpf_trace_printk];\t\t\t\\\n+\tr0 = 0;\t\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t:\n+\t: __imm(bpf_trace_printk)\n+\t: __clobber_all);\n+}\n+\n+static __used __naked int subprog_reads_arg_as_64bit(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tr0 = r1;\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t::: __clobber_all);\n+}\n+\n+/* subprogram parameters are conservatively read as 64-bit */\n+SEC(\"socket\")\n+__success __log_level(2)\n+__msg(\"w1 = w0{{ +}}; zext\")\n+__naked void subprog_param_read_as_64bit(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tcall %[bpf_get_prandom_u32];\t\t\t\\\n+\tw1 = w0;\t\t\t\t\t\\\n+\tcall subprog_reads_arg_as_64bit;\t\t\\\n+\tr0 = 0;\t\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t:\n+\t: __imm(bpf_get_prandom_u32)\n+\t: __clobber_all);\n+}\n+\n+/* kfunc parameters are zero extended */\n+SEC(\"tc\")\n+__success __log_level(2)\n+__msg(\"w1 = 1{{ +}}; zext\")\n+__msg(\"w2 = 1{{ +}}; zext\")\n+__msg(\"w3 = 1{{ +}}; zext\")\n+__msg(\"w4 = 1{{ +}}; zext\")\n+__naked void kfunc_param_read_per_btf(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tw1 = 1;\t\t\t\t\t\t\\\n+\tw2 = 1;\t\t\t\t\t\t\\\n+\tw3 = 1;\t\t\t\t\t\t\\\n+\tw4 = 1;\t\t\t\t\t\t\\\n+\tcall bpf_kfunc_call_test4;\t\t\t\\\n+\tr0 = 0;\t\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t::: __clobber_all);\n+}\n+\n+SEC(\"socket\")\n+__success __log_level(2)\n+__not_msg(\"; zext\")\n+__naked void alu32_and_32bit_conditional(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tcall %[bpf_get_prandom_u32];\t\t\t\\\n+\tw1 = w0;\t\t\t\t\t\\\n+\tif w1 \u003e 42 goto 1f;\t\t/* BPF_K */\t\\\n+\tw2 = 28;\t\t\t\t\t\\\n+\tif w2 \u003e w1 goto 1f;\t\t/* BPF_X */\t\\\n+\tr0 = 0;\t\t\t\t\t\t\\\n+1:\texit;\t\t\t\t\t\t\\\n+\"\t:\n+\t: __imm(bpf_get_prandom_u32)\n+\t: __clobber_all);\n+}\n+\n+SEC(\"socket\")\n+__success __log_level(2)\n+__msg(\"w1 = w0{{ +}}; zext\")\n+__naked void alu32_and_64bit_conditional(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tcall %[bpf_get_prandom_u32];\t\t\t\\\n+\tw1 = w0;\t\t\t\t\t\\\n+\tif r1 \u003e 42 goto 1f;\t\t/* BPF_K */\t\\\n+\tr2 = 28;\t\t\t\t\t\\\n+\tif r2 \u003e r1 goto 1f;\t\t/* BPF_X */\t\\\n+\tr0 = 0;\t\t\t\t\t\t\\\n+1:\texit;\t\t\t\t\t\t\\\n+\"\t:\n+\t: __imm(bpf_get_prandom_u32)\n+\t: __clobber_all);\n+}\n+\n+SEC(\"socket\")\n+__success __log_level(2)\n+__not_msg(\"; zext\")\n+__naked void alu64_and_conditionals(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tcall %[bpf_get_prandom_u32];\t\t\t\\\n+\tr1 = r0;\t\t\t\t\t\\\n+\tif w1 \u003e 42 goto 1f;\t\t/* BPF_K */\t\\\n+\tif r1 \u003e 42 goto 1f;\t\t/* BPF_K */\t\\\n+\tr2 = 28;\t\t\t\t\t\\\n+\tif w2 \u003e w1 goto 1f;\t\t/* BPF_X */\t\\\n+\tif r2 \u003e r1 goto 1f;\t\t/* BPF_X */\t\\\n+\tr0 = 0;\t\t\t\t\t\t\\\n+1:\texit;\t\t\t\t\t\t\\\n+\"\t:\n+\t: __imm(bpf_get_prandom_u32)\n+\t: __clobber_all);\n+}\n+\n+#ifdef __BPF_FEATURE_ADDR_SPACE_CAST\n+\n+SEC(\"?fentry.s/\" SYS_PREFIX \"sys_getpgid\")\n+__arch_s390x\n+__xlated(\"7: w1 = w0\")\n+__xlated(\"8: w1 = w1\")\n+__xlated(\"9: w1 += 8\")\n+__xlated(\"10: w1 = w1\")\n+__xlated(\"11: w2 = w1\")\n+__xlated(\"12: w2 = w2\")\n+__xlated(\"13: *(u64 *)(r1 +0) = r2\")\n+__naked void arena_ptr(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tr1 = %[arena] ll;\t\t\t\t\\\n+\tr2 = 0;\t\t\t\t\t\t\\\n+\tr3 = 1;\t\t\t\t\t\t\\\n+\tr4 = 0;\t\t\t\t\t\t\\\n+\tr5 = 0;\t\t\t\t\t\t\\\n+\tcall %[bpf_arena_alloc_pages];\t\t\t\\\n+\tr1 = addr_space_cast(r0, 0, 1);\t\t/* needs zext */ \\\n+\tr1 += 8;\t\t\t\t/* needs zext */ \\\n+\tr2 = addr_space_cast(r1, 1, 0);\t\t/* needs zext because of BPF_F_NO_USER_CONV */ \\\n+\t*(u64 *)(r1 +0) = r2;\t\t\t\t\\\n+\tr0 = 0;\t\t\t\t\t\t\\\n+\texit;\t\t\t\t\t\t\\\n+\"\t:\n+\t: __imm(bpf_arena_alloc_pages),\n+\t  __imm_addr(arena)\n+\t: __clobber_all);\n+}\n+\n+#endif\n+\n+/* Check if probe mem loads keep their zero extension. */\n+SEC(\"socket\")\n+__success __log_level(2)\n+__arch_s390x\n+__xlated(\"3: r1 = *(u64 *)(r0 +0)\")\n+__xlated(\"4: r2 = *(u32 *)(r0 +0)\")\n+__xlated(\"5: w2 = w2\")\n+__xlated(\"6: r3 = *(u16 *)(r0 +0)\")\n+__xlated(\"7: w3 = w3\")\n+__xlated(\"8: r4 = *(u8 *)(r0 +0)\")\n+__xlated(\"9: w4 = w4\")\n+__naked void probe_mem(void)\n+{\n+\tasm volatile (\"\t\t\t\t\t\\\n+\tr1 = 0;\t\t\t\t\t\t\\\n+\tr2 = 0;\t\t\t\t\t\t\\\n+\tcall %[bpf_rdonly_cast];\t\t\t\\\n+\tr1 = *(u64 *)(r0 + 0);\t/* BPF_PROBE_MEM */\t\\\n+\tr2 = *(u32 *)(r0 + 0);\t/* BPF_PROBE_MEM */\t\\\n+\tr3 = *(u16 *)(r0 + 0);\t/* BPF_PROBE_MEM */\t\\\n+\tr4 = *(u8 *)(r0 + 0);\t/* BPF_PROBE_MEM */\t\\\n+\tr0 = r1;\t\t/* make the registers used */ \\\n+\tr0 += r2;\t\t\t\t\t\\\n+\tr0 += r3;\t\t\t\t\t\\\n+\tr0 += r4;\t\t\t\t\t\\\n+1:\texit;\t\t\t\t\t\t\\\n+\"\t:\n+\t: __imm(bpf_rdonly_cast)\n+\t: __clobber_all);\n+}\n+\n+char _license[] SEC(\"license\") = \"GPL\";\n"
}

2/1 2026/08/07 21:37 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL"
  ],
  "FocusSymbols": [
    "bpf_insn_def32",
    "bpf_compute_live_registers",
    "bpf_opt_subreg_zext_lo32_rnd_hi32",
    "bpf_do_misc_fixups"
  ],
  "Reasoning": "The patch modifies the BPF verifier to track 32-bit register zero-extension using liveness analysis instead of the `subreg_def` field in `bpf_reg_state`. This is a significant functional change to the core BPF verifier logic, affecting how 32-bit register values are zero-extended to 64-bit. This change is reachable via the `bpf()` syscall when loading BPF programs and is definitely worth fuzzing.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


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

Prompt:
For your convenience, here is the diff of the changes:
commit c811bb7277c87779b883f585fbd26208510a1533
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Aug 7 21:37:06 2026 +0000

    syz-cluster: applied patch under review

diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index a2a40caca0a0e..2c74d676ede97 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -162,11 +162,6 @@ struct bpf_reg_state {
 	 * pointing to bpf_func_state.
 	 */
 	u32 frameno;
-	/* Tracks subreg definition. The stored value is the insn_idx of the
-	 * writing insn. This is safe because subreg_def is used before any insn
-	 * patching which only happens after main verification finished.
-	 */
-	s32 subreg_def;
 	/* if (!precise && SCALAR_VALUE) min/max/tnum don't affect safety */
 	bool precise;
 };
@@ -1637,7 +1632,6 @@ struct bpf_kfunc_desc_tab {
 };
 
 /* Functions exported from verifier.c, used by fixups.c */
-bool bpf_is_reg64(struct bpf_insn *insn, u32 regno, struct bpf_reg_state *reg, enum bpf_reg_arg_type t);
 void bpf_clear_insn_aux_data(struct bpf_verifier_env *env, int start, int len);
 void bpf_mark_subprog_exc_cb(struct bpf_verifier_env *env, int subprog);
 bool bpf_allow_tail_call_in_subprogs(struct bpf_verifier_env *env);
@@ -1661,5 +1655,6 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env);
 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);
 
 #endif /* _LINUX_BPF_VERIFIER_H */
diff --git a/kernel/bpf/backtrack.c b/kernel/bpf/backtrack.c
index 2f473ad4fd7c1..40bd04421a991 100644
--- a/kernel/bpf/backtrack.c
+++ b/kernel/bpf/backtrack.c
@@ -285,6 +285,7 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
 		verbose(env, "stack=%s before ", env->tmp_str_buf);
 		verbose(env, "%d: ", idx);
 		bpf_verbose_insn(env, insn);
+		verbose(env, "\n");
 	}
 
 	/* If there is a history record that some registers gained range at this insn,
diff --git a/kernel/bpf/disasm.c b/kernel/bpf/disasm.c
index 0391b3bc00734..50b3ca5149a0a 100644
--- a/kernel/bpf/disasm.c
+++ b/kernel/bpf/disasm.c
@@ -139,7 +139,7 @@ static void print_bpf_end_insn(bpf_insn_print_t verbose,
 			       void *private_data,
 			       const struct bpf_insn *insn)
 {
-	verbose(private_data, "(%02x) r%d = %s%d r%d\n",
+	verbose(private_data, "(%02x) r%d = %s%d r%d",
 		insn->code, insn->dst_reg,
 		BPF_SRC(insn->code) == BPF_TO_BE ? "be" : "le",
 		insn->imm, insn->dst_reg);
@@ -149,7 +149,7 @@ static void print_bpf_bswap_insn(bpf_insn_print_t verbose,
 			       void *private_data,
 			       const struct bpf_insn *insn)
 {
-	verbose(private_data, "(%02x) r%d = bswap%d r%d\n",
+	verbose(private_data, "(%02x) r%d = bswap%d r%d",
 		insn->code, insn->dst_reg,
 		insn->imm, insn->dst_reg);
 }
@@ -197,19 +197,19 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 			else
 				print_bpf_end_insn(verbose, cbs->private_data, insn);
 		} else if (BPF_OP(insn->code) == BPF_NEG) {
-			verbose(cbs->private_data, "(%02x) %c%d = -%c%d\n",
+			verbose(cbs->private_data, "(%02x) %c%d = -%c%d",
 				insn->code, class == BPF_ALU ? 'w' : 'r',
 				insn->dst_reg, class == BPF_ALU ? 'w' : 'r',
 				insn->dst_reg);
 		} else if (is_addr_space_cast(insn)) {
-			verbose(cbs->private_data, "(%02x) r%d = addr_space_cast(r%d, %u, %u)\n",
+			verbose(cbs->private_data, "(%02x) r%d = addr_space_cast(r%d, %u, %u)",
 				insn->code, insn->dst_reg,
 				insn->src_reg, ((u32)insn->imm) >> 16, (u16)insn->imm);
 		} else if (is_mov_percpu_addr(insn)) {
-			verbose(cbs->private_data, "(%02x) r%d = &(void __percpu *)(r%d)\n",
+			verbose(cbs->private_data, "(%02x) r%d = &(void __percpu *)(r%d)",
 				insn->code, insn->dst_reg, insn->src_reg);
 		} else if (BPF_SRC(insn->code) == BPF_X) {
-			verbose(cbs->private_data, "(%02x) %c%d %s %s%c%d\n",
+			verbose(cbs->private_data, "(%02x) %c%d %s %s%c%d",
 				insn->code, class == BPF_ALU ? 'w' : 'r',
 				insn->dst_reg,
 				is_sdiv_smod(insn) ? bpf_alu_sign_string[BPF_OP(insn->code) >> 4]
@@ -218,7 +218,7 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 				class == BPF_ALU ? 'w' : 'r',
 				insn->src_reg);
 		} else {
-			verbose(cbs->private_data, "(%02x) %c%d %s %d\n",
+			verbose(cbs->private_data, "(%02x) %c%d %s %d",
 				insn->code, class == BPF_ALU ? 'w' : 'r',
 				insn->dst_reg,
 				is_sdiv_smod(insn) ? bpf_alu_sign_string[BPF_OP(insn->code) >> 4]
@@ -227,7 +227,7 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 		}
 	} else if (class == BPF_STX) {
 		if (BPF_MODE(insn->code) == BPF_MEM)
-			verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = r%d\n",
+			verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = r%d",
 				insn->code,
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 				insn->dst_reg,
@@ -235,7 +235,7 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 		else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
 			 (insn->imm == BPF_ADD || insn->imm == BPF_AND ||
 			  insn->imm == BPF_OR || insn->imm == BPF_XOR)) {
-			verbose(cbs->private_data, "(%02x) lock *(%s *)(r%d %+d) %s r%d\n",
+			verbose(cbs->private_data, "(%02x) lock *(%s *)(r%d %+d) %s r%d",
 				insn->code,
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 				insn->dst_reg, insn->off,
@@ -246,7 +246,7 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 			    insn->imm == (BPF_AND | BPF_FETCH) ||
 			    insn->imm == (BPF_OR | BPF_FETCH) ||
 			    insn->imm == (BPF_XOR | BPF_FETCH))) {
-			verbose(cbs->private_data, "(%02x) r%d = atomic%s_fetch_%s((%s *)(r%d %+d), r%d)\n",
+			verbose(cbs->private_data, "(%02x) r%d = atomic%s_fetch_%s((%s *)(r%d %+d), r%d)",
 				insn->code, insn->src_reg,
 				BPF_SIZE(insn->code) == BPF_DW ? "64" : "",
 				bpf_atomic_alu_string[BPF_OP(insn->imm) >> 4],
@@ -254,7 +254,7 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 				insn->dst_reg, insn->off, insn->src_reg);
 		} else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
 			   insn->imm == BPF_CMPXCHG) {
-			verbose(cbs->private_data, "(%02x) r0 = atomic%s_cmpxchg((%s *)(r%d %+d), r0, r%d)\n",
+			verbose(cbs->private_data, "(%02x) r0 = atomic%s_cmpxchg((%s *)(r%d %+d), r0, r%d)",
 				insn->code,
 				BPF_SIZE(insn->code) == BPF_DW ? "64" : "",
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
@@ -262,44 +262,44 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 				insn->src_reg);
 		} else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
 			   insn->imm == BPF_XCHG) {
-			verbose(cbs->private_data, "(%02x) r%d = atomic%s_xchg((%s *)(r%d %+d), r%d)\n",
+			verbose(cbs->private_data, "(%02x) r%d = atomic%s_xchg((%s *)(r%d %+d), r%d)",
 				insn->code, insn->src_reg,
 				BPF_SIZE(insn->code) == BPF_DW ? "64" : "",
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 				insn->dst_reg, insn->off, insn->src_reg);
 		} else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
 			   insn->imm == BPF_LOAD_ACQ) {
-			verbose(cbs->private_data, "(%02x) r%d = load_acquire((%s *)(r%d %+d))\n",
+			verbose(cbs->private_data, "(%02x) r%d = load_acquire((%s *)(r%d %+d))",
 				insn->code, insn->dst_reg,
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 				insn->src_reg, insn->off);
 		} else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
 			   insn->imm == BPF_STORE_REL) {
-			verbose(cbs->private_data, "(%02x) store_release((%s *)(r%d %+d), r%d)\n",
+			verbose(cbs->private_data, "(%02x) store_release((%s *)(r%d %+d), r%d)",
 				insn->code,
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 				insn->dst_reg, insn->off, insn->src_reg);
 		} else {
-			verbose(cbs->private_data, "BUG_%02x\n", insn->code);
+			verbose(cbs->private_data, "BUG_%02x", insn->code);
 		}
 	} else if (class == BPF_ST) {
 		if (BPF_MODE(insn->code) == BPF_MEM) {
-			verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = %d\n",
+			verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = %d",
 				insn->code,
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 				insn->dst_reg,
 				insn->off, insn->imm);
 		} else if (BPF_MODE(insn->code) == 0xc0 /* BPF_NOSPEC, no UAPI */) {
-			verbose(cbs->private_data, "(%02x) nospec\n", insn->code);
+			verbose(cbs->private_data, "(%02x) nospec", insn->code);
 		} else {
-			verbose(cbs->private_data, "BUG_st_%02x\n", insn->code);
+			verbose(cbs->private_data, "BUG_st_%02x", insn->code);
 		}
 	} else if (class == BPF_LDX) {
 		if (BPF_MODE(insn->code) != BPF_MEM && BPF_MODE(insn->code) != BPF_MEMSX) {
-			verbose(cbs->private_data, "BUG_ldx_%02x\n", insn->code);
+			verbose(cbs->private_data, "BUG_ldx_%02x", insn->code);
 			return;
 		}
-		verbose(cbs->private_data, "(%02x) r%d = *(%s *)(r%d %+d)\n",
+		verbose(cbs->private_data, "(%02x) r%d = *(%s *)(r%d %+d)",
 			insn->code, insn->dst_reg,
 			BPF_MODE(insn->code) == BPF_MEM ?
 				 bpf_ldst_string[BPF_SIZE(insn->code) >> 3] :
@@ -307,12 +307,12 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 			insn->src_reg, insn->off);
 	} else if (class == BPF_LD) {
 		if (BPF_MODE(insn->code) == BPF_ABS) {
-			verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[%d]\n",
+			verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[%d]",
 				insn->code,
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 				insn->imm);
 		} else if (BPF_MODE(insn->code) == BPF_IND) {
-			verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[r%d + %d]\n",
+			verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[r%d + %d]",
 				insn->code,
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 				insn->src_reg, insn->imm);
@@ -332,12 +332,12 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 			if (is_ptr && !allow_ptr_leaks)
 				imm = 0;
 
-			verbose(cbs->private_data, "(%02x) r%d = %s\n",
+			verbose(cbs->private_data, "(%02x) r%d = %s",
 				insn->code, insn->dst_reg,
 				__func_imm_name(cbs, insn, imm,
 						tmp, sizeof(tmp)));
 		} else {
-			verbose(cbs->private_data, "BUG_ld_%02x\n", insn->code);
+			verbose(cbs->private_data, "BUG_ld_%02x", insn->code);
 			return;
 		}
 	} else if (class == BPF_JMP32 || class == BPF_JMP) {
@@ -347,35 +347,35 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 			char tmp[64];
 
 			if (insn->src_reg == BPF_PSEUDO_CALL) {
-				verbose(cbs->private_data, "(%02x) call pc%s\n",
+				verbose(cbs->private_data, "(%02x) call pc%s",
 					insn->code,
 					__func_get_name(cbs, insn,
 							tmp, sizeof(tmp)));
 			} else {
 				strcpy(tmp, "unknown");
-				verbose(cbs->private_data, "(%02x) call %s#%d\n", insn->code,
+				verbose(cbs->private_data, "(%02x) call %s#%d", insn->code,
 					__func_get_name(cbs, insn,
 							tmp, sizeof(tmp)),
 					insn->imm);
 			}
 		} else if (insn->code == (BPF_JMP | BPF_JA)) {
-			verbose(cbs->private_data, "(%02x) goto pc%+d\n",
+			verbose(cbs->private_data, "(%02x) goto pc%+d",
 				insn->code, insn->off);
 		} else if (insn->code == (BPF_JMP | BPF_JA | BPF_X)) {
-			verbose(cbs->private_data, "(%02x) gotox r%d\n",
+			verbose(cbs->private_data, "(%02x) gotox r%d",
 				insn->code, insn->dst_reg);
 		} else if (insn->code == (BPF_JMP | BPF_JCOND) &&
 			   insn->src_reg == BPF_MAY_GOTO) {
-			verbose(cbs->private_data, "(%02x) may_goto pc%+d\n",
+			verbose(cbs->private_data, "(%02x) may_goto pc%+d",
 				insn->code, insn->off);
 		} else if (insn->code == (BPF_JMP32 | BPF_JA)) {
-			verbose(cbs->private_data, "(%02x) gotol pc%+d\n",
+			verbose(cbs->private_data, "(%02x) gotol pc%+d",
 				insn->code, insn->imm);
 		} else if (insn->code == (BPF_JMP | BPF_EXIT)) {
-			verbose(cbs->private_data, "(%02x) exit\n", insn->code);
+			verbose(cbs->private_data, "(%02x) exit", insn->code);
 		} else if (BPF_SRC(insn->code) == BPF_X) {
 			verbose(cbs->private_data,
-				"(%02x) if %c%d %s %c%d goto pc%+d\n",
+				"(%02x) if %c%d %s %c%d goto pc%+d",
 				insn->code, class == BPF_JMP32 ? 'w' : 'r',
 				insn->dst_reg,
 				bpf_jmp_string[BPF_OP(insn->code) >> 4],
@@ -383,14 +383,14 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 				insn->src_reg, insn->off);
 		} else {
 			verbose(cbs->private_data,
-				"(%02x) if %c%d %s 0x%x goto pc%+d\n",
+				"(%02x) if %c%d %s 0x%x goto pc%+d",
 				insn->code, class == BPF_JMP32 ? 'w' : 'r',
 				insn->dst_reg,
 				bpf_jmp_string[BPF_OP(insn->code) >> 4],
 				(u32)insn->imm, insn->off);
 		}
 	} else {
-		verbose(cbs->private_data, "(%02x) %s\n",
+		verbose(cbs->private_data, "(%02x) %s",
 			insn->code, bpf_class_string[class]);
 	}
 }
diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
index a0bddada7964e..661e2d13a6042 100644
--- a/kernel/bpf/fixups.c
+++ b/kernel/bpf/fixups.c
@@ -20,6 +20,26 @@ static bool is_cmpxchg_insn(const struct bpf_insn *insn)
 	       insn->imm == BPF_CMPXCHG;
 }
 
+/* Returns true if 'insn' is an address space cast instruction translated as BPF_ALU op */
+static bool is_addr_space_cast32(struct bpf_prog *prog, const struct bpf_insn *insn)
+{
+	struct bpf_map *arena = (struct bpf_map *)prog->aux->arena;
+
+	if (insn->code != (BPF_ALU64 | BPF_MOV | BPF_X) || insn->off != BPF_ADDR_SPACE_CAST)
+		return false;
+
+	/* cast from as(1) to as(0) */
+	if (insn->imm == 1)
+		return true;
+
+	/* cast from as(0) to as(1) */
+	if (insn->imm == 1 << 16)
+		return arena && arena->map_flags & BPF_F_NO_USER_CONV;
+
+	/* non-BPF_F_NO_USER_CONV cast from as(0) to as(1) should be handled by JIT */
+	return false;
+}
+
 /* Return the regno defined by the insn, or -1. */
 static int insn_def_regno(const struct bpf_insn *insn)
 {
@@ -44,15 +64,60 @@ static int insn_def_regno(const struct bpf_insn *insn)
 	}
 }
 
-/* Return TRUE if INSN has defined any 32-bit value explicitly. */
-static bool insn_has_def32(struct bpf_insn *insn)
+/*
+ * For use only in combination with insn_def_regno() >= 0.
+ * Returns TRUE if the destination register operates on 64-bit,
+ * otherwise return FALSE.
+ */
+static bool bpf_is_reg64(struct bpf_prog *prog, struct bpf_insn *insn)
 {
-	int dst_reg = insn_def_regno(insn);
+	u8 class = BPF_CLASS(insn->code);
+	u8 mode = BPF_MODE(insn->code);
+	u8 size = BPF_SIZE(insn->code);
+	u8 op = BPF_OP(insn->code);
+	bool mode_mem;
+
+	/* subregister endiness swap */
+	if ((class == BPF_ALU || class == BPF_ALU64) && op == BPF_END && insn->imm != 64)
+		return false;
+
+	/* w0 += 1 */
+	if (class == BPF_ALU && op != BPF_END)
+		return false;
+
+	/* address space casts converted to BPF_ALU, see bpf_do_misc_fixups() */
+	if (is_addr_space_cast32(prog, insn))
+		return false;
 
-	if (dst_reg == -1)
+	/* non 64-bit, non signed extended loads */
+	mode_mem = mode == BPF_MEM || mode == BPF_PROBE_MEM || mode == BPF_PROBE_MEM32;
+	if (class == BPF_LDX && mode_mem && size != BPF_DW)
 		return false;
 
-	return !bpf_is_reg64(insn, dst_reg, NULL, DST_OP);
+	/* atomics, see insn_def_regno() */
+	if (class == BPF_STX && size != BPF_DW)
+		return false;
+
+	/* both LD_IND and LD_ABS return 32-bit data. */
+	if (class == BPF_LD && (mode == BPF_IND || mode == BPF_ABS))
+		return false;
+
+	/* Conservatively return true at default. */
+	return true;
+}
+
+/*
+ * Return the 32-bit subregister defined by INSN, or -1 if INSN does not
+ * explicitly define a 32-bit value.
+ */
+int bpf_insn_def32(struct bpf_prog *prog, struct bpf_insn *insn)
+{
+	int dst_reg = insn_def_regno(insn);
+
+	if (dst_reg < 0 || bpf_is_reg64(prog, insn))
+		return -1;
+
+	return dst_reg;
 }
 
 static int kfunc_desc_cmp_by_imm_off(const void *a, const void *b)
@@ -169,7 +234,7 @@ static void adjust_insn_aux_data(struct bpf_verifier_env *env,
 	 * (cnt == 1) is taken or not. There is no guarantee INSN at OFF is the
 	 * original insn at old prog.
 	 */
-	data[off].zext_dst = insn_has_def32(insn + off + cnt - 1);
+	data[off].zext_dst = bpf_insn_def32(new_prog, insn + off + cnt - 1) >= 0;
 
 	if (cnt == 1)
 		return;
@@ -181,7 +246,7 @@ static void adjust_insn_aux_data(struct bpf_verifier_env *env,
 	for (i = off; i < off + cnt - 1; i++) {
 		/* Expand insni[off]'s seen count to the patched range. */
 		data[i].seen = old_seen;
-		data[i].zext_dst = insn_has_def32(insn + i);
+		data[i].zext_dst = bpf_insn_def32(new_prog, insn + i) >= 0;
 	}
 
 	/*
@@ -616,11 +681,7 @@ int bpf_opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,
 			if (load_reg == -1)
 				continue;
 
-			/* NOTE: arg "reg" (the fourth one) is only used for
-			 *       BPF_STX + SRC_OP, so it is safe to pass NULL
-			 *       here.
-			 */
-			if (bpf_is_reg64(&insn, load_reg, NULL, DST_OP)) {
+			if (bpf_is_reg64(env->prog, &insn)) {
 				if (class == BPF_LD &&
 				    BPF_MODE(code) == BPF_IMM)
 					i++;
@@ -1513,15 +1574,12 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 	}
 
 	for (i = 0; i < insn_cnt;) {
-		if (insn->code == (BPF_ALU64 | BPF_MOV | BPF_X) && insn->imm) {
-			if ((insn->off == BPF_ADDR_SPACE_CAST && insn->imm == 1) ||
-			    (((struct bpf_map *)env->prog->aux->arena)->map_flags & BPF_F_NO_USER_CONV)) {
-				/* convert to 32-bit mov that clears upper 32-bit */
-				insn->code = BPF_ALU | BPF_MOV | BPF_X;
-				/* clear off and imm, so it's a normal 'wX = wY' from JIT pov */
-				insn->off = 0;
-				insn->imm = 0;
-			} /* cast from as(0) to as(1) should be handled by JIT */
+		if (is_addr_space_cast32(env->prog, insn)) {
+			/* convert to 32-bit mov that clears upper 32-bit */
+			insn->code = BPF_ALU | BPF_MOV | BPF_X;
+			/* clear off and imm, so it's a normal 'wX = wY' from JIT pov */
+			insn->off = 0;
+			insn->imm = 0;
 			goto next_insn;
 		}
 
diff --git a/kernel/bpf/liveness.c b/kernel/bpf/liveness.c
index 0aadfbae0acc5..ef9a5a9228872 100644
--- a/kernel/bpf/liveness.c
+++ b/kernel/bpf/liveness.c
@@ -497,7 +497,6 @@ static void print_instance(struct bpf_verifier_env *env, struct func_instance *i
 		pos = env->log.end_pos;
 		verbose(env, "%3d: ", insn_idx);
 		bpf_verbose_insn(env, &insns[insn_idx]);
-		bpf_vlog_reset(&env->log, env->log.end_pos - 1); /* remove \n */
 		insn_pos = env->log.end_pos;
 		verbose(env, "%*c;", bpf_vlog_alignment(insn_pos - pos), ' ');
 		pos = env->log.end_pos;
@@ -1043,7 +1042,6 @@ static void arg_track_log(struct bpf_verifier_env *env, struct bpf_insn *insn, i
 		if (!printed) {
 			verbose(env, "%3d: ", idx);
 			bpf_verbose_insn(env, insn);
-			bpf_vlog_reset(&env->log, env->log.end_pos - 1);
 			printed = true;
 		}
 		verbose(env, "\tr%d: ", i); verbose_arg_track(env, &at_in[i]);
@@ -1058,7 +1056,6 @@ static void arg_track_log(struct bpf_verifier_env *env, struct bpf_insn *insn, i
 		if (!printed) {
 			verbose(env, "%3d: ", idx);
 			bpf_verbose_insn(env, insn);
-			bpf_vlog_reset(&env->log, env->log.end_pos - 1);
 			printed = true;
 		}
 		verbose(env, "\tsa%d: ", i); verbose_arg_track(env, &at_in[ai]);
@@ -1070,7 +1067,6 @@ static void arg_track_log(struct bpf_verifier_env *env, struct bpf_insn *insn, i
 		if (!printed) {
 			verbose(env, "%3d: ", idx);
 			bpf_verbose_insn(env, insn);
-			bpf_vlog_reset(&env->log, env->log.end_pos - 1);
 			printed = true;
 		}
 		verbose(env, "\tfp%+d: ", -(i + 1) * 8); verbose_arg_track(env, &at_stack_in[i]);
@@ -1545,6 +1541,7 @@ static void print_subprog_arg_access(struct bpf_verifier_env *env,
 
 		verbose(env, "%3d: ", idx);
 		bpf_verbose_insn(env, &insns[idx]);
+		verbose(env, "\n");
 
 		/* Collect what needs printing */
 		if (is_ldx_stx_call &&
@@ -2050,29 +2047,38 @@ int bpf_compute_subprog_arg_access(struct bpf_verifier_env *env)
 
 /* Each field is a register bitmask */
 struct insn_live_regs {
-	u16 use;	/* registers read by instruction */
-	u16 def;	/* registers written by instruction */
-	u16 in;		/* registers that may be alive before instruction */
-	u16 out;	/* registers that may be alive after instruction */
+	u32 use;	/* registers read by instruction */
+	u32 def;	/* registers written by instruction */
+	u32 in;		/* registers that may be alive before instruction */
+	u32 out;	/* registers that may be alive after instruction */
 };
 
 /* Bitmask with 1s for all caller saved registers */
 #define ALL_CALLER_SAVED_REGS ((1u << CALLER_SAVED_REGS) - 1)
 
+static inline u32 reg32_mask(u32 n) { return BIT(n); }
+static inline u32 reg64_mask(u32 n) { return BIT(n) | BIT(n + 16); }
+static inline u32 mask_widen(u32 m) { return m | (m << 16); }
+static inline u16 mask_lo(u32 m) { return (u16)m; }
+static inline u16 mask_hi(u32 m) { return (u16)(m >> 16); }
+
 /* Compute info->{use,def} fields for the instruction */
 static void compute_insn_live_regs(struct bpf_verifier_env *env,
 				   struct bpf_insn *insn,
 				   struct insn_live_regs *info)
 {
 	struct bpf_call_summary cs;
-	u8 class = BPF_CLASS(insn->code);
-	u8 code = BPF_OP(insn->code);
-	u8 mode = BPF_MODE(insn->code);
-	u16 src = BIT(insn->src_reg);
-	u16 dst = BIT(insn->dst_reg);
-	u16 r0  = BIT(0);
-	u16 def = 0;
-	u16 use = 0xffff;
+	const u8 class = BPF_CLASS(insn->code);
+	const u8 code = BPF_OP(insn->code);
+	const u8 mode = BPF_MODE(insn->code);
+	const u8 size = BPF_SIZE(insn->code);
+	const u32 src = reg64_mask(insn->src_reg);
+	const u32 dst = reg64_mask(insn->dst_reg);
+	const u32 src32 = mask_lo(src);
+	const u32 dst32 = mask_lo(dst);
+	const u32 r0  = reg64_mask(0);
+	u32 def = 0;
+	u32 use = U32_MAX;
 
 	switch (class) {
 	case BPF_LD:
@@ -2083,8 +2089,8 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,
 				use = 0;
 			}
 			break;
-		case BPF_LD | BPF_ABS:
-		case BPF_LD | BPF_IND:
+		case BPF_ABS:
+		case BPF_IND:
 			/* stick with defaults */
 			break;
 		}
@@ -2092,7 +2098,15 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,
 	case BPF_LDX:
 		switch (mode) {
 		case BPF_MEM:
+			/* a narrow load still redefines the whole register */
+			def = dst;
+			use = src;
+			break;
 		case BPF_MEMSX:
+			/*
+			 * sign extension defines the whole register;
+			 * src holds a pointer, hence is used as 64-bit.
+			 */
 			def = dst;
 			use = src;
 			break;
@@ -2110,12 +2124,19 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,
 		switch (mode) {
 		case BPF_MEM:
 			def = 0;
-			use = dst | src;
+			use = dst | (size == BPF_DW ? src : src32);
 			break;
-		case BPF_ATOMIC:
+		case BPF_ATOMIC: {
+			/*
+			 * dst holds a pointer and is always used as 64-bit;
+			 * the value operand and r0 are read as 32-bit for BPF_W atomics.
+			 */
+			u32 srcv = size == BPF_DW ? src : src32;
+			u32 r0v  = size == BPF_DW ? r0 : mask_lo(r0);
+
 			switch (insn->imm) {
 			case BPF_CMPXCHG:
-				use = r0 | dst | src;
+				use = r0v | dst | srcv;
 				def = r0;
 				break;
 			case BPF_LOAD_ACQ:
@@ -2124,10 +2145,10 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,
 				break;
 			case BPF_STORE_REL:
 				def = 0;
-				use = dst | src;
+				use = dst | srcv;
 				break;
 			default:
-				use = dst | src;
+				use = dst | srcv;
 				if (insn->imm & BPF_FETCH)
 					def = src;
 				else
@@ -2135,6 +2156,7 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,
 			}
 			break;
 		}
+		}
 		break;
 	case BPF_ALU:
 	case BPF_ALU64:
@@ -2148,14 +2170,14 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,
 			if (BPF_SRC(insn->code) == BPF_K)
 				use = 0;
 			else
-				use = src;
+				use = class == BPF_ALU64 ? src : src32;
 			break;
 		default:
 			def = dst;
 			if (BPF_SRC(insn->code) == BPF_K)
-				use = dst;
+				use = class == BPF_ALU64 ? dst : dst32;
 			else
-				use = dst | src;
+				use = class == BPF_ALU64 ? (dst | src) : (dst32 | src32);
 		}
 		break;
 	case BPF_JMP:
@@ -2181,13 +2203,14 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,
 			use = def & ~BIT(BPF_REG_0);
 			if (bpf_get_call_summary(env, insn, &cs))
 				use = GENMASK(min_t(u8, cs.num_params, MAX_BPF_FUNC_REG_ARGS), 1);
+			def = mask_widen(def);
+			use = mask_widen(use);
 			break;
 		default:
 			def = 0;
-			if (BPF_SRC(insn->code) == BPF_K)
-				use = dst;
-			else
-				use = dst | src;
+			use = class == BPF_JMP ? dst : dst32;
+			if (BPF_SRC(insn->code) == BPF_X)
+				use |= class == BPF_JMP ? src : src32;
 		}
 		break;
 	}
@@ -2209,6 +2232,7 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)
 	struct bpf_insn *insns = env->prog->insnsi;
 	struct insn_live_regs *state;
 	int insn_cnt = env->prog->len;
+	u64 pos, insn_pos;
 	int err = 0, i, j;
 	bool changed;
 
@@ -2252,8 +2276,8 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)
 			int insn_idx = env->cfg.insn_postorder[i];
 			struct insn_live_regs *live = &state[insn_idx];
 			struct bpf_iarray *succ;
-			u16 new_out = 0;
-			u16 new_in = 0;
+			u32 new_out = 0;
+			u32 new_in = 0;
 
 			succ = bpf_insn_successors(env, insn_idx);
 			for (int s = 0; s < succ->cnt; ++s)
@@ -2267,8 +2291,20 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)
 		}
 	}
 
-	for (i = 0; i < insn_cnt; ++i)
-		insn_aux[i].live_regs_before = state[i].in;
+	for (i = 0; i < insn_cnt; ++i) {
+		int def32 = bpf_insn_def32(env->prog, &insns[i]);
+		u32 out = state[i].out;
+		u32 in = state[i].in;
+
+		insn_aux[i].live_regs_before = mask_lo(in) | mask_hi(in);
+		/*
+		 * On architectures where 32-bit operations do not reset upper halves
+		 * of the registers, the verifier needs to zero extend a destination
+		 * register if an instruction defines a 32-bit subregister and the
+		 * upper half of that register is alive after the instruction.
+		 */
+		insn_aux[i].zext_dst = def32 >= 0 && (mask_hi(out) & BIT(def32));
+	}
 
 	if (env->log.level & BPF_LOG_LEVEL2) {
 		verbose(env, "Live regs before insn:\n");
@@ -2284,7 +2320,12 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)
 				else
 					verbose(env, ".");
 			verbose(env, " ");
+			pos = env->log.end_pos;
 			bpf_verbose_insn(env, &insns[i]);
+			insn_pos = env->log.end_pos;
+			if (insn_aux[i].zext_dst)
+				verbose(env, "%*c; zext", bpf_vlog_alignment(insn_pos - pos), ' ');
+			verbose(env, "\n");
 			if (bpf_is_ldimm64(&insns[i]))
 				i++;
 		}
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e6233c0081d10..14f7516458749 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -2132,12 +2132,9 @@ static int reg_bounds_sanity_check(struct bpf_verifier_env *env,
 /* Mark a register as having a completely unknown (scalar) value. */
 void bpf_mark_reg_unknown_imprecise(struct bpf_reg_state *reg)
 {
-	s32 subreg_def = reg->subreg_def;
-
 	memset(reg, 0, sizeof(*reg));
 	reg->type = SCALAR_VALUE;
 	reg->var_off = tnum_unknown;
-	reg->subreg_def = subreg_def;
 	__mark_reg_unbounded(reg);
 }
 
@@ -2213,7 +2210,6 @@ static int mark_btf_ld_reg(struct bpf_verifier_env *env,
 	}
 }
 
-#define DEF_NOT_SUBREG	(0)
 static void init_reg_state(struct bpf_verifier_env *env,
 			   struct bpf_func_state *state)
 {
@@ -2222,7 +2218,6 @@ static void init_reg_state(struct bpf_verifier_env *env,
 
 	for (i = 0; i < MAX_BPF_REG; i++) {
 		bpf_mark_reg_not_init(env, &regs[i]);
-		regs[i].subreg_def = DEF_NOT_SUBREG;
 	}
 
 	/* frame pointer */
@@ -3058,120 +3053,14 @@ static void mark_stack_slots_scratched(struct bpf_verifier_env *env,
 		mark_stack_slot_scratched(env, spi - i);
 }
 
-/* This function is supposed to be used by the following 32-bit optimization
- * code only. It returns TRUE if the source or destination register operates
- * on 64-bit, otherwise return FALSE.
- */
-bool bpf_is_reg64(struct bpf_insn *insn,
-	      u32 regno, struct bpf_reg_state *reg, enum bpf_reg_arg_type t)
-{
-	u8 code, class, op;
-
-	code = insn->code;
-	class = BPF_CLASS(code);
-	op = BPF_OP(code);
-	if (class == BPF_JMP) {
-		/* BPF_EXIT for "main" will reach here. Return TRUE
-		 * conservatively.
-		 */
-		if (op == BPF_EXIT)
-			return true;
-		if (op == BPF_CALL) {
-			/* BPF to BPF call will reach here because of marking
-			 * caller saved clobber with DST_OP_NO_MARK for which we
-			 * don't care the register def because they are anyway
-			 * marked as NOT_INIT already.
-			 */
-			if (insn->src_reg == BPF_PSEUDO_CALL)
-				return false;
-			/* Helper call will reach here because of arg type
-			 * check, conservatively return TRUE.
-			 */
-			if (t == SRC_OP)
-				return true;
-
-			return false;
-		}
-	}
-
-	if (class == BPF_ALU64 && op == BPF_END && (insn->imm == 16 || insn->imm == 32))
-		return false;
-
-	if (class == BPF_ALU64 || class == BPF_JMP ||
-	    (class == BPF_ALU && op == BPF_END && insn->imm == 64))
-		return true;
-
-	if (class == BPF_ALU || class == BPF_JMP32)
-		return false;
-
-	if (class == BPF_LDX) {
-		if (t != SRC_OP)
-			return BPF_SIZE(code) == BPF_DW || BPF_MODE(code) == BPF_MEMSX;
-		/* LDX source must be ptr. */
-		return true;
-	}
-
-	if (class == BPF_STX) {
-		/* BPF_STX (including atomic variants) has one or more source
-		 * operands, one of which is a ptr. Check whether the caller is
-		 * asking about it.
-		 */
-		if (t == SRC_OP && reg->type != SCALAR_VALUE)
-			return true;
-		return BPF_SIZE(code) == BPF_DW;
-	}
-
-	if (class == BPF_LD) {
-		u8 mode = BPF_MODE(code);
-
-		/* LD_IMM64 */
-		if (mode == BPF_IMM)
-			return true;
-
-		/* Both LD_IND and LD_ABS return 32-bit data. */
-		if (t != SRC_OP)
-			return  false;
-
-		/* Implicit ctx ptr. */
-		if (regno == BPF_REG_6)
-			return true;
-
-		/* Explicit source could be any width. */
-		return true;
-	}
-
-	if (class == BPF_ST)
-		/* The only source register for BPF_ST is a ptr. */
-		return true;
-
-	/* Conservatively return true at default. */
-	return true;
-}
-
-static void mark_insn_zext(struct bpf_verifier_env *env,
-			   struct bpf_reg_state *reg)
-{
-	s32 def_idx = reg->subreg_def;
-
-	if (def_idx == DEF_NOT_SUBREG)
-		return;
-
-	env->insn_aux_data[def_idx - 1].zext_dst = true;
-	/* The dst will be zero extended, so won't be sub-register anymore. */
-	reg->subreg_def = DEF_NOT_SUBREG;
-}
-
 static int __check_reg_arg(struct bpf_verifier_env *env, struct bpf_reg_state *regs, u32 regno,
 			   enum bpf_reg_arg_type t)
 {
-	struct bpf_insn *insn = env->prog->insnsi + env->insn_idx;
 	struct bpf_reg_state *reg;
-	bool rw64;
 
 	mark_reg_scratched(env, regno);
 
 	reg = &regs[regno];
-	rw64 = bpf_is_reg64(insn, regno, reg, t);
 	if (t == SRC_OP) {
 		/* check whether register used as source operand can be read */
 		if (reg->type == NOT_INIT) {
@@ -3182,9 +3071,6 @@ static int __check_reg_arg(struct bpf_verifier_env *env, struct bpf_reg_state *r
 		if (regno == BPF_REG_FP)
 			return 0;
 
-		if (rw64)
-			mark_insn_zext(env, reg);
-
 		return 0;
 	} else {
 		/* check whether register used as dest operand can be written to */
@@ -3192,7 +3078,6 @@ static int __check_reg_arg(struct bpf_verifier_env *env, struct bpf_reg_state *r
 			verbose(env, "frame pointer is read only\n");
 			return -EACCES;
 		}
-		reg->subreg_def = rw64 ? DEF_NOT_SUBREG : env->insn_idx + 1;
 		if (t == DST_OP)
 			mark_reg_unknown(env, regs, regno);
 	}
@@ -3848,11 +3733,6 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
 
 			if (size <= spill_size &&
 			    bpf_stack_narrow_access_ok(off, size, spill_size)) {
-				/* The earlier check_reg_arg() has decided the
-				 * subreg_def for this insn.  Save it first.
-				 */
-				s32 subreg_def = state->regs[dst_regno].subreg_def;
-
 				if (env->bpf_capable && size == 4 && spill_size == 4 &&
 				    get_reg_width(reg) <= 32)
 					/* Ensure stack slot has an ID to build a relation
@@ -3860,7 +3740,6 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
 					 */
 					assign_scalar_id_before_mov(env, reg);
 				state->regs[dst_regno] = *reg;
-				state->regs[dst_regno].subreg_def = subreg_def;
 
 				/* Break the relation on a narrowing fill.
 				 * coerce_reg_to_size will adjust the boundaries.
@@ -6311,12 +6190,6 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b
 			} else {
 				mark_reg_known_zero(env, regs,
 						    value_regno);
-				/* A load of ctx field could have different
-				 * actual load size with the one encoded in the
-				 * insn. When the dst is PTR, it is for sure not
-				 * a sub-register.
-				 */
-				regs[value_regno].subreg_def = DEF_NOT_SUBREG;
 				if (base_type(info.reg_type) == PTR_TO_BTF_ID) {
 					regs[value_regno].btf = info.btf;
 					regs[value_regno].btf_id = info.btf_id;
@@ -7415,10 +7288,6 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
 			if (spi < 0)
 				return spi;
 
-			/*
-			 * For CONST_PTR_TO_DYNPTR, reg is already scratched by check_reg_arg
-			 * in check_helper_call and mark_btf_func_reg_size in check_kfunc_call.
-			 */
 			mark_stack_slots_scratched(env, spi, BPF_DYNPTR_NR_SLOTS);
 
 			reg = &state->stack[spi].spilled_ptr;
@@ -9522,7 +9391,6 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 		/* All non-void global functions return a 64-bit SCALAR_VALUE. */
 		if (!subprog_returns_void(env, subprog)) {
 			mark_reg_unknown(env, caller->regs, BPF_REG_0);
-			caller->regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG;
 		}
 
 		if (env->subprog_info[subprog].might_throw) {
@@ -10542,9 +10410,6 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
 	}
 	invalidate_outgoing_stack_args(env, cur_func(env));
 
-	/* helper call returns 64-bit value. */
-	regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG;
-
 	/* update return register (already marked as written above) */
 	ret_type = fn->ret_type;
 	ret_flag = type_flag(ret_type);
@@ -10784,30 +10649,6 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
 	return 0;
 }
 
-/* mark_btf_func_reg_size() is used when the reg size is determined by
- * the BTF func_proto's return value size and argument.
- */
-static void __mark_btf_func_reg_size(struct bpf_verifier_env *env, struct bpf_reg_state *regs,
-				     u32 regno, size_t reg_size)
-{
-	struct bpf_reg_state *reg = &regs[regno];
-
-	if (regno == BPF_REG_0) {
-		/* Function return value */
-		reg->subreg_def = reg_size == sizeof(u64) ?
-			DEF_NOT_SUBREG : env->insn_idx + 1;
-	} else if (reg_size == sizeof(u64)) {
-		/* Function argument */
-		mark_insn_zext(env, reg);
-	}
-}
-
-static void mark_btf_func_reg_size(struct bpf_verifier_env *env, u32 regno,
-				   size_t reg_size)
-{
-	return __mark_btf_func_reg_size(env, cur_regs(env), regno, reg_size);
-}
-
 static bool is_kfunc_acquire(struct bpf_call_arg_meta *meta)
 {
 	return meta->kfunc_flags & KF_ACQUIRE;
@@ -13026,7 +12867,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 	struct bpf_call_arg_meta meta;
 	struct bpf_insn_aux_data *insn_aux;
 	int err, insn_idx = *insn_idx_p;
-	const struct btf_param *args;
 	u32 i, nargs, ptr_type_id;
 	struct bpf_kfunc_desc *desc;
 	struct btf *desc_btf;
@@ -13078,7 +12918,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 			verbose(env, "failed to mark s32 range for retval in forked state for lock\n");
 			return err;
 		}
-		__mark_btf_func_reg_size(env, regs, BPF_REG_0, sizeof(u32));
 	} else if (!insn->off && insn->imm == special_kfunc_list[KF___bpf_trap]) {
 		verbose(env, "unexpected __bpf_trap() due to uninitialized variable?\n");
 		return -EFAULT;
@@ -13231,7 +13070,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 		u32 regno = caller_saved[i];
 
 		bpf_mark_reg_not_init(env, &regs[regno]);
-		regs[regno].subreg_def = DEF_NOT_SUBREG;
 	}
 	invalidate_outgoing_stack_args(env, cur_func(env));
 
@@ -13253,7 +13091,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 		if (meta.btf == btf_vmlinux && (meta.func_id == special_kfunc_list[KF_bpf_res_spin_lock] ||
 		    meta.func_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave]))
 			__mark_reg_const_zero(env, &regs[BPF_REG_0]);
-		mark_btf_func_reg_size(env, BPF_REG_0, t->size);
 	} else if (btf_type_is_ptr(t)) {
 		ptr_type = btf_type_skip_modifiers(desc_btf, t->type, &ptr_type_id);
 		err = check_special_kfunc(env, &meta, regs, insn_aux, ptr_type, desc_btf);
@@ -13344,7 +13181,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 			/* For mark_ptr_or_null_reg, see 93c230e3f5bd6 */
 			regs[BPF_REG_0].id = ++env->id_gen;
 		}
-		mark_btf_func_reg_size(env, BPF_REG_0, sizeof(void *));
 		if (is_kfunc_acquire(&meta)) {
 			id = acquire_reference(env, insn_idx, 0);
 			if (id < 0)
@@ -13381,18 +13217,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 			caller_info->stack_arg_cnt = stack_arg_cnt;
 	}
 
-	args = (const struct btf_param *)(meta.func_proto + 1);
-	for (i = 0; i < min_t(int, nargs, MAX_BPF_FUNC_REG_ARGS); i++) {
-		u32 regno = i + 1;
-
-		t = btf_type_skip_modifiers(desc_btf, args[i].type, NULL);
-		if (btf_type_is_ptr(t))
-			mark_btf_func_reg_size(env, regno, sizeof(void *));
-		else
-			/* scalar. ensured by check_kfunc_args() */
-			mark_btf_func_reg_size(env, regno, t->size);
-	}
-
 	if (bpf_is_iter_next_kfunc(&meta)) {
 		err = process_iter_next_call(env, insn_idx, &meta);
 		if (err)
@@ -14886,14 +14710,14 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env,
 		if (dst_reg->type != PTR_TO_ARENA)
 			*dst_reg = *src_reg;
 
-		dst_reg->subreg_def = env->insn_idx + 1;
-
-		if (BPF_CLASS(insn->code) == BPF_ALU64)
+		if (BPF_CLASS(insn->code) == BPF_ALU64) {
 			/*
 			 * 32-bit operations zero upper bits automatically.
 			 * 64-bit operations need to be converted to 32.
 			 */
 			aux->needs_zext = true;
+			aux->zext_dst = true;
+		}
 
 		/* Any arithmetic operations are allowed on arena pointers */
 		return 0;
@@ -15089,18 +14913,14 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
 				if (insn->imm) {
 					/* off == BPF_ADDR_SPACE_CAST */
 					mark_reg_unknown(env, regs, insn->dst_reg);
-					if (insn->imm == 1) { /* cast from as(1) to as(0) */
+					if (insn->imm == 1) /* cast from as(1) to as(0) */
 						dst_reg->type = PTR_TO_ARENA;
-						/* PTR_TO_ARENA is 32-bit */
-						dst_reg->subreg_def = env->insn_idx + 1;
-					}
 				} else if (insn->off == 0) {
 					/* case: R1 = R2
 					 * copy register state to dest reg
 					 */
 					assign_scalar_id_before_mov(env, src_reg);
 					*dst_reg = *src_reg;
-					dst_reg->subreg_def = DEF_NOT_SUBREG;
 				} else {
 					/* case: R1 = (s8, s16 s32)R2 */
 					if (is_pointer_value(env, insn->src_reg)) {
@@ -15118,7 +14938,6 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
 						if (!no_sext)
 							clear_scalar_id(dst_reg);
 						coerce_reg_to_size_sx(dst_reg, insn->off >> 3);
-						dst_reg->subreg_def = DEF_NOT_SUBREG;
 					} else {
 						mark_reg_unknown(env, regs, insn->dst_reg);
 					}
@@ -15143,7 +14962,6 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
 						 */
 						if (!is_src_reg_u32)
 							clear_scalar_id(dst_reg);
-						dst_reg->subreg_def = env->insn_idx + 1;
 					} else {
 						/* case: W1 = (s8, s16)W2 */
 						bool no_sext = reg_umax(src_reg) < (1ULL << (insn->off - 1));
@@ -15153,7 +14971,6 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
 						*dst_reg = *src_reg;
 						if (!no_sext)
 							clear_scalar_id(dst_reg);
-						dst_reg->subreg_def = env->insn_idx + 1;
 						coerce_subreg_to_size_sx(dst_reg, insn->off >> 3);
 					}
 				} else {
@@ -16022,12 +15839,8 @@ static void sync_linked_regs(struct bpf_verifier_env *env, struct bpf_verifier_s
 			continue;
 		if ((!(reg->id & BPF_ADD_CONST) && !(known_reg->id & BPF_ADD_CONST)) ||
 		    reg->delta == known_reg->delta) {
-			s32 saved_subreg_def = reg->subreg_def;
-
 			*reg = *known_reg;
-			reg->subreg_def = saved_subreg_def;
 		} else {
-			s32 saved_subreg_def = reg->subreg_def;
 			s32 saved_off = reg->delta;
 			u32 saved_id = reg->id;
 
@@ -16037,12 +15850,11 @@ static void sync_linked_regs(struct bpf_verifier_env *env, struct bpf_verifier_s
 			/* reg = known_reg; reg += delta */
 			*reg = *known_reg;
 			/*
-			 * Must preserve off, id and subreg_def flag,
-			 * otherwise another sync_linked_regs() will be incorrect.
+			 * Must preserve off and id, otherwise another sync_linked_regs()
+			 * will be incorrect.
 			 */
 			reg->delta = saved_off;
 			reg->id = saved_id;
-			reg->subreg_def = saved_subreg_def;
 
 			scalar32_min_max_add(reg, &fake_reg);
 			scalar_min_max_add(reg, &fake_reg);
@@ -16477,8 +16289,6 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
 	 * Already marked as written above.
 	 */
 	mark_reg_unknown(env, regs, BPF_REG_0);
-	/* ld_abs load up to 32-bit skb data. */
-	regs[BPF_REG_0].subreg_def = env->insn_idx + 1;
 	/*
 	 * See bpf_gen_ld_abs() which emits a hidden BPF_EXIT with r0=0
 	 * which must be explored by the verifier when in a subprog.
@@ -16793,7 +16603,6 @@ bool bpf_get_call_summary(struct bpf_verifier_env *env, struct bpf_insn *call,
 	int i;
 
 	if (bpf_helper_call(call)) {
-
 		if (bpf_get_helper_proto(env, call->imm, &fn) < 0)
 			/* error would be reported later */
 			return false;
@@ -17538,6 +17347,7 @@ static int do_check(struct bpf_verifier_env *env)
 			env->prev_log_pos = env->log.end_pos;
 			verbose(env, "%d: ", env->insn_idx);
 			bpf_verbose_insn(env, insn);
+			verbose(env, "\n");
 			env->prev_insn_print_pos = env->log.end_pos - env->prev_log_pos;
 			env->prev_log_pos = env->log.end_pos;
 		}
diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c
index 5e7cb8b36fef2..5579173a61e3b 100644
--- a/tools/bpf/bpftool/xlated_dumper.c
+++ b/tools/bpf/bpftool/xlated_dumper.c
@@ -107,14 +107,7 @@ print_insn_for_graph(void *private_data, const char *fmt, ...)
 
 	p = buf;
 	while (*p != '\0') {
-		if (*p == '\n') {
-			memmove(p + 3, p, strlen(buf) + 1 - (p - buf));
-			/* Align each instruction dump row left. */
-			*p++ = '\\';
-			*p++ = 'l';
-			/* Output multiline concatenation. */
-			*p++ = '\\';
-		} else if (*p == '<' || *p == '>' || *p == '|' || *p == '&') {
+		if (*p == '<' || *p == '>' || *p == '|' || *p == '&') {
 			memmove(p + 1, p, strlen(buf) + 1 - (p - buf));
 			/* Escape special character. */
 			*p++ = '\\';
@@ -129,16 +122,10 @@ print_insn_for_graph(void *private_data, const char *fmt, ...)
 static void __printf(2, 3)
 print_insn_json(void *private_data, const char *fmt, ...)
 {
-	unsigned int l = strlen(fmt);
-	char chomped_fmt[l];
 	va_list args;
 
 	va_start(args, fmt);
-	if (l > 0) {
-		strncpy(chomped_fmt, fmt, l - 1);
-		chomped_fmt[l - 1] = '\0';
-	}
-	jsonw_vprintf_enquote(json_wtr, chomped_fmt, args);
+	jsonw_vprintf_enquote(json_wtr, fmt, args);
 	va_end(args);
 }
 
@@ -351,6 +338,7 @@ void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
 
 		printf("%4u: ", i);
 		print_bpf_insn(&cbs, insn + i, true);
+		printf("\n");
 
 		if (opcodes) {
 			printf("       ");
@@ -417,6 +405,7 @@ void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
 
 		printf("%u: ", insn_off);
 		print_bpf_insn(&cbs, cur, true);
+		printf("\\l\\\n");
 
 		if (opcodes) {
 			printf("\\ \\ \\ \\ ");
diff --git a/tools/testing/selftests/bpf/disasm_helpers.c b/tools/testing/selftests/bpf/disasm_helpers.c
index f529f1c8c171d..30221352568d7 100644
--- a/tools/testing/selftests/bpf/disasm_helpers.c
+++ b/tools/testing/selftests/bpf/disasm_helpers.c
@@ -55,10 +55,9 @@ struct bpf_insn *disasm_insn(struct bpf_insn *insn, char *buf, size_t buf_sz)
 	 * for each instruction (FF stands for instruction `code` byte).
 	 * Remove the prefix inplace, and also simplify call instructions.
 	 * E.g.: "(85) call foo#10" -> "call foo".
-	 * Also remove newline in the end (the 'max(strlen(buf) - 1, 0)' thing).
 	 */
 	pfx_end = buf + 5;
-	sfx_start = buf + max((int)strlen(buf) - 1, 0);
+	sfx_start = buf + (int)strlen(buf);
 	if (strncmp(pfx_end, "call ", 5) == 0 && (tmp = strrchr(buf, '#')))
 		sfx_start = tmp;
 	len = sfx_start - pfx_end;
diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index b79bafca68f7a..0baa74618fa03 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -126,6 +126,7 @@
 #include "verifier_jit_inline.skel.h"
 #include "irq.skel.h"
 #include "verifier_ctx_ptr_param.skel.h"
+#include "verifier_zext.skel.h"
 
 #define MAX_ENTRIES 11
 
@@ -281,6 +282,7 @@ void test_irq(void)			      { RUN(irq); }
 void test_verifier_mtu(void)		      { RUN(verifier_mtu); }
 void test_verifier_jit_inline(void)               { RUN(verifier_jit_inline); }
 void test_verifier_ctx_ptr_param(void)       { RUN(verifier_ctx_ptr_param); }
+void test_verifier_zext(void)                 { RUN_TESTS(verifier_zext); }
 
 static int init_test_val_map(struct bpf_object *obj, char *map_name)
 {
diff --git a/tools/testing/selftests/bpf/progs/verifier_zext.c b/tools/testing/selftests/bpf/progs/verifier_zext.c
new file mode 100644
index 0000000000000..8f2362da91d69
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_zext.c
@@ -0,0 +1,392 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include "../../../include/linux/filter.h"
+#include <bpf_arena_common.h>
+#include <bpf/bpf_core_read.h>
+#include "bpf_misc.h"
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARENA);
+	__uint(map_flags, BPF_F_MMAPABLE | BPF_F_NO_USER_CONV);
+	__uint(max_entries, 1);
+} arena SEC(".maps");
+
+extern long bpf_kfunc_call_test4(signed char a, short b, int c, long d) __ksym;
+
+/* to retain debug info for BTF generation */
+void __kfunc_btf_root(void)
+{
+	bpf_kfunc_call_test4(0, 0, 0, 0);
+	bpf_arena_alloc_pages(0, 0, 0, 0, 0);
+	bpf_rdonly_cast(0, 0);
+}
+
+SEC("socket")
+__flag(BPF_F_TEST_STATE_FREQ)
+__flag(BPF_F_TEST_RND_HI32)
+__success __retval(0)
+__naked void zext_lost_across_checkpoint(void)
+{
+	asm volatile ("									\
+	call %[bpf_ktime_get_ns];							\
+	r8 = r0;									\
+	r6 = 0xdeadbeefcafebabe ll;	/* inject some value for r6's upper half */	\
+	if r8 != 0 goto 1f;		/* fall-through cached first, branch pruned */	\
+	r6 = 32;			/* full 64-bit def */				\
+	goto 2f;									\
+1:	w6 = 32;			/* 32-bit def, zext mark lost */		\
+2:	r0 = r6;			/* buggy verifier believed upper 32 bits are 0 */ \
+					/* and thus did not zero extended w6 = 32. */	\
+	r0 >>= 32;									\
+	exit;										\
+"	:
+	: __imm(bpf_ktime_get_ns)
+	: __clobber_all);
+}
+
+/* 32-bit ALU result read as 64-bit -> zext */
+SEC("socket")
+__success __log_level(2)
+__msg("w1 = w0{{ +}}; zext")
+__naked void zext_alu32_hi_used(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	w1 = w0;					\
+	r0 = r1;					\
+	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+/* 32-bit ALU result read only as 32-bit -> no zext */
+SEC("socket")
+__success __log_level(2)
+__not_msg("; zext")
+__naked void no_zext_alu32_hi_unused(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	w1 = w0;		/* MOV */		\
+	w2 = w1;					\
+	w2 += w1;		/* ALU32, BPF_X */	\
+	w2 += 1;		/* ALU32, BPF_K */	\
+	w2 = w2;		/* keep w2 alive for previous instruction */ \
+	r0 = 0;						\
+	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+/* 64-bit definition is never zero extended */
+SEC("socket")
+__success __log_level(2)
+__not_msg("r1 = r0{{.*}}; zext")
+__naked void no_zext_mov64(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	r1 = r0;					\
+	r0 = r1;					\
+	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+/* Narrow load result read as 64-bit -> zext */
+SEC("socket")
+__success __log_level(2)
+__msg("r1 = *(u32 *)(r10 -8){{ +}}; zext")
+__naked void zext_narrow_load_hi_used(void)
+{
+	asm volatile ("					\
+	r0 = 0;						\
+	*(u64 *)(r10 - 8) = r0;				\
+	r1 = *(u32 *)(r10 - 8);				\
+	r0 = r1;					\
+	exit;						\
+"	::: __clobber_all);
+}
+
+/* 32-bit atomic fetch result read as 64-bit -> zext */
+SEC("socket")
+__success __log_level(2)
+__msg("r1 = atomic_fetch_add((u32 *)(r10 -8), r1){{ +}}; zext")
+__naked void zext_atomic_fetch32_hi_used(void)
+{
+	asm volatile ("					\
+	r1 = 0;						\
+	*(u64 *)(r10 - 8) = r1;				\
+	w1 = 1;						\
+	.8byte %[fetch_add32];				\
+	r0 = r1;					\
+	exit;						\
+"	:
+	: __imm_insn(fetch_add32,
+		     BPF_ATOMIC_OP(BPF_W, BPF_ADD | BPF_FETCH, BPF_REG_10, BPF_REG_1, -8))
+	: __clobber_all);
+}
+
+/* 32-bit atomic cmpxchg result (r0) read as 64-bit -> zext */
+SEC("socket")
+__success __log_level(2)
+__msg("r0 = atomic_cmpxchg((u32 *)(r10 -8), r0, r1){{ +}}; zext")
+__naked void zext_cmpxchg32_hi_used(void)
+{
+	asm volatile ("					\
+	r1 = 0;						\
+	*(u64 *)(r10 - 8) = r1;				\
+	w0 = 0;						\
+	w1 = 1;						\
+	.8byte %[cmpxchg32];				\
+	r2 = r0;					\
+	r0 = r2;					\
+	exit;						\
+"	:
+	: __imm_insn(cmpxchg32,
+		     BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, BPF_REG_10, BPF_REG_1, -8))
+	: __clobber_all);
+}
+
+/* 32-bit def before a branch, upper half used on one branch -> zext */
+SEC("socket")
+__success __log_level(2)
+__msg("w6 = 32{{ +}}; zext")
+__naked void zext_cfg_hi_used_one_branch(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	w6 = 32;					\
+	if r0 == 0 goto 1f;				\
+	r0 = r6;					\
+	exit;						\
+1:	r0 = 0;						\
+	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+/* r1's upper half is dead, so 'w1 = 1' must NOT be marked for zero extension. */
+SEC("socket")
+__success __log_level(2)
+__not_msg("w1 = 1{{.*}}; zext")
+__naked void no_zext_other_reg_hi_used(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	r6 = r0;					\
+	r6 <<= 32;					\
+	w1 = 1;						\
+	r0 = r6;					\
+	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+/* LD_ABS defines r0; when r0 is read as 64-bit it must be zero extended */
+SEC("socket")
+__success __log_level(2)
+__msg("r0 = *(u8 *)skb[0]{{.*}}; zext")
+__naked void zext_ld_abs_hi_used(void)
+{
+	asm volatile ("					\
+	r6 = r1;					\
+	r0 = *(u8 *)skb[0];				\
+	r7 = r0;					\
+	r0 = r7;					\
+	exit;						\
+"	::: __clobber_all);
+}
+
+/* Helper parameters are read as 64-bit (call_use_mask() fallback) */
+SEC("socket")
+__success __log_level(2)
+__msg("w2 = 1{{ +}}; zext")
+__naked void helper_param_read_as_64bit(void)
+{
+	asm volatile ("					\
+	r1 = r10;					\
+	r1 += -8;					\
+	w2 = 1;						\
+	call %[bpf_trace_printk];			\
+	r0 = 0;						\
+	exit;						\
+"	:
+	: __imm(bpf_trace_printk)
+	: __clobber_all);
+}
+
+static __used __naked int subprog_reads_arg_as_64bit(void)
+{
+	asm volatile ("					\
+	r0 = r1;					\
+	exit;						\
+"	::: __clobber_all);
+}
+
+/* subprogram parameters are conservatively read as 64-bit */
+SEC("socket")
+__success __log_level(2)
+__msg("w1 = w0{{ +}}; zext")
+__naked void subprog_param_read_as_64bit(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	w1 = w0;					\
+	call subprog_reads_arg_as_64bit;		\
+	r0 = 0;						\
+	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+/* kfunc parameters are zero extended */
+SEC("tc")
+__success __log_level(2)
+__msg("w1 = 1{{ +}}; zext")
+__msg("w2 = 1{{ +}}; zext")
+__msg("w3 = 1{{ +}}; zext")
+__msg("w4 = 1{{ +}}; zext")
+__naked void kfunc_param_read_per_btf(void)
+{
+	asm volatile ("					\
+	w1 = 1;						\
+	w2 = 1;						\
+	w3 = 1;						\
+	w4 = 1;						\
+	call bpf_kfunc_call_test4;			\
+	r0 = 0;						\
+	exit;						\
+"	::: __clobber_all);
+}
+
+SEC("socket")
+__success __log_level(2)
+__not_msg("; zext")
+__naked void alu32_and_32bit_conditional(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	w1 = w0;					\
+	if w1 > 42 goto 1f;		/* BPF_K */	\
+	w2 = 28;					\
+	if w2 > w1 goto 1f;		/* BPF_X */	\
+	r0 = 0;						\
+1:	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+SEC("socket")
+__success __log_level(2)
+__msg("w1 = w0{{ +}}; zext")
+__naked void alu32_and_64bit_conditional(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	w1 = w0;					\
+	if r1 > 42 goto 1f;		/* BPF_K */	\
+	r2 = 28;					\
+	if r2 > r1 goto 1f;		/* BPF_X */	\
+	r0 = 0;						\
+1:	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+SEC("socket")
+__success __log_level(2)
+__not_msg("; zext")
+__naked void alu64_and_conditionals(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	r1 = r0;					\
+	if w1 > 42 goto 1f;		/* BPF_K */	\
+	if r1 > 42 goto 1f;		/* BPF_K */	\
+	r2 = 28;					\
+	if w2 > w1 goto 1f;		/* BPF_X */	\
+	if r2 > r1 goto 1f;		/* BPF_X */	\
+	r0 = 0;						\
+1:	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+#ifdef __BPF_FEATURE_ADDR_SPACE_CAST
+
+SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
+__arch_s390x
+__xlated("7: w1 = w0")
+__xlated("8: w1 = w1")
+__xlated("9: w1 += 8")
+__xlated("10: w1 = w1")
+__xlated("11: w2 = w1")
+__xlated("12: w2 = w2")
+__xlated("13: *(u64 *)(r1 +0) = r2")
+__naked void arena_ptr(void)
+{
+	asm volatile ("					\
+	r1 = %[arena] ll;				\
+	r2 = 0;						\
+	r3 = 1;						\
+	r4 = 0;						\
+	r5 = 0;						\
+	call %[bpf_arena_alloc_pages];			\
+	r1 = addr_space_cast(r0, 0, 1);		/* needs zext */ \
+	r1 += 8;				/* needs zext */ \
+	r2 = addr_space_cast(r1, 1, 0);		/* needs zext because of BPF_F_NO_USER_CONV */ \
+	*(u64 *)(r1 +0) = r2;				\
+	r0 = 0;						\
+	exit;						\
+"	:
+	: __imm(bpf_arena_alloc_pages),
+	  __imm_addr(arena)
+	: __clobber_all);
+}
+
+#endif
+
+/* Check if probe mem loads keep their zero extension. */
+SEC("socket")
+__success __log_level(2)
+__arch_s390x
+__xlated("3: r1 = *(u64 *)(r0 +0)")
+__xlated("4: r2 = *(u32 *)(r0 +0)")
+__xlated("5: w2 = w2")
+__xlated("6: r3 = *(u16 *)(r0 +0)")
+__xlated("7: w3 = w3")
+__xlated("8: r4 = *(u8 *)(r0 +0)")
+__xlated("9: w4 = w4")
+__naked void probe_mem(void)
+{
+	asm volatile ("					\
+	r1 = 0;						\
+	r2 = 0;						\
+	call %[bpf_rdonly_cast];			\
+	r1 = *(u64 *)(r0 + 0);	/* BPF_PROBE_MEM */	\
+	r2 = *(u32 *)(r0 + 0);	/* BPF_PROBE_MEM */	\
+	r3 = *(u16 *)(r0 + 0);	/* BPF_PROBE_MEM */	\
+	r4 = *(u8 *)(r0 + 0);	/* BPF_PROBE_MEM */	\
+	r0 = r1;		/* make the registers used */ \
+	r0 += r2;					\
+	r0 += r3;					\
+	r0 += r4;					\
+1:	exit;						\
+"	:
+	: __imm(bpf_rdonly_cast)
+	: __clobber_all);
+}
+
+char _license[] SEC("license") = "GPL";

3/2 2026/08/07 21:37 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 26678
							output: 33
							thoughts: 540
						

Thoughts:
**BPF Verifier Zero Extension Optimization: A Fuzzing Target**

Okay, so I've got this patch that fundamentally changes how the BPF verifier deals with zero-extension of 32-bit registers. This is a pretty significant change, removing `subreg_def` from `bpf_reg_state` and moving the zero-extension calculation, `zext_dst`, to the liveness analysis phase, specifically within `bpf_compute_live_registers`. It also introduces this new function, `bpf_insn_def32`, to identify instructions that define a 32-bit subregister.

The scope is fairly broad, touching `kernel/bpf/verifier.c`, `kernel/bpf/liveness.c`, `kernel/bpf/fixups.c`, `kernel/bpf/backtrack.c`, and even `kernel/bpf/disasm.c` along with `include/linux/bpf_verifier.h`. There's also a new test case, `tools/testing/selftests/bpf/progs/verifier_zext.c`. This is definitely a change that warrants some serious fuzzing.

My primary focus here is going to be identifying the key functions. Let's list these out:
*   `bpf_insn_def32` - This is new and likely the entry point for the new subregister detection logic.
*   `bpf_is_reg64`
*   `compute_insn_live_regs`
*   `bpf_compute_live_registers` - This one is key, given that it's now responsible for the zero-extension calculation.
*   `is_addr_space_cast32`
*   `bpf_do_misc_fixups` - This might be interesting, especially around how fixups are handled with the new liveness-driven approach.
*   `bpf_opt_subreg_zext_lo32_rnd_hi32` - This optimization function might expose some interesting edge cases.
*   `adjust_insn_aux_data`
*   `check_alu_op`
*   `sync_linked_regs`

I'll start digging into `kernel/bpf/liveness.c` and `kernel/bpf/verifier.c`. `bpf_compute_live_registers` looks particularly important, as does `bpf_opt_subreg_zext_lo32_rnd_hi32`. `bpf_do_misc_fixups` is also a likely target. I'll need to pay close attention to `bpf_insn_def32` since it's a new function. From here, I will begin to analyze the logic. Let's see what's in `kernel/bpf/liveness.c` and `kernel/bpf/fixups.c` and proceed from there.



4/2 2026/08/07 21:37 tool grepper
0m Args:
{
  "Expression": "bpf_compute_live_registers",
  "PathPrefix": "kernel/bpf/"
}

Results:
{
  "Output": "kernel/bpf/liveness.c=2066=static void compute_insn_live_regs(struct bpf_verifier_env *env,\n--\nkernel/bpf/liveness.c-2228- */\nkernel/bpf/liveness.c:2229:int bpf_compute_live_registers(struct bpf_verifier_env *env)\nkernel/bpf/liveness.c-2230-{\n--\nkernel/bpf/verifier.c=20006=int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\n--\nkernel/bpf/verifier.c-20171-\nkernel/bpf/verifier.c:20172:\tret = bpf_compute_live_registers(env);\nkernel/bpf/verifier.c-20173-\tif (ret \u003c 0)\n"
}

5/2 2026/08/07 21:37 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 28137
							output: 34
							thoughts: 88
						

Thoughts:
Here's my summary, as I understand the situation:

**Tracing the BPF Code Analysis**

Okay, so I'm looking at the BPF (Berkeley Packet Filter) code flow, and I'm zeroing in on how instructions are checked and processed. I see that the `bpf_check` function seems to be the central point of a few key operations. Specifically, I'm noticing that the `bpf_compute_live_registers` function is called from within `bpf_check`. This tells me that register liveness analysis is performed as part of the instruction verification process. Then, I also see `bpf_opt_subreg_zext_lo32_rnd_hi32` is called from `bpf_check` too which is something to look at, as it means the optimization of sub-register zero-extension, low 32 bits, rounding, high 32 bits is part of this checking phase. Additionally, I see that miscellaneous fixups are also applied during the check, as `bpf_do_misc_fixups` is also called by `bpf_check`.

Given this overview, I think it's worth taking a closer look at `bpf_insn_def32` to see if I can get a better sense of where this fits into the checking process. Specifically, I want to see if `bpf_insn_def32` is the type of instruction being checked or if that call is checking the definition of the register instruction.



6/2 2026/08/07 21:37 tool grepper
0m Args:
{
  "Expression": "bpf_insn_def32",
  "PathPrefix": "kernel/bpf/"
}

Results:
{
  "Output": "kernel/bpf/fixups.c=72=static bool bpf_is_reg64(struct bpf_prog *prog, struct bpf_insn *insn)\n--\nkernel/bpf/fixups.c-112- */\nkernel/bpf/fixups.c:113:int bpf_insn_def32(struct bpf_prog *prog, struct bpf_insn *insn)\nkernel/bpf/fixups.c-114-{\n--\nkernel/bpf/fixups.c=224=static void adjust_insn_aux_data(struct bpf_verifier_env *env,\n--\nkernel/bpf/fixups.c-236-\t */\nkernel/bpf/fixups.c:237:\tdata[off].zext_dst = bpf_insn_def32(new_prog, insn + off + cnt - 1) \u003e= 0;\nkernel/bpf/fixups.c-238-\n--\nkernel/bpf/fixups.c-248-\t\tdata[i].seen = old_seen;\nkernel/bpf/fixups.c:249:\t\tdata[i].zext_dst = bpf_insn_def32(new_prog, insn + i) \u003e= 0;\nkernel/bpf/fixups.c-250-\t}\n--\nkernel/bpf/liveness.c=2229=int bpf_compute_live_registers(struct bpf_verifier_env *env)\n--\nkernel/bpf/liveness.c-2294-\tfor (i = 0; i \u003c insn_cnt; ++i) {\nkernel/bpf/liveness.c:2295:\t\tint def32 = bpf_insn_def32(env-\u003eprog, \u0026insns[i]);\nkernel/bpf/liveness.c-2296-\t\tu32 out = state[i].out;\n"
}

7/2 2026/08/07 21:37 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 29078
							output: 27
							thoughts: 88
						

Thoughts:
Okay, here's my take on those symbols, keeping in mind my expertise:

**My BPF Debugging Checklist**

Right, so I'm looking at these BPF-related symbols and thinking strategically about where to start my analysis. Based on experience, here's my quick mental checklist:

*   `bpf_insn_def32` – Okay, this is a very interesting starting point. Seems to be related to the definition of 32-bit BPF instructions which means I can trace instruction handling and potential vulnerabilities. I'll need to drill down into the instruction processing logic.
*   `bpf_compute_live_registers` – Ah, register liveness analysis! This is crucial. Understanding how the compiler tracks which registers are actively in use is important, and potentially where we'll see any compiler optimization issues. I'll look into how this interacts with the instruction set.
*   `bpf_opt_subreg_zext_lo32_rnd_hi32` – This looks like an optimization pass. Specifically, a sub-register extension optimization that deals with zero-extension and rounding. I will focus on the details of how this works. I wonder if there are any subtle issues here that can create bugs.

Now, let's take a look at the system configuration. The initial check I need to perform is that BPF is even enabled. Good, `CONFIG_BPF_SYSCALL` will turn it on, so that saves me some time. So I can proceed with the analysis.



8/2 2026/08/07 21:37 tool grepper
0m Args:
{
  "Expression": "config BPF_SYSCALL",
  "PathPrefix": ""
}

Results:
{
  "Output": "kernel/bpf/Kconfig=25=menu \"BPF subsystem\"\nkernel/bpf/Kconfig-26-\nkernel/bpf/Kconfig:27:config BPF_SYSCALL\nkernel/bpf/Kconfig-28-\tbool \"Enable bpf() system call\"\n"
}

9/2 2026/08/07 21:37 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 29605
							output: 45
							thoughts: 127
						

Thoughts:
**Analyzing BPF Optimization and Fixup Routines**

Okay, so I'm looking at BPF configurations, specifically `BPF_SYSCALL`, which is the immediate context. My focus is on understanding the interaction between a few key symbols: `bpf_insn_def32`, `bpf_compute_live_registers`, `bpf_opt_subreg_zext_lo32_rnd_hi32`, and `bpf_do_misc_fixups`.

I want to understand how the optimizer is working here. Specifically, I need to zoom in on how the optimizer handles those subregister extensions, zext particularly, and the rounding of hi32, this is quite important. I want to see how `bpf_opt_subreg_zext_lo32_rnd_hi32` is implemented, and then how these fixes are done by `bpf_do_misc_fixups`. My next step is to examine the source code, specifically those functions, so I'll be digging into `kernel/bpf/fixups.c`.



10/2 2026/08/07 21:37 tool grepper
0m Args:
{
  "Expression": "bpf_opt_subreg_zext_lo32_rnd_hi32",
  "PathPrefix": "kernel/bpf/"
}

Results:
{
  "Output": "kernel/bpf/fixups.c=622=int bpf_opt_remove_nops(struct bpf_verifier_env *env)\n--\nkernel/bpf/fixups.c-646-\nkernel/bpf/fixups.c:647:int bpf_opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,\nkernel/bpf/fixups.c-648-\t\t\t\t\t const union bpf_attr *attr)\n--\nkernel/bpf/verifier.c=20006=int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\n--\nkernel/bpf/verifier.c-20224-\tif (ret == 0 \u0026\u0026 !bpf_prog_is_offloaded(env-\u003eprog-\u003eaux)) {\nkernel/bpf/verifier.c:20225:\t\tret = bpf_opt_subreg_zext_lo32_rnd_hi32(env, attr);\nkernel/bpf/verifier.c-20226-\t\tenv-\u003eprog-\u003eaux-\u003everifier_zext = bpf_jit_needs_zext() ? !ret\n"
}

11/2 2026/08/07 21:37 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 30326
							output: 33
							thoughts: 35
						

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

**Analyzing Focus Symbols in BPF Liveness Analysis**

Right, these *are* indeed good indicators of focus. They're telling me something significant about the code's behavior. Time to dig in a bit further. My gut says to look closely at the register liveness computation, specifically within the kernel BPF infrastructure. Let's start with `compute_insn_live_regs` in `kernel/bpf/liveness.c`. I need to understand exactly *how* it's determining register liveness at the instruction level within the BPF context. I want to see how the analysis interacts with those symbols, and how they contribute to determining control-flow-sensitive liveness. It's the liveness analysis that can show me how data flows through the program, and these focus symbols are the clues to understand the flow.



12/2 2026/08/07 21:37 tool grepper
0m Args:
{
  "Expression": "compute_insn_live_regs",
  "PathPrefix": "kernel/bpf/"
}

Results:
{
  "Output": "kernel/bpf/liveness.c=2063=static inline u16 mask_hi(u32 m) { return (u16)(m \u003e\u003e 16); }\n--\nkernel/bpf/liveness.c-2065-/* Compute info-\u003e{use,def} fields for the instruction */\nkernel/bpf/liveness.c:2066:static void compute_insn_live_regs(struct bpf_verifier_env *env,\nkernel/bpf/liveness.c-2067-\t\t\t\t   struct bpf_insn *insn,\n--\nkernel/bpf/liveness.c=2229=int bpf_compute_live_registers(struct bpf_verifier_env *env)\n--\nkernel/bpf/liveness.c-2264-\tfor (i = 0; i \u003c insn_cnt; ++i)\nkernel/bpf/liveness.c:2265:\t\tcompute_insn_live_regs(env, \u0026insns[i], \u0026state[i]);\nkernel/bpf/liveness.c-2266-\n"
}

13/2 2026/08/07 21:37 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 30870
							output: 33
							thoughts: 50
						

Thoughts:
Okay, here's my take on those thoughts, framed as an expert's internal monologue:

**Focus Symbol Selection and Initial Investigation**

Hmm, `compute_insn_live_regs` is a static function, which means it likely has a limited scope and isn't the best entry point for a wider analysis. It's probably buried deep in some specific BPF instruction processing. We need something more central, more likely to give us a broader view.

Let's quickly check `bpf_is_reg64`, now that I think of it. I'll search for it in `kernel/bpf/fixups.c`. That file is probably where the 64-bit register handling, if there is any, is performed. That's a reasonable first step. Hopefully that will give a decent idea of the approach taken.



14/2 2026/08/07 21:37 tool grepper
0m Args:
{
  "Expression": "bpf_is_reg64",
  "PathPrefix": "kernel/bpf/"
}

Results:
{
  "Output": "kernel/bpf/fixups.c=44=static int insn_def_regno(const struct bpf_insn *insn)\n--\nkernel/bpf/fixups.c-71- */\nkernel/bpf/fixups.c:72:static bool bpf_is_reg64(struct bpf_prog *prog, struct bpf_insn *insn)\nkernel/bpf/fixups.c-73-{\n--\nkernel/bpf/fixups.c=113=int bpf_insn_def32(struct bpf_prog *prog, struct bpf_insn *insn)\n--\nkernel/bpf/fixups.c-116-\nkernel/bpf/fixups.c:117:\tif (dst_reg \u003c 0 || bpf_is_reg64(prog, insn))\nkernel/bpf/fixups.c-118-\t\treturn -1;\n--\nkernel/bpf/fixups.c=647=int bpf_opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,\n--\nkernel/bpf/fixups.c-683-\nkernel/bpf/fixups.c:684:\t\t\tif (bpf_is_reg64(env-\u003eprog, \u0026insn)) {\nkernel/bpf/fixups.c-685-\t\t\t\tif (class == BPF_LD \u0026\u0026\n"
}

15/2 2026/08/07 21:37 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 31504
							output: 28
							thoughts: 166
						

Thoughts:
**Analyzing BPF Register Logic**

Okay, so `bpf_is_reg64` is a static function, which immediately narrows down my focus. I need to understand how it's being used. The key symbols I'm going to look at are `bpf_insn_def32`, `bpf_compute_live_registers`, `bpf_opt_subreg_zext_lo32_rnd_hi32`, and `bpf_do_misc_fixups`. These are likely where the function's behavior is impacting the broader BPF code.

Now, a critical step: I need to determine if any other configuration options are at play here. The patch seems to be touching `kernel/bpf/verifier.c`, `kernel/bpf/liveness.c`, and `kernel/bpf/fixups.c`, which are all standard parts of the BPF ecosystem. A quick mental note: these are all built when `CONFIG_BPF_SYSCALL` is enabled. That's good to know, but let's just double check the `kernel/bpf/Makefile` to confirm I haven't missed anything obvious. It's always best to be thorough.



16/2 2026/08/07 21:37 tool grepper
0m Args:
{
  "Expression": "verifier.o",
  "PathPrefix": "kernel/bpf/"
}

Results:
{
  "Output": "kernel/bpf/Makefile=7=CFLAGS_core.o += -Wno-override-init $(cflags-nogcse-yy)\nkernel/bpf/Makefile-8-\nkernel/bpf/Makefile:9:obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o cnum.o log.o token.o liveness.o const_fold.o\nkernel/bpf/Makefile-10-obj-$(CONFIG_BPF_SYSCALL) += bpf_iter.o map_iter.o task_iter.o prog_iter.o link_iter.o\n--\nkernel/bpf/bpf_lsm.c=413=const struct bpf_prog_ops lsm_prog_ops = {\n--\nkernel/bpf/bpf_lsm.c-415-\nkernel/bpf/bpf_lsm.c:416:const struct bpf_verifier_ops lsm_verifier_ops = {\nkernel/bpf/bpf_lsm.c-417-\t.get_func_proto = bpf_lsm_func_proto,\n--\nkernel/bpf/bpf_struct_ops.c=64=static DEFINE_MUTEX(update_mutex);\n--\nkernel/bpf/bpf_struct_ops.c-68-\nkernel/bpf/bpf_struct_ops.c:69:const struct bpf_verifier_ops bpf_struct_ops_verifier_ops = {\nkernel/bpf/bpf_struct_ops.c-70-};\n--\nkernel/bpf/btf.c=5950=static struct btf *btf_parse(const union bpf_attr *attr, bpfptr_t uattr,\n--\nkernel/bpf/btf.c-5966-\nkernel/bpf/btf.c:5967:\t/* user could have requested verbose verifier output\nkernel/bpf/btf.c-5968-\t * and supplied buffer to store the verification trace\n--\nkernel/bpf/cgroup.c=1906=const struct bpf_prog_ops cg_dev_prog_ops = {\n--\nkernel/bpf/cgroup.c-1908-\nkernel/bpf/cgroup.c:1909:const struct bpf_verifier_ops cg_dev_verifier_ops = {\nkernel/bpf/cgroup.c-1910-\t.get_func_proto\t\t= cgroup_dev_func_proto,\n--\nkernel/bpf/cgroup.c=2461=static u32 sysctl_convert_ctx_access(enum bpf_access_type type,\n--\nkernel/bpf/cgroup.c-2524-\nkernel/bpf/cgroup.c:2525:const struct bpf_verifier_ops cg_sysctl_verifier_ops = {\nkernel/bpf/cgroup.c-2526-\t.get_func_proto\t\t= sysctl_func_proto,\n--\nkernel/bpf/cgroup.c=2739=static int cg_sockopt_get_prologue(struct bpf_insn *insn_buf,\n--\nkernel/bpf/cgroup.c-2747-\nkernel/bpf/cgroup.c:2748:const struct bpf_verifier_ops cg_sockopt_verifier_ops = {\nkernel/bpf/cgroup.c-2749-\t.get_func_proto\t\t= cg_sockopt_func_proto,\n--\nkernel/bpf/fixups.c=749=int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)\n--\nkernel/bpf/fixups.c-751-\tstruct bpf_subprog_info *subprogs = env-\u003esubprog_info;\nkernel/bpf/fixups.c:752:\tconst struct bpf_verifier_ops *ops = env-\u003eops;\nkernel/bpf/fixups.c-753-\tint i, cnt, size, ctx_field_size, ret, delta = 0, epilogue_cnt = 0;\n--\nkernel/bpf/helpers.c-35-/* If kernel subsystem is allowing eBPF programs to call this function,\nkernel/bpf/helpers.c:36: * inside its own verifier_ops-\u003eget_func_proto() callback it should return\nkernel/bpf/helpers.c-37- * bpf_map_lookup_elem_proto, so that verifier can properly check the arguments\n--\nkernel/bpf/syscall.c=6620=syscall_prog_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)\n--\nkernel/bpf/syscall.c-6636-\nkernel/bpf/syscall.c:6637:const struct bpf_verifier_ops bpf_syscall_verifier_ops = {\nkernel/bpf/syscall.c-6638-\t.get_func_proto  = syscall_prog_func_proto,\n--\nkernel/bpf/task_iter.c=988=__bpf_kfunc struct vm_area_struct *bpf_iter_task_vma_next(struct bpf_iter_task_vma *it)\n--\nkernel/bpf/task_iter.c-1006-\t/*\nkernel/bpf/task_iter.c:1007:\t * The verifier only trusts vm_mm and vm_file (see\nkernel/bpf/task_iter.c-1008-\t * BTF_TYPE_SAFE_TRUSTED_OR_NULL in verifier.c). Take a reference\n--\nkernel/bpf/trampoline.c-17-/* dummy _ops. The verifier will operate on target program's ops. */\nkernel/bpf/trampoline.c:18:const struct bpf_verifier_ops bpf_extension_verifier_ops = {\nkernel/bpf/trampoline.c-19-};\n--\nkernel/bpf/verifier.c-38-\nkernel/bpf/verifier.c:39:static const struct bpf_verifier_ops * const bpf_verifier_ops[] = {\nkernel/bpf/verifier.c-40-#define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type) \\\nkernel/bpf/verifier.c:41:\t[_id] = \u0026 _name ## _verifier_ops,\nkernel/bpf/verifier.c-42-#define BPF_MAP_TYPE(_id, _ops)\n--\nkernel/bpf/verifier.c=6938=enum {\n--\nkernel/bpf/verifier.c-6949- * For traditional PTR_TO_MAP_VALUE or PTR_TO_BTF_ID | MEM_ALLOC, the verifier\nkernel/bpf/verifier.c:6950: * clears reg-\u003eid after value_or_null-\u003evalue transition, since the verifier only\nkernel/bpf/verifier.c-6951- * cares about the range of access to valid map value pointer and doesn't care\n--\nkernel/bpf/verifier.c=16598=bool bpf_get_call_summary(struct bpf_verifier_env *env, struct bpf_insn *call,\n--\nkernel/bpf/verifier.c-16663- *\nkernel/bpf/verifier.c:16664: * - if so, and if verifier or current JIT inlines the call to the\nkernel/bpf/verifier.c-16665- *   bpf_fastcall function (e.g. a helper call), kernel removes unnecessary\n--\nkernel/bpf/verifier.c=18605=static int check_struct_ops_btf_id(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-18708-\tprog-\u003eaux-\u003eattach_func_name = mname;\nkernel/bpf/verifier.c:18709:\tenv-\u003eops = st_ops-\u003everifier_ops;\nkernel/bpf/verifier.c-18710-\n--\nkernel/bpf/verifier.c=19333=static int check_attach_btf_id(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-19372-\t\t */\nkernel/bpf/verifier.c:19373:\t\tenv-\u003eops = bpf_verifier_ops[tgt_prog-\u003etype];\nkernel/bpf/verifier.c-19374-\t\tprog-\u003eexpected_attach_type = tgt_prog-\u003eexpected_attach_type;\n--\nkernel/bpf/verifier.c=20006=int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\n--\nkernel/bpf/verifier.c-20016-\t/* no program is valid */\nkernel/bpf/verifier.c:20017:\tif (ARRAY_SIZE(bpf_verifier_ops) == 0)\nkernel/bpf/verifier.c-20018-\t\treturn -EINVAL;\n--\nkernel/bpf/verifier.c-20028-\tenv-\u003eprog = *prog;\nkernel/bpf/verifier.c:20029:\tenv-\u003eops = bpf_verifier_ops[env-\u003eprog-\u003etype];\nkernel/bpf/verifier.c-20030-\n--\nkernel/bpf/verifier.c-20037-\nkernel/bpf/verifier.c:20038:\t/* user could have requested verbose verifier output\nkernel/bpf/verifier.c-20039-\t * and supplied buffer to store the verification trace\n"
}

17/2 2026/08/07 21:37 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 34382
							output: 196
							thoughts: 529
						

Thoughts:
**BPF Verifier Logic Change for Fuzzing**

Okay, so this patch is a big deal; it's messing with the core BPF verifier logic. Specifically, it's about how 32-bit register zero-extension is handled. Right now, it's using liveness analysis instead of the `subreg_def` field. This is a pretty significant functional change – it impacts how those 32-bit values are zero-extended to 64-bit, and that's critical.

Since this affects the `bpf()` syscall when loading BPF programs, that's where I need to focus. `CONFIG_BPF_SYSCALL` is the only config I need to worry about – good to know. I'll return `BPF_SYSCALL`.

I've identified a few key symbols to keep an eye on: `bpf_insn_def32`, `bpf_compute_live_registers`, `bpf_opt_subreg_zext_lo32_rnd_hi32`, and `bpf_do_misc_fixups`. I need to double-check each one, make sure they're what I think they are. I won't add `bpf_is_reg64` though, as it says not to use static internal helper functions in this case.

I confirmed that `bpf_insn_def32`, `bpf_compute_live_registers`, `bpf_opt_subreg_zext_lo32_rnd_hi32`, and `bpf_do_misc_fixups` are all exported in `include/linux/bpf_verifier.h`. Now, let's call `set-results`.



18/2 2026/08/07 21:37 tool set-results
0m Args:
{
  "EnableConfigs": [
    "BPF_SYSCALL"
  ],
  "FocusSymbols": [
    "bpf_insn_def32",
    "bpf_compute_live_registers",
    "bpf_opt_subreg_zext_lo32_rnd_hi32",
    "bpf_do_misc_fixups"
  ],
  "Reasoning": "The patch modifies the BPF verifier to track 32-bit register zero-extension using liveness analysis instead of the `subreg_def` field in `bpf_reg_state`. This is a significant functional change to the core BPF verifier logic, affecting how 32-bit register values are zero-extended to 64-bit. This change is reachable via the `bpf()` syscall when loading BPF programs and is definitely worth fuzzing.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL"
  ],
  "FocusSymbols": [
    "bpf_insn_def32",
    "bpf_compute_live_registers",
    "bpf_opt_subreg_zext_lo32_rnd_hi32",
    "bpf_do_misc_fixups"
  ],
  "Reasoning": "The patch modifies the BPF verifier to track 32-bit register zero-extension using liveness analysis instead of the `subreg_def` field in `bpf_reg_state`. This is a significant functional change to the core BPF verifier logic, affecting how 32-bit register values are zero-extended to 64-bit. This change is reachable via the `bpf()` syscall when loading BPF programs and is definitely worth fuzzing.",
  "WorthFuzzing": true
}

19/1 2026/08/07 21:37 action If
0m Args:
{
  "WorthFuzzing": true
}

Results:
null

20/2 2026/08/07 21:37 agent kmsan-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "KMSANReasoning": "The patch modifies the BPF verifier's internal logic for tracking 32-bit subregister zero-extension and register liveness. It refactors how zero-extension requirements are computed by replacing the `subreg_def` field in `struct bpf_reg_state` with a liveness-based approach using `struct insn_live_regs`. These changes are purely algorithmic and do not introduce new kernel structures copied to user space, nor do they add complex data structures with potentially uninitialized fields. Any bugs introduced by these changes would manifest as verifier logic errors, potentially leading to out-of-bounds accesses or memory corruption in BPF programs, which are effectively caught by KASAN. There is no risk of uninitialized kernel memory being leaked or used in control flow, so a dedicated KMSAN fuzzing session is not required.",
  "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:
For your convenience, here is the diff of the changes:
commit c811bb7277c87779b883f585fbd26208510a1533
Author: syz-cluster <triage@syzkaller.com>
Date:   Fri Aug 7 21:37:06 2026 +0000

    syz-cluster: applied patch under review

diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index a2a40caca0a0e..2c74d676ede97 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -162,11 +162,6 @@ struct bpf_reg_state {
 	 * pointing to bpf_func_state.
 	 */
 	u32 frameno;
-	/* Tracks subreg definition. The stored value is the insn_idx of the
-	 * writing insn. This is safe because subreg_def is used before any insn
-	 * patching which only happens after main verification finished.
-	 */
-	s32 subreg_def;
 	/* if (!precise && SCALAR_VALUE) min/max/tnum don't affect safety */
 	bool precise;
 };
@@ -1637,7 +1632,6 @@ struct bpf_kfunc_desc_tab {
 };
 
 /* Functions exported from verifier.c, used by fixups.c */
-bool bpf_is_reg64(struct bpf_insn *insn, u32 regno, struct bpf_reg_state *reg, enum bpf_reg_arg_type t);
 void bpf_clear_insn_aux_data(struct bpf_verifier_env *env, int start, int len);
 void bpf_mark_subprog_exc_cb(struct bpf_verifier_env *env, int subprog);
 bool bpf_allow_tail_call_in_subprogs(struct bpf_verifier_env *env);
@@ -1661,5 +1655,6 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env);
 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);
 
 #endif /* _LINUX_BPF_VERIFIER_H */
diff --git a/kernel/bpf/backtrack.c b/kernel/bpf/backtrack.c
index 2f473ad4fd7c1..40bd04421a991 100644
--- a/kernel/bpf/backtrack.c
+++ b/kernel/bpf/backtrack.c
@@ -285,6 +285,7 @@ static int backtrack_insn(struct bpf_verifier_env *env, int idx, int subseq_idx,
 		verbose(env, "stack=%s before ", env->tmp_str_buf);
 		verbose(env, "%d: ", idx);
 		bpf_verbose_insn(env, insn);
+		verbose(env, "\n");
 	}
 
 	/* If there is a history record that some registers gained range at this insn,
diff --git a/kernel/bpf/disasm.c b/kernel/bpf/disasm.c
index 0391b3bc00734..50b3ca5149a0a 100644
--- a/kernel/bpf/disasm.c
+++ b/kernel/bpf/disasm.c
@@ -139,7 +139,7 @@ static void print_bpf_end_insn(bpf_insn_print_t verbose,
 			       void *private_data,
 			       const struct bpf_insn *insn)
 {
-	verbose(private_data, "(%02x) r%d = %s%d r%d\n",
+	verbose(private_data, "(%02x) r%d = %s%d r%d",
 		insn->code, insn->dst_reg,
 		BPF_SRC(insn->code) == BPF_TO_BE ? "be" : "le",
 		insn->imm, insn->dst_reg);
@@ -149,7 +149,7 @@ static void print_bpf_bswap_insn(bpf_insn_print_t verbose,
 			       void *private_data,
 			       const struct bpf_insn *insn)
 {
-	verbose(private_data, "(%02x) r%d = bswap%d r%d\n",
+	verbose(private_data, "(%02x) r%d = bswap%d r%d",
 		insn->code, insn->dst_reg,
 		insn->imm, insn->dst_reg);
 }
@@ -197,19 +197,19 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 			else
 				print_bpf_end_insn(verbose, cbs->private_data, insn);
 		} else if (BPF_OP(insn->code) == BPF_NEG) {
-			verbose(cbs->private_data, "(%02x) %c%d = -%c%d\n",
+			verbose(cbs->private_data, "(%02x) %c%d = -%c%d",
 				insn->code, class == BPF_ALU ? 'w' : 'r',
 				insn->dst_reg, class == BPF_ALU ? 'w' : 'r',
 				insn->dst_reg);
 		} else if (is_addr_space_cast(insn)) {
-			verbose(cbs->private_data, "(%02x) r%d = addr_space_cast(r%d, %u, %u)\n",
+			verbose(cbs->private_data, "(%02x) r%d = addr_space_cast(r%d, %u, %u)",
 				insn->code, insn->dst_reg,
 				insn->src_reg, ((u32)insn->imm) >> 16, (u16)insn->imm);
 		} else if (is_mov_percpu_addr(insn)) {
-			verbose(cbs->private_data, "(%02x) r%d = &(void __percpu *)(r%d)\n",
+			verbose(cbs->private_data, "(%02x) r%d = &(void __percpu *)(r%d)",
 				insn->code, insn->dst_reg, insn->src_reg);
 		} else if (BPF_SRC(insn->code) == BPF_X) {
-			verbose(cbs->private_data, "(%02x) %c%d %s %s%c%d\n",
+			verbose(cbs->private_data, "(%02x) %c%d %s %s%c%d",
 				insn->code, class == BPF_ALU ? 'w' : 'r',
 				insn->dst_reg,
 				is_sdiv_smod(insn) ? bpf_alu_sign_string[BPF_OP(insn->code) >> 4]
@@ -218,7 +218,7 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 				class == BPF_ALU ? 'w' : 'r',
 				insn->src_reg);
 		} else {
-			verbose(cbs->private_data, "(%02x) %c%d %s %d\n",
+			verbose(cbs->private_data, "(%02x) %c%d %s %d",
 				insn->code, class == BPF_ALU ? 'w' : 'r',
 				insn->dst_reg,
 				is_sdiv_smod(insn) ? bpf_alu_sign_string[BPF_OP(insn->code) >> 4]
@@ -227,7 +227,7 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 		}
 	} else if (class == BPF_STX) {
 		if (BPF_MODE(insn->code) == BPF_MEM)
-			verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = r%d\n",
+			verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = r%d",
 				insn->code,
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 				insn->dst_reg,
@@ -235,7 +235,7 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 		else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
 			 (insn->imm == BPF_ADD || insn->imm == BPF_AND ||
 			  insn->imm == BPF_OR || insn->imm == BPF_XOR)) {
-			verbose(cbs->private_data, "(%02x) lock *(%s *)(r%d %+d) %s r%d\n",
+			verbose(cbs->private_data, "(%02x) lock *(%s *)(r%d %+d) %s r%d",
 				insn->code,
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 				insn->dst_reg, insn->off,
@@ -246,7 +246,7 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 			    insn->imm == (BPF_AND | BPF_FETCH) ||
 			    insn->imm == (BPF_OR | BPF_FETCH) ||
 			    insn->imm == (BPF_XOR | BPF_FETCH))) {
-			verbose(cbs->private_data, "(%02x) r%d = atomic%s_fetch_%s((%s *)(r%d %+d), r%d)\n",
+			verbose(cbs->private_data, "(%02x) r%d = atomic%s_fetch_%s((%s *)(r%d %+d), r%d)",
 				insn->code, insn->src_reg,
 				BPF_SIZE(insn->code) == BPF_DW ? "64" : "",
 				bpf_atomic_alu_string[BPF_OP(insn->imm) >> 4],
@@ -254,7 +254,7 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 				insn->dst_reg, insn->off, insn->src_reg);
 		} else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
 			   insn->imm == BPF_CMPXCHG) {
-			verbose(cbs->private_data, "(%02x) r0 = atomic%s_cmpxchg((%s *)(r%d %+d), r0, r%d)\n",
+			verbose(cbs->private_data, "(%02x) r0 = atomic%s_cmpxchg((%s *)(r%d %+d), r0, r%d)",
 				insn->code,
 				BPF_SIZE(insn->code) == BPF_DW ? "64" : "",
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
@@ -262,44 +262,44 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 				insn->src_reg);
 		} else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
 			   insn->imm == BPF_XCHG) {
-			verbose(cbs->private_data, "(%02x) r%d = atomic%s_xchg((%s *)(r%d %+d), r%d)\n",
+			verbose(cbs->private_data, "(%02x) r%d = atomic%s_xchg((%s *)(r%d %+d), r%d)",
 				insn->code, insn->src_reg,
 				BPF_SIZE(insn->code) == BPF_DW ? "64" : "",
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 				insn->dst_reg, insn->off, insn->src_reg);
 		} else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
 			   insn->imm == BPF_LOAD_ACQ) {
-			verbose(cbs->private_data, "(%02x) r%d = load_acquire((%s *)(r%d %+d))\n",
+			verbose(cbs->private_data, "(%02x) r%d = load_acquire((%s *)(r%d %+d))",
 				insn->code, insn->dst_reg,
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 				insn->src_reg, insn->off);
 		} else if (BPF_MODE(insn->code) == BPF_ATOMIC &&
 			   insn->imm == BPF_STORE_REL) {
-			verbose(cbs->private_data, "(%02x) store_release((%s *)(r%d %+d), r%d)\n",
+			verbose(cbs->private_data, "(%02x) store_release((%s *)(r%d %+d), r%d)",
 				insn->code,
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 				insn->dst_reg, insn->off, insn->src_reg);
 		} else {
-			verbose(cbs->private_data, "BUG_%02x\n", insn->code);
+			verbose(cbs->private_data, "BUG_%02x", insn->code);
 		}
 	} else if (class == BPF_ST) {
 		if (BPF_MODE(insn->code) == BPF_MEM) {
-			verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = %d\n",
+			verbose(cbs->private_data, "(%02x) *(%s *)(r%d %+d) = %d",
 				insn->code,
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 				insn->dst_reg,
 				insn->off, insn->imm);
 		} else if (BPF_MODE(insn->code) == 0xc0 /* BPF_NOSPEC, no UAPI */) {
-			verbose(cbs->private_data, "(%02x) nospec\n", insn->code);
+			verbose(cbs->private_data, "(%02x) nospec", insn->code);
 		} else {
-			verbose(cbs->private_data, "BUG_st_%02x\n", insn->code);
+			verbose(cbs->private_data, "BUG_st_%02x", insn->code);
 		}
 	} else if (class == BPF_LDX) {
 		if (BPF_MODE(insn->code) != BPF_MEM && BPF_MODE(insn->code) != BPF_MEMSX) {
-			verbose(cbs->private_data, "BUG_ldx_%02x\n", insn->code);
+			verbose(cbs->private_data, "BUG_ldx_%02x", insn->code);
 			return;
 		}
-		verbose(cbs->private_data, "(%02x) r%d = *(%s *)(r%d %+d)\n",
+		verbose(cbs->private_data, "(%02x) r%d = *(%s *)(r%d %+d)",
 			insn->code, insn->dst_reg,
 			BPF_MODE(insn->code) == BPF_MEM ?
 				 bpf_ldst_string[BPF_SIZE(insn->code) >> 3] :
@@ -307,12 +307,12 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 			insn->src_reg, insn->off);
 	} else if (class == BPF_LD) {
 		if (BPF_MODE(insn->code) == BPF_ABS) {
-			verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[%d]\n",
+			verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[%d]",
 				insn->code,
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 				insn->imm);
 		} else if (BPF_MODE(insn->code) == BPF_IND) {
-			verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[r%d + %d]\n",
+			verbose(cbs->private_data, "(%02x) r0 = *(%s *)skb[r%d + %d]",
 				insn->code,
 				bpf_ldst_string[BPF_SIZE(insn->code) >> 3],
 				insn->src_reg, insn->imm);
@@ -332,12 +332,12 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 			if (is_ptr && !allow_ptr_leaks)
 				imm = 0;
 
-			verbose(cbs->private_data, "(%02x) r%d = %s\n",
+			verbose(cbs->private_data, "(%02x) r%d = %s",
 				insn->code, insn->dst_reg,
 				__func_imm_name(cbs, insn, imm,
 						tmp, sizeof(tmp)));
 		} else {
-			verbose(cbs->private_data, "BUG_ld_%02x\n", insn->code);
+			verbose(cbs->private_data, "BUG_ld_%02x", insn->code);
 			return;
 		}
 	} else if (class == BPF_JMP32 || class == BPF_JMP) {
@@ -347,35 +347,35 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 			char tmp[64];
 
 			if (insn->src_reg == BPF_PSEUDO_CALL) {
-				verbose(cbs->private_data, "(%02x) call pc%s\n",
+				verbose(cbs->private_data, "(%02x) call pc%s",
 					insn->code,
 					__func_get_name(cbs, insn,
 							tmp, sizeof(tmp)));
 			} else {
 				strcpy(tmp, "unknown");
-				verbose(cbs->private_data, "(%02x) call %s#%d\n", insn->code,
+				verbose(cbs->private_data, "(%02x) call %s#%d", insn->code,
 					__func_get_name(cbs, insn,
 							tmp, sizeof(tmp)),
 					insn->imm);
 			}
 		} else if (insn->code == (BPF_JMP | BPF_JA)) {
-			verbose(cbs->private_data, "(%02x) goto pc%+d\n",
+			verbose(cbs->private_data, "(%02x) goto pc%+d",
 				insn->code, insn->off);
 		} else if (insn->code == (BPF_JMP | BPF_JA | BPF_X)) {
-			verbose(cbs->private_data, "(%02x) gotox r%d\n",
+			verbose(cbs->private_data, "(%02x) gotox r%d",
 				insn->code, insn->dst_reg);
 		} else if (insn->code == (BPF_JMP | BPF_JCOND) &&
 			   insn->src_reg == BPF_MAY_GOTO) {
-			verbose(cbs->private_data, "(%02x) may_goto pc%+d\n",
+			verbose(cbs->private_data, "(%02x) may_goto pc%+d",
 				insn->code, insn->off);
 		} else if (insn->code == (BPF_JMP32 | BPF_JA)) {
-			verbose(cbs->private_data, "(%02x) gotol pc%+d\n",
+			verbose(cbs->private_data, "(%02x) gotol pc%+d",
 				insn->code, insn->imm);
 		} else if (insn->code == (BPF_JMP | BPF_EXIT)) {
-			verbose(cbs->private_data, "(%02x) exit\n", insn->code);
+			verbose(cbs->private_data, "(%02x) exit", insn->code);
 		} else if (BPF_SRC(insn->code) == BPF_X) {
 			verbose(cbs->private_data,
-				"(%02x) if %c%d %s %c%d goto pc%+d\n",
+				"(%02x) if %c%d %s %c%d goto pc%+d",
 				insn->code, class == BPF_JMP32 ? 'w' : 'r',
 				insn->dst_reg,
 				bpf_jmp_string[BPF_OP(insn->code) >> 4],
@@ -383,14 +383,14 @@ void print_bpf_insn(const struct bpf_insn_cbs *cbs,
 				insn->src_reg, insn->off);
 		} else {
 			verbose(cbs->private_data,
-				"(%02x) if %c%d %s 0x%x goto pc%+d\n",
+				"(%02x) if %c%d %s 0x%x goto pc%+d",
 				insn->code, class == BPF_JMP32 ? 'w' : 'r',
 				insn->dst_reg,
 				bpf_jmp_string[BPF_OP(insn->code) >> 4],
 				(u32)insn->imm, insn->off);
 		}
 	} else {
-		verbose(cbs->private_data, "(%02x) %s\n",
+		verbose(cbs->private_data, "(%02x) %s",
 			insn->code, bpf_class_string[class]);
 	}
 }
diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
index a0bddada7964e..661e2d13a6042 100644
--- a/kernel/bpf/fixups.c
+++ b/kernel/bpf/fixups.c
@@ -20,6 +20,26 @@ static bool is_cmpxchg_insn(const struct bpf_insn *insn)
 	       insn->imm == BPF_CMPXCHG;
 }
 
+/* Returns true if 'insn' is an address space cast instruction translated as BPF_ALU op */
+static bool is_addr_space_cast32(struct bpf_prog *prog, const struct bpf_insn *insn)
+{
+	struct bpf_map *arena = (struct bpf_map *)prog->aux->arena;
+
+	if (insn->code != (BPF_ALU64 | BPF_MOV | BPF_X) || insn->off != BPF_ADDR_SPACE_CAST)
+		return false;
+
+	/* cast from as(1) to as(0) */
+	if (insn->imm == 1)
+		return true;
+
+	/* cast from as(0) to as(1) */
+	if (insn->imm == 1 << 16)
+		return arena && arena->map_flags & BPF_F_NO_USER_CONV;
+
+	/* non-BPF_F_NO_USER_CONV cast from as(0) to as(1) should be handled by JIT */
+	return false;
+}
+
 /* Return the regno defined by the insn, or -1. */
 static int insn_def_regno(const struct bpf_insn *insn)
 {
@@ -44,15 +64,60 @@ static int insn_def_regno(const struct bpf_insn *insn)
 	}
 }
 
-/* Return TRUE if INSN has defined any 32-bit value explicitly. */
-static bool insn_has_def32(struct bpf_insn *insn)
+/*
+ * For use only in combination with insn_def_regno() >= 0.
+ * Returns TRUE if the destination register operates on 64-bit,
+ * otherwise return FALSE.
+ */
+static bool bpf_is_reg64(struct bpf_prog *prog, struct bpf_insn *insn)
 {
-	int dst_reg = insn_def_regno(insn);
+	u8 class = BPF_CLASS(insn->code);
+	u8 mode = BPF_MODE(insn->code);
+	u8 size = BPF_SIZE(insn->code);
+	u8 op = BPF_OP(insn->code);
+	bool mode_mem;
+
+	/* subregister endiness swap */
+	if ((class == BPF_ALU || class == BPF_ALU64) && op == BPF_END && insn->imm != 64)
+		return false;
+
+	/* w0 += 1 */
+	if (class == BPF_ALU && op != BPF_END)
+		return false;
+
+	/* address space casts converted to BPF_ALU, see bpf_do_misc_fixups() */
+	if (is_addr_space_cast32(prog, insn))
+		return false;
 
-	if (dst_reg == -1)
+	/* non 64-bit, non signed extended loads */
+	mode_mem = mode == BPF_MEM || mode == BPF_PROBE_MEM || mode == BPF_PROBE_MEM32;
+	if (class == BPF_LDX && mode_mem && size != BPF_DW)
 		return false;
 
-	return !bpf_is_reg64(insn, dst_reg, NULL, DST_OP);
+	/* atomics, see insn_def_regno() */
+	if (class == BPF_STX && size != BPF_DW)
+		return false;
+
+	/* both LD_IND and LD_ABS return 32-bit data. */
+	if (class == BPF_LD && (mode == BPF_IND || mode == BPF_ABS))
+		return false;
+
+	/* Conservatively return true at default. */
+	return true;
+}
+
+/*
+ * Return the 32-bit subregister defined by INSN, or -1 if INSN does not
+ * explicitly define a 32-bit value.
+ */
+int bpf_insn_def32(struct bpf_prog *prog, struct bpf_insn *insn)
+{
+	int dst_reg = insn_def_regno(insn);
+
+	if (dst_reg < 0 || bpf_is_reg64(prog, insn))
+		return -1;
+
+	return dst_reg;
 }
 
 static int kfunc_desc_cmp_by_imm_off(const void *a, const void *b)
@@ -169,7 +234,7 @@ static void adjust_insn_aux_data(struct bpf_verifier_env *env,
 	 * (cnt == 1) is taken or not. There is no guarantee INSN at OFF is the
 	 * original insn at old prog.
 	 */
-	data[off].zext_dst = insn_has_def32(insn + off + cnt - 1);
+	data[off].zext_dst = bpf_insn_def32(new_prog, insn + off + cnt - 1) >= 0;
 
 	if (cnt == 1)
 		return;
@@ -181,7 +246,7 @@ static void adjust_insn_aux_data(struct bpf_verifier_env *env,
 	for (i = off; i < off + cnt - 1; i++) {
 		/* Expand insni[off]'s seen count to the patched range. */
 		data[i].seen = old_seen;
-		data[i].zext_dst = insn_has_def32(insn + i);
+		data[i].zext_dst = bpf_insn_def32(new_prog, insn + i) >= 0;
 	}
 
 	/*
@@ -616,11 +681,7 @@ int bpf_opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,
 			if (load_reg == -1)
 				continue;
 
-			/* NOTE: arg "reg" (the fourth one) is only used for
-			 *       BPF_STX + SRC_OP, so it is safe to pass NULL
-			 *       here.
-			 */
-			if (bpf_is_reg64(&insn, load_reg, NULL, DST_OP)) {
+			if (bpf_is_reg64(env->prog, &insn)) {
 				if (class == BPF_LD &&
 				    BPF_MODE(code) == BPF_IMM)
 					i++;
@@ -1513,15 +1574,12 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
 	}
 
 	for (i = 0; i < insn_cnt;) {
-		if (insn->code == (BPF_ALU64 | BPF_MOV | BPF_X) && insn->imm) {
-			if ((insn->off == BPF_ADDR_SPACE_CAST && insn->imm == 1) ||
-			    (((struct bpf_map *)env->prog->aux->arena)->map_flags & BPF_F_NO_USER_CONV)) {
-				/* convert to 32-bit mov that clears upper 32-bit */
-				insn->code = BPF_ALU | BPF_MOV | BPF_X;
-				/* clear off and imm, so it's a normal 'wX = wY' from JIT pov */
-				insn->off = 0;
-				insn->imm = 0;
-			} /* cast from as(0) to as(1) should be handled by JIT */
+		if (is_addr_space_cast32(env->prog, insn)) {
+			/* convert to 32-bit mov that clears upper 32-bit */
+			insn->code = BPF_ALU | BPF_MOV | BPF_X;
+			/* clear off and imm, so it's a normal 'wX = wY' from JIT pov */
+			insn->off = 0;
+			insn->imm = 0;
 			goto next_insn;
 		}
 
diff --git a/kernel/bpf/liveness.c b/kernel/bpf/liveness.c
index 0aadfbae0acc5..ef9a5a9228872 100644
--- a/kernel/bpf/liveness.c
+++ b/kernel/bpf/liveness.c
@@ -497,7 +497,6 @@ static void print_instance(struct bpf_verifier_env *env, struct func_instance *i
 		pos = env->log.end_pos;
 		verbose(env, "%3d: ", insn_idx);
 		bpf_verbose_insn(env, &insns[insn_idx]);
-		bpf_vlog_reset(&env->log, env->log.end_pos - 1); /* remove \n */
 		insn_pos = env->log.end_pos;
 		verbose(env, "%*c;", bpf_vlog_alignment(insn_pos - pos), ' ');
 		pos = env->log.end_pos;
@@ -1043,7 +1042,6 @@ static void arg_track_log(struct bpf_verifier_env *env, struct bpf_insn *insn, i
 		if (!printed) {
 			verbose(env, "%3d: ", idx);
 			bpf_verbose_insn(env, insn);
-			bpf_vlog_reset(&env->log, env->log.end_pos - 1);
 			printed = true;
 		}
 		verbose(env, "\tr%d: ", i); verbose_arg_track(env, &at_in[i]);
@@ -1058,7 +1056,6 @@ static void arg_track_log(struct bpf_verifier_env *env, struct bpf_insn *insn, i
 		if (!printed) {
 			verbose(env, "%3d: ", idx);
 			bpf_verbose_insn(env, insn);
-			bpf_vlog_reset(&env->log, env->log.end_pos - 1);
 			printed = true;
 		}
 		verbose(env, "\tsa%d: ", i); verbose_arg_track(env, &at_in[ai]);
@@ -1070,7 +1067,6 @@ static void arg_track_log(struct bpf_verifier_env *env, struct bpf_insn *insn, i
 		if (!printed) {
 			verbose(env, "%3d: ", idx);
 			bpf_verbose_insn(env, insn);
-			bpf_vlog_reset(&env->log, env->log.end_pos - 1);
 			printed = true;
 		}
 		verbose(env, "\tfp%+d: ", -(i + 1) * 8); verbose_arg_track(env, &at_stack_in[i]);
@@ -1545,6 +1541,7 @@ static void print_subprog_arg_access(struct bpf_verifier_env *env,
 
 		verbose(env, "%3d: ", idx);
 		bpf_verbose_insn(env, &insns[idx]);
+		verbose(env, "\n");
 
 		/* Collect what needs printing */
 		if (is_ldx_stx_call &&
@@ -2050,29 +2047,38 @@ int bpf_compute_subprog_arg_access(struct bpf_verifier_env *env)
 
 /* Each field is a register bitmask */
 struct insn_live_regs {
-	u16 use;	/* registers read by instruction */
-	u16 def;	/* registers written by instruction */
-	u16 in;		/* registers that may be alive before instruction */
-	u16 out;	/* registers that may be alive after instruction */
+	u32 use;	/* registers read by instruction */
+	u32 def;	/* registers written by instruction */
+	u32 in;		/* registers that may be alive before instruction */
+	u32 out;	/* registers that may be alive after instruction */
 };
 
 /* Bitmask with 1s for all caller saved registers */
 #define ALL_CALLER_SAVED_REGS ((1u << CALLER_SAVED_REGS) - 1)
 
+static inline u32 reg32_mask(u32 n) { return BIT(n); }
+static inline u32 reg64_mask(u32 n) { return BIT(n) | BIT(n + 16); }
+static inline u32 mask_widen(u32 m) { return m | (m << 16); }
+static inline u16 mask_lo(u32 m) { return (u16)m; }
+static inline u16 mask_hi(u32 m) { return (u16)(m >> 16); }
+
 /* Compute info->{use,def} fields for the instruction */
 static void compute_insn_live_regs(struct bpf_verifier_env *env,
 				   struct bpf_insn *insn,
 				   struct insn_live_regs *info)
 {
 	struct bpf_call_summary cs;
-	u8 class = BPF_CLASS(insn->code);
-	u8 code = BPF_OP(insn->code);
-	u8 mode = BPF_MODE(insn->code);
-	u16 src = BIT(insn->src_reg);
-	u16 dst = BIT(insn->dst_reg);
-	u16 r0  = BIT(0);
-	u16 def = 0;
-	u16 use = 0xffff;
+	const u8 class = BPF_CLASS(insn->code);
+	const u8 code = BPF_OP(insn->code);
+	const u8 mode = BPF_MODE(insn->code);
+	const u8 size = BPF_SIZE(insn->code);
+	const u32 src = reg64_mask(insn->src_reg);
+	const u32 dst = reg64_mask(insn->dst_reg);
+	const u32 src32 = mask_lo(src);
+	const u32 dst32 = mask_lo(dst);
+	const u32 r0  = reg64_mask(0);
+	u32 def = 0;
+	u32 use = U32_MAX;
 
 	switch (class) {
 	case BPF_LD:
@@ -2083,8 +2089,8 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,
 				use = 0;
 			}
 			break;
-		case BPF_LD | BPF_ABS:
-		case BPF_LD | BPF_IND:
+		case BPF_ABS:
+		case BPF_IND:
 			/* stick with defaults */
 			break;
 		}
@@ -2092,7 +2098,15 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,
 	case BPF_LDX:
 		switch (mode) {
 		case BPF_MEM:
+			/* a narrow load still redefines the whole register */
+			def = dst;
+			use = src;
+			break;
 		case BPF_MEMSX:
+			/*
+			 * sign extension defines the whole register;
+			 * src holds a pointer, hence is used as 64-bit.
+			 */
 			def = dst;
 			use = src;
 			break;
@@ -2110,12 +2124,19 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,
 		switch (mode) {
 		case BPF_MEM:
 			def = 0;
-			use = dst | src;
+			use = dst | (size == BPF_DW ? src : src32);
 			break;
-		case BPF_ATOMIC:
+		case BPF_ATOMIC: {
+			/*
+			 * dst holds a pointer and is always used as 64-bit;
+			 * the value operand and r0 are read as 32-bit for BPF_W atomics.
+			 */
+			u32 srcv = size == BPF_DW ? src : src32;
+			u32 r0v  = size == BPF_DW ? r0 : mask_lo(r0);
+
 			switch (insn->imm) {
 			case BPF_CMPXCHG:
-				use = r0 | dst | src;
+				use = r0v | dst | srcv;
 				def = r0;
 				break;
 			case BPF_LOAD_ACQ:
@@ -2124,10 +2145,10 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,
 				break;
 			case BPF_STORE_REL:
 				def = 0;
-				use = dst | src;
+				use = dst | srcv;
 				break;
 			default:
-				use = dst | src;
+				use = dst | srcv;
 				if (insn->imm & BPF_FETCH)
 					def = src;
 				else
@@ -2135,6 +2156,7 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,
 			}
 			break;
 		}
+		}
 		break;
 	case BPF_ALU:
 	case BPF_ALU64:
@@ -2148,14 +2170,14 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,
 			if (BPF_SRC(insn->code) == BPF_K)
 				use = 0;
 			else
-				use = src;
+				use = class == BPF_ALU64 ? src : src32;
 			break;
 		default:
 			def = dst;
 			if (BPF_SRC(insn->code) == BPF_K)
-				use = dst;
+				use = class == BPF_ALU64 ? dst : dst32;
 			else
-				use = dst | src;
+				use = class == BPF_ALU64 ? (dst | src) : (dst32 | src32);
 		}
 		break;
 	case BPF_JMP:
@@ -2181,13 +2203,14 @@ static void compute_insn_live_regs(struct bpf_verifier_env *env,
 			use = def & ~BIT(BPF_REG_0);
 			if (bpf_get_call_summary(env, insn, &cs))
 				use = GENMASK(min_t(u8, cs.num_params, MAX_BPF_FUNC_REG_ARGS), 1);
+			def = mask_widen(def);
+			use = mask_widen(use);
 			break;
 		default:
 			def = 0;
-			if (BPF_SRC(insn->code) == BPF_K)
-				use = dst;
-			else
-				use = dst | src;
+			use = class == BPF_JMP ? dst : dst32;
+			if (BPF_SRC(insn->code) == BPF_X)
+				use |= class == BPF_JMP ? src : src32;
 		}
 		break;
 	}
@@ -2209,6 +2232,7 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)
 	struct bpf_insn *insns = env->prog->insnsi;
 	struct insn_live_regs *state;
 	int insn_cnt = env->prog->len;
+	u64 pos, insn_pos;
 	int err = 0, i, j;
 	bool changed;
 
@@ -2252,8 +2276,8 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)
 			int insn_idx = env->cfg.insn_postorder[i];
 			struct insn_live_regs *live = &state[insn_idx];
 			struct bpf_iarray *succ;
-			u16 new_out = 0;
-			u16 new_in = 0;
+			u32 new_out = 0;
+			u32 new_in = 0;
 
 			succ = bpf_insn_successors(env, insn_idx);
 			for (int s = 0; s < succ->cnt; ++s)
@@ -2267,8 +2291,20 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)
 		}
 	}
 
-	for (i = 0; i < insn_cnt; ++i)
-		insn_aux[i].live_regs_before = state[i].in;
+	for (i = 0; i < insn_cnt; ++i) {
+		int def32 = bpf_insn_def32(env->prog, &insns[i]);
+		u32 out = state[i].out;
+		u32 in = state[i].in;
+
+		insn_aux[i].live_regs_before = mask_lo(in) | mask_hi(in);
+		/*
+		 * On architectures where 32-bit operations do not reset upper halves
+		 * of the registers, the verifier needs to zero extend a destination
+		 * register if an instruction defines a 32-bit subregister and the
+		 * upper half of that register is alive after the instruction.
+		 */
+		insn_aux[i].zext_dst = def32 >= 0 && (mask_hi(out) & BIT(def32));
+	}
 
 	if (env->log.level & BPF_LOG_LEVEL2) {
 		verbose(env, "Live regs before insn:\n");
@@ -2284,7 +2320,12 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)
 				else
 					verbose(env, ".");
 			verbose(env, " ");
+			pos = env->log.end_pos;
 			bpf_verbose_insn(env, &insns[i]);
+			insn_pos = env->log.end_pos;
+			if (insn_aux[i].zext_dst)
+				verbose(env, "%*c; zext", bpf_vlog_alignment(insn_pos - pos), ' ');
+			verbose(env, "\n");
 			if (bpf_is_ldimm64(&insns[i]))
 				i++;
 		}
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e6233c0081d10..14f7516458749 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -2132,12 +2132,9 @@ static int reg_bounds_sanity_check(struct bpf_verifier_env *env,
 /* Mark a register as having a completely unknown (scalar) value. */
 void bpf_mark_reg_unknown_imprecise(struct bpf_reg_state *reg)
 {
-	s32 subreg_def = reg->subreg_def;
-
 	memset(reg, 0, sizeof(*reg));
 	reg->type = SCALAR_VALUE;
 	reg->var_off = tnum_unknown;
-	reg->subreg_def = subreg_def;
 	__mark_reg_unbounded(reg);
 }
 
@@ -2213,7 +2210,6 @@ static int mark_btf_ld_reg(struct bpf_verifier_env *env,
 	}
 }
 
-#define DEF_NOT_SUBREG	(0)
 static void init_reg_state(struct bpf_verifier_env *env,
 			   struct bpf_func_state *state)
 {
@@ -2222,7 +2218,6 @@ static void init_reg_state(struct bpf_verifier_env *env,
 
 	for (i = 0; i < MAX_BPF_REG; i++) {
 		bpf_mark_reg_not_init(env, &regs[i]);
-		regs[i].subreg_def = DEF_NOT_SUBREG;
 	}
 
 	/* frame pointer */
@@ -3058,120 +3053,14 @@ static void mark_stack_slots_scratched(struct bpf_verifier_env *env,
 		mark_stack_slot_scratched(env, spi - i);
 }
 
-/* This function is supposed to be used by the following 32-bit optimization
- * code only. It returns TRUE if the source or destination register operates
- * on 64-bit, otherwise return FALSE.
- */
-bool bpf_is_reg64(struct bpf_insn *insn,
-	      u32 regno, struct bpf_reg_state *reg, enum bpf_reg_arg_type t)
-{
-	u8 code, class, op;
-
-	code = insn->code;
-	class = BPF_CLASS(code);
-	op = BPF_OP(code);
-	if (class == BPF_JMP) {
-		/* BPF_EXIT for "main" will reach here. Return TRUE
-		 * conservatively.
-		 */
-		if (op == BPF_EXIT)
-			return true;
-		if (op == BPF_CALL) {
-			/* BPF to BPF call will reach here because of marking
-			 * caller saved clobber with DST_OP_NO_MARK for which we
-			 * don't care the register def because they are anyway
-			 * marked as NOT_INIT already.
-			 */
-			if (insn->src_reg == BPF_PSEUDO_CALL)
-				return false;
-			/* Helper call will reach here because of arg type
-			 * check, conservatively return TRUE.
-			 */
-			if (t == SRC_OP)
-				return true;
-
-			return false;
-		}
-	}
-
-	if (class == BPF_ALU64 && op == BPF_END && (insn->imm == 16 || insn->imm == 32))
-		return false;
-
-	if (class == BPF_ALU64 || class == BPF_JMP ||
-	    (class == BPF_ALU && op == BPF_END && insn->imm == 64))
-		return true;
-
-	if (class == BPF_ALU || class == BPF_JMP32)
-		return false;
-
-	if (class == BPF_LDX) {
-		if (t != SRC_OP)
-			return BPF_SIZE(code) == BPF_DW || BPF_MODE(code) == BPF_MEMSX;
-		/* LDX source must be ptr. */
-		return true;
-	}
-
-	if (class == BPF_STX) {
-		/* BPF_STX (including atomic variants) has one or more source
-		 * operands, one of which is a ptr. Check whether the caller is
-		 * asking about it.
-		 */
-		if (t == SRC_OP && reg->type != SCALAR_VALUE)
-			return true;
-		return BPF_SIZE(code) == BPF_DW;
-	}
-
-	if (class == BPF_LD) {
-		u8 mode = BPF_MODE(code);
-
-		/* LD_IMM64 */
-		if (mode == BPF_IMM)
-			return true;
-
-		/* Both LD_IND and LD_ABS return 32-bit data. */
-		if (t != SRC_OP)
-			return  false;
-
-		/* Implicit ctx ptr. */
-		if (regno == BPF_REG_6)
-			return true;
-
-		/* Explicit source could be any width. */
-		return true;
-	}
-
-	if (class == BPF_ST)
-		/* The only source register for BPF_ST is a ptr. */
-		return true;
-
-	/* Conservatively return true at default. */
-	return true;
-}
-
-static void mark_insn_zext(struct bpf_verifier_env *env,
-			   struct bpf_reg_state *reg)
-{
-	s32 def_idx = reg->subreg_def;
-
-	if (def_idx == DEF_NOT_SUBREG)
-		return;
-
-	env->insn_aux_data[def_idx - 1].zext_dst = true;
-	/* The dst will be zero extended, so won't be sub-register anymore. */
-	reg->subreg_def = DEF_NOT_SUBREG;
-}
-
 static int __check_reg_arg(struct bpf_verifier_env *env, struct bpf_reg_state *regs, u32 regno,
 			   enum bpf_reg_arg_type t)
 {
-	struct bpf_insn *insn = env->prog->insnsi + env->insn_idx;
 	struct bpf_reg_state *reg;
-	bool rw64;
 
 	mark_reg_scratched(env, regno);
 
 	reg = &regs[regno];
-	rw64 = bpf_is_reg64(insn, regno, reg, t);
 	if (t == SRC_OP) {
 		/* check whether register used as source operand can be read */
 		if (reg->type == NOT_INIT) {
@@ -3182,9 +3071,6 @@ static int __check_reg_arg(struct bpf_verifier_env *env, struct bpf_reg_state *r
 		if (regno == BPF_REG_FP)
 			return 0;
 
-		if (rw64)
-			mark_insn_zext(env, reg);
-
 		return 0;
 	} else {
 		/* check whether register used as dest operand can be written to */
@@ -3192,7 +3078,6 @@ static int __check_reg_arg(struct bpf_verifier_env *env, struct bpf_reg_state *r
 			verbose(env, "frame pointer is read only\n");
 			return -EACCES;
 		}
-		reg->subreg_def = rw64 ? DEF_NOT_SUBREG : env->insn_idx + 1;
 		if (t == DST_OP)
 			mark_reg_unknown(env, regs, regno);
 	}
@@ -3848,11 +3733,6 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
 
 			if (size <= spill_size &&
 			    bpf_stack_narrow_access_ok(off, size, spill_size)) {
-				/* The earlier check_reg_arg() has decided the
-				 * subreg_def for this insn.  Save it first.
-				 */
-				s32 subreg_def = state->regs[dst_regno].subreg_def;
-
 				if (env->bpf_capable && size == 4 && spill_size == 4 &&
 				    get_reg_width(reg) <= 32)
 					/* Ensure stack slot has an ID to build a relation
@@ -3860,7 +3740,6 @@ static int check_stack_read_fixed_off(struct bpf_verifier_env *env,
 					 */
 					assign_scalar_id_before_mov(env, reg);
 				state->regs[dst_regno] = *reg;
-				state->regs[dst_regno].subreg_def = subreg_def;
 
 				/* Break the relation on a narrowing fill.
 				 * coerce_reg_to_size will adjust the boundaries.
@@ -6311,12 +6190,6 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b
 			} else {
 				mark_reg_known_zero(env, regs,
 						    value_regno);
-				/* A load of ctx field could have different
-				 * actual load size with the one encoded in the
-				 * insn. When the dst is PTR, it is for sure not
-				 * a sub-register.
-				 */
-				regs[value_regno].subreg_def = DEF_NOT_SUBREG;
 				if (base_type(info.reg_type) == PTR_TO_BTF_ID) {
 					regs[value_regno].btf = info.btf;
 					regs[value_regno].btf_id = info.btf_id;
@@ -7415,10 +7288,6 @@ static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_stat
 			if (spi < 0)
 				return spi;
 
-			/*
-			 * For CONST_PTR_TO_DYNPTR, reg is already scratched by check_reg_arg
-			 * in check_helper_call and mark_btf_func_reg_size in check_kfunc_call.
-			 */
 			mark_stack_slots_scratched(env, spi, BPF_DYNPTR_NR_SLOTS);
 
 			reg = &state->stack[spi].spilled_ptr;
@@ -9522,7 +9391,6 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 		/* All non-void global functions return a 64-bit SCALAR_VALUE. */
 		if (!subprog_returns_void(env, subprog)) {
 			mark_reg_unknown(env, caller->regs, BPF_REG_0);
-			caller->regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG;
 		}
 
 		if (env->subprog_info[subprog].might_throw) {
@@ -10542,9 +10410,6 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
 	}
 	invalidate_outgoing_stack_args(env, cur_func(env));
 
-	/* helper call returns 64-bit value. */
-	regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG;
-
 	/* update return register (already marked as written above) */
 	ret_type = fn->ret_type;
 	ret_flag = type_flag(ret_type);
@@ -10784,30 +10649,6 @@ static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn
 	return 0;
 }
 
-/* mark_btf_func_reg_size() is used when the reg size is determined by
- * the BTF func_proto's return value size and argument.
- */
-static void __mark_btf_func_reg_size(struct bpf_verifier_env *env, struct bpf_reg_state *regs,
-				     u32 regno, size_t reg_size)
-{
-	struct bpf_reg_state *reg = &regs[regno];
-
-	if (regno == BPF_REG_0) {
-		/* Function return value */
-		reg->subreg_def = reg_size == sizeof(u64) ?
-			DEF_NOT_SUBREG : env->insn_idx + 1;
-	} else if (reg_size == sizeof(u64)) {
-		/* Function argument */
-		mark_insn_zext(env, reg);
-	}
-}
-
-static void mark_btf_func_reg_size(struct bpf_verifier_env *env, u32 regno,
-				   size_t reg_size)
-{
-	return __mark_btf_func_reg_size(env, cur_regs(env), regno, reg_size);
-}
-
 static bool is_kfunc_acquire(struct bpf_call_arg_meta *meta)
 {
 	return meta->kfunc_flags & KF_ACQUIRE;
@@ -13026,7 +12867,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 	struct bpf_call_arg_meta meta;
 	struct bpf_insn_aux_data *insn_aux;
 	int err, insn_idx = *insn_idx_p;
-	const struct btf_param *args;
 	u32 i, nargs, ptr_type_id;
 	struct bpf_kfunc_desc *desc;
 	struct btf *desc_btf;
@@ -13078,7 +12918,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 			verbose(env, "failed to mark s32 range for retval in forked state for lock\n");
 			return err;
 		}
-		__mark_btf_func_reg_size(env, regs, BPF_REG_0, sizeof(u32));
 	} else if (!insn->off && insn->imm == special_kfunc_list[KF___bpf_trap]) {
 		verbose(env, "unexpected __bpf_trap() due to uninitialized variable?\n");
 		return -EFAULT;
@@ -13231,7 +13070,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 		u32 regno = caller_saved[i];
 
 		bpf_mark_reg_not_init(env, &regs[regno]);
-		regs[regno].subreg_def = DEF_NOT_SUBREG;
 	}
 	invalidate_outgoing_stack_args(env, cur_func(env));
 
@@ -13253,7 +13091,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 		if (meta.btf == btf_vmlinux && (meta.func_id == special_kfunc_list[KF_bpf_res_spin_lock] ||
 		    meta.func_id == special_kfunc_list[KF_bpf_res_spin_lock_irqsave]))
 			__mark_reg_const_zero(env, &regs[BPF_REG_0]);
-		mark_btf_func_reg_size(env, BPF_REG_0, t->size);
 	} else if (btf_type_is_ptr(t)) {
 		ptr_type = btf_type_skip_modifiers(desc_btf, t->type, &ptr_type_id);
 		err = check_special_kfunc(env, &meta, regs, insn_aux, ptr_type, desc_btf);
@@ -13344,7 +13181,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 			/* For mark_ptr_or_null_reg, see 93c230e3f5bd6 */
 			regs[BPF_REG_0].id = ++env->id_gen;
 		}
-		mark_btf_func_reg_size(env, BPF_REG_0, sizeof(void *));
 		if (is_kfunc_acquire(&meta)) {
 			id = acquire_reference(env, insn_idx, 0);
 			if (id < 0)
@@ -13381,18 +13217,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 			caller_info->stack_arg_cnt = stack_arg_cnt;
 	}
 
-	args = (const struct btf_param *)(meta.func_proto + 1);
-	for (i = 0; i < min_t(int, nargs, MAX_BPF_FUNC_REG_ARGS); i++) {
-		u32 regno = i + 1;
-
-		t = btf_type_skip_modifiers(desc_btf, args[i].type, NULL);
-		if (btf_type_is_ptr(t))
-			mark_btf_func_reg_size(env, regno, sizeof(void *));
-		else
-			/* scalar. ensured by check_kfunc_args() */
-			mark_btf_func_reg_size(env, regno, t->size);
-	}
-
 	if (bpf_is_iter_next_kfunc(&meta)) {
 		err = process_iter_next_call(env, insn_idx, &meta);
 		if (err)
@@ -14886,14 +14710,14 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env,
 		if (dst_reg->type != PTR_TO_ARENA)
 			*dst_reg = *src_reg;
 
-		dst_reg->subreg_def = env->insn_idx + 1;
-
-		if (BPF_CLASS(insn->code) == BPF_ALU64)
+		if (BPF_CLASS(insn->code) == BPF_ALU64) {
 			/*
 			 * 32-bit operations zero upper bits automatically.
 			 * 64-bit operations need to be converted to 32.
 			 */
 			aux->needs_zext = true;
+			aux->zext_dst = true;
+		}
 
 		/* Any arithmetic operations are allowed on arena pointers */
 		return 0;
@@ -15089,18 +14913,14 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
 				if (insn->imm) {
 					/* off == BPF_ADDR_SPACE_CAST */
 					mark_reg_unknown(env, regs, insn->dst_reg);
-					if (insn->imm == 1) { /* cast from as(1) to as(0) */
+					if (insn->imm == 1) /* cast from as(1) to as(0) */
 						dst_reg->type = PTR_TO_ARENA;
-						/* PTR_TO_ARENA is 32-bit */
-						dst_reg->subreg_def = env->insn_idx + 1;
-					}
 				} else if (insn->off == 0) {
 					/* case: R1 = R2
 					 * copy register state to dest reg
 					 */
 					assign_scalar_id_before_mov(env, src_reg);
 					*dst_reg = *src_reg;
-					dst_reg->subreg_def = DEF_NOT_SUBREG;
 				} else {
 					/* case: R1 = (s8, s16 s32)R2 */
 					if (is_pointer_value(env, insn->src_reg)) {
@@ -15118,7 +14938,6 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
 						if (!no_sext)
 							clear_scalar_id(dst_reg);
 						coerce_reg_to_size_sx(dst_reg, insn->off >> 3);
-						dst_reg->subreg_def = DEF_NOT_SUBREG;
 					} else {
 						mark_reg_unknown(env, regs, insn->dst_reg);
 					}
@@ -15143,7 +14962,6 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
 						 */
 						if (!is_src_reg_u32)
 							clear_scalar_id(dst_reg);
-						dst_reg->subreg_def = env->insn_idx + 1;
 					} else {
 						/* case: W1 = (s8, s16)W2 */
 						bool no_sext = reg_umax(src_reg) < (1ULL << (insn->off - 1));
@@ -15153,7 +14971,6 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
 						*dst_reg = *src_reg;
 						if (!no_sext)
 							clear_scalar_id(dst_reg);
-						dst_reg->subreg_def = env->insn_idx + 1;
 						coerce_subreg_to_size_sx(dst_reg, insn->off >> 3);
 					}
 				} else {
@@ -16022,12 +15839,8 @@ static void sync_linked_regs(struct bpf_verifier_env *env, struct bpf_verifier_s
 			continue;
 		if ((!(reg->id & BPF_ADD_CONST) && !(known_reg->id & BPF_ADD_CONST)) ||
 		    reg->delta == known_reg->delta) {
-			s32 saved_subreg_def = reg->subreg_def;
-
 			*reg = *known_reg;
-			reg->subreg_def = saved_subreg_def;
 		} else {
-			s32 saved_subreg_def = reg->subreg_def;
 			s32 saved_off = reg->delta;
 			u32 saved_id = reg->id;
 
@@ -16037,12 +15850,11 @@ static void sync_linked_regs(struct bpf_verifier_env *env, struct bpf_verifier_s
 			/* reg = known_reg; reg += delta */
 			*reg = *known_reg;
 			/*
-			 * Must preserve off, id and subreg_def flag,
-			 * otherwise another sync_linked_regs() will be incorrect.
+			 * Must preserve off and id, otherwise another sync_linked_regs()
+			 * will be incorrect.
 			 */
 			reg->delta = saved_off;
 			reg->id = saved_id;
-			reg->subreg_def = saved_subreg_def;
 
 			scalar32_min_max_add(reg, &fake_reg);
 			scalar_min_max_add(reg, &fake_reg);
@@ -16477,8 +16289,6 @@ static int check_ld_abs(struct bpf_verifier_env *env, struct bpf_insn *insn)
 	 * Already marked as written above.
 	 */
 	mark_reg_unknown(env, regs, BPF_REG_0);
-	/* ld_abs load up to 32-bit skb data. */
-	regs[BPF_REG_0].subreg_def = env->insn_idx + 1;
 	/*
 	 * See bpf_gen_ld_abs() which emits a hidden BPF_EXIT with r0=0
 	 * which must be explored by the verifier when in a subprog.
@@ -16793,7 +16603,6 @@ bool bpf_get_call_summary(struct bpf_verifier_env *env, struct bpf_insn *call,
 	int i;
 
 	if (bpf_helper_call(call)) {
-
 		if (bpf_get_helper_proto(env, call->imm, &fn) < 0)
 			/* error would be reported later */
 			return false;
@@ -17538,6 +17347,7 @@ static int do_check(struct bpf_verifier_env *env)
 			env->prev_log_pos = env->log.end_pos;
 			verbose(env, "%d: ", env->insn_idx);
 			bpf_verbose_insn(env, insn);
+			verbose(env, "\n");
 			env->prev_insn_print_pos = env->log.end_pos - env->prev_log_pos;
 			env->prev_log_pos = env->log.end_pos;
 		}
diff --git a/tools/bpf/bpftool/xlated_dumper.c b/tools/bpf/bpftool/xlated_dumper.c
index 5e7cb8b36fef2..5579173a61e3b 100644
--- a/tools/bpf/bpftool/xlated_dumper.c
+++ b/tools/bpf/bpftool/xlated_dumper.c
@@ -107,14 +107,7 @@ print_insn_for_graph(void *private_data, const char *fmt, ...)
 
 	p = buf;
 	while (*p != '\0') {
-		if (*p == '\n') {
-			memmove(p + 3, p, strlen(buf) + 1 - (p - buf));
-			/* Align each instruction dump row left. */
-			*p++ = '\\';
-			*p++ = 'l';
-			/* Output multiline concatenation. */
-			*p++ = '\\';
-		} else if (*p == '<' || *p == '>' || *p == '|' || *p == '&') {
+		if (*p == '<' || *p == '>' || *p == '|' || *p == '&') {
 			memmove(p + 1, p, strlen(buf) + 1 - (p - buf));
 			/* Escape special character. */
 			*p++ = '\\';
@@ -129,16 +122,10 @@ print_insn_for_graph(void *private_data, const char *fmt, ...)
 static void __printf(2, 3)
 print_insn_json(void *private_data, const char *fmt, ...)
 {
-	unsigned int l = strlen(fmt);
-	char chomped_fmt[l];
 	va_list args;
 
 	va_start(args, fmt);
-	if (l > 0) {
-		strncpy(chomped_fmt, fmt, l - 1);
-		chomped_fmt[l - 1] = '\0';
-	}
-	jsonw_vprintf_enquote(json_wtr, chomped_fmt, args);
+	jsonw_vprintf_enquote(json_wtr, fmt, args);
 	va_end(args);
 }
 
@@ -351,6 +338,7 @@ void dump_xlated_plain(struct dump_data *dd, void *buf, unsigned int len,
 
 		printf("%4u: ", i);
 		print_bpf_insn(&cbs, insn + i, true);
+		printf("\n");
 
 		if (opcodes) {
 			printf("       ");
@@ -417,6 +405,7 @@ void dump_xlated_for_graph(struct dump_data *dd, void *buf_start, void *buf_end,
 
 		printf("%u: ", insn_off);
 		print_bpf_insn(&cbs, cur, true);
+		printf("\\l\\\n");
 
 		if (opcodes) {
 			printf("\\ \\ \\ \\ ");
diff --git a/tools/testing/selftests/bpf/disasm_helpers.c b/tools/testing/selftests/bpf/disasm_helpers.c
index f529f1c8c171d..30221352568d7 100644
--- a/tools/testing/selftests/bpf/disasm_helpers.c
+++ b/tools/testing/selftests/bpf/disasm_helpers.c
@@ -55,10 +55,9 @@ struct bpf_insn *disasm_insn(struct bpf_insn *insn, char *buf, size_t buf_sz)
 	 * for each instruction (FF stands for instruction `code` byte).
 	 * Remove the prefix inplace, and also simplify call instructions.
 	 * E.g.: "(85) call foo#10" -> "call foo".
-	 * Also remove newline in the end (the 'max(strlen(buf) - 1, 0)' thing).
 	 */
 	pfx_end = buf + 5;
-	sfx_start = buf + max((int)strlen(buf) - 1, 0);
+	sfx_start = buf + (int)strlen(buf);
 	if (strncmp(pfx_end, "call ", 5) == 0 && (tmp = strrchr(buf, '#')))
 		sfx_start = tmp;
 	len = sfx_start - pfx_end;
diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index b79bafca68f7a..0baa74618fa03 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -126,6 +126,7 @@
 #include "verifier_jit_inline.skel.h"
 #include "irq.skel.h"
 #include "verifier_ctx_ptr_param.skel.h"
+#include "verifier_zext.skel.h"
 
 #define MAX_ENTRIES 11
 
@@ -281,6 +282,7 @@ void test_irq(void)			      { RUN(irq); }
 void test_verifier_mtu(void)		      { RUN(verifier_mtu); }
 void test_verifier_jit_inline(void)               { RUN(verifier_jit_inline); }
 void test_verifier_ctx_ptr_param(void)       { RUN(verifier_ctx_ptr_param); }
+void test_verifier_zext(void)                 { RUN_TESTS(verifier_zext); }
 
 static int init_test_val_map(struct bpf_object *obj, char *map_name)
 {
diff --git a/tools/testing/selftests/bpf/progs/verifier_zext.c b/tools/testing/selftests/bpf/progs/verifier_zext.c
new file mode 100644
index 0000000000000..8f2362da91d69
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_zext.c
@@ -0,0 +1,392 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include "../../../include/linux/filter.h"
+#include <bpf_arena_common.h>
+#include <bpf/bpf_core_read.h>
+#include "bpf_misc.h"
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARENA);
+	__uint(map_flags, BPF_F_MMAPABLE | BPF_F_NO_USER_CONV);
+	__uint(max_entries, 1);
+} arena SEC(".maps");
+
+extern long bpf_kfunc_call_test4(signed char a, short b, int c, long d) __ksym;
+
+/* to retain debug info for BTF generation */
+void __kfunc_btf_root(void)
+{
+	bpf_kfunc_call_test4(0, 0, 0, 0);
+	bpf_arena_alloc_pages(0, 0, 0, 0, 0);
+	bpf_rdonly_cast(0, 0);
+}
+
+SEC("socket")
+__flag(BPF_F_TEST_STATE_FREQ)
+__flag(BPF_F_TEST_RND_HI32)
+__success __retval(0)
+__naked void zext_lost_across_checkpoint(void)
+{
+	asm volatile ("									\
+	call %[bpf_ktime_get_ns];							\
+	r8 = r0;									\
+	r6 = 0xdeadbeefcafebabe ll;	/* inject some value for r6's upper half */	\
+	if r8 != 0 goto 1f;		/* fall-through cached first, branch pruned */	\
+	r6 = 32;			/* full 64-bit def */				\
+	goto 2f;									\
+1:	w6 = 32;			/* 32-bit def, zext mark lost */		\
+2:	r0 = r6;			/* buggy verifier believed upper 32 bits are 0 */ \
+					/* and thus did not zero extended w6 = 32. */	\
+	r0 >>= 32;									\
+	exit;										\
+"	:
+	: __imm(bpf_ktime_get_ns)
+	: __clobber_all);
+}
+
+/* 32-bit ALU result read as 64-bit -> zext */
+SEC("socket")
+__success __log_level(2)
+__msg("w1 = w0{{ +}}; zext")
+__naked void zext_alu32_hi_used(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	w1 = w0;					\
+	r0 = r1;					\
+	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+/* 32-bit ALU result read only as 32-bit -> no zext */
+SEC("socket")
+__success __log_level(2)
+__not_msg("; zext")
+__naked void no_zext_alu32_hi_unused(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	w1 = w0;		/* MOV */		\
+	w2 = w1;					\
+	w2 += w1;		/* ALU32, BPF_X */	\
+	w2 += 1;		/* ALU32, BPF_K */	\
+	w2 = w2;		/* keep w2 alive for previous instruction */ \
+	r0 = 0;						\
+	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+/* 64-bit definition is never zero extended */
+SEC("socket")
+__success __log_level(2)
+__not_msg("r1 = r0{{.*}}; zext")
+__naked void no_zext_mov64(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	r1 = r0;					\
+	r0 = r1;					\
+	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+/* Narrow load result read as 64-bit -> zext */
+SEC("socket")
+__success __log_level(2)
+__msg("r1 = *(u32 *)(r10 -8){{ +}}; zext")
+__naked void zext_narrow_load_hi_used(void)
+{
+	asm volatile ("					\
+	r0 = 0;						\
+	*(u64 *)(r10 - 8) = r0;				\
+	r1 = *(u32 *)(r10 - 8);				\
+	r0 = r1;					\
+	exit;						\
+"	::: __clobber_all);
+}
+
+/* 32-bit atomic fetch result read as 64-bit -> zext */
+SEC("socket")
+__success __log_level(2)
+__msg("r1 = atomic_fetch_add((u32 *)(r10 -8), r1){{ +}}; zext")
+__naked void zext_atomic_fetch32_hi_used(void)
+{
+	asm volatile ("					\
+	r1 = 0;						\
+	*(u64 *)(r10 - 8) = r1;				\
+	w1 = 1;						\
+	.8byte %[fetch_add32];				\
+	r0 = r1;					\
+	exit;						\
+"	:
+	: __imm_insn(fetch_add32,
+		     BPF_ATOMIC_OP(BPF_W, BPF_ADD | BPF_FETCH, BPF_REG_10, BPF_REG_1, -8))
+	: __clobber_all);
+}
+
+/* 32-bit atomic cmpxchg result (r0) read as 64-bit -> zext */
+SEC("socket")
+__success __log_level(2)
+__msg("r0 = atomic_cmpxchg((u32 *)(r10 -8), r0, r1){{ +}}; zext")
+__naked void zext_cmpxchg32_hi_used(void)
+{
+	asm volatile ("					\
+	r1 = 0;						\
+	*(u64 *)(r10 - 8) = r1;				\
+	w0 = 0;						\
+	w1 = 1;						\
+	.8byte %[cmpxchg32];				\
+	r2 = r0;					\
+	r0 = r2;					\
+	exit;						\
+"	:
+	: __imm_insn(cmpxchg32,
+		     BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, BPF_REG_10, BPF_REG_1, -8))
+	: __clobber_all);
+}
+
+/* 32-bit def before a branch, upper half used on one branch -> zext */
+SEC("socket")
+__success __log_level(2)
+__msg("w6 = 32{{ +}}; zext")
+__naked void zext_cfg_hi_used_one_branch(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	w6 = 32;					\
+	if r0 == 0 goto 1f;				\
+	r0 = r6;					\
+	exit;						\
+1:	r0 = 0;						\
+	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+/* r1's upper half is dead, so 'w1 = 1' must NOT be marked for zero extension. */
+SEC("socket")
+__success __log_level(2)
+__not_msg("w1 = 1{{.*}}; zext")
+__naked void no_zext_other_reg_hi_used(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	r6 = r0;					\
+	r6 <<= 32;					\
+	w1 = 1;						\
+	r0 = r6;					\
+	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+/* LD_ABS defines r0; when r0 is read as 64-bit it must be zero extended */
+SEC("socket")
+__success __log_level(2)
+__msg("r0 = *(u8 *)skb[0]{{.*}}; zext")
+__naked void zext_ld_abs_hi_used(void)
+{
+	asm volatile ("					\
+	r6 = r1;					\
+	r0 = *(u8 *)skb[0];				\
+	r7 = r0;					\
+	r0 = r7;					\
+	exit;						\
+"	::: __clobber_all);
+}
+
+/* Helper parameters are read as 64-bit (call_use_mask() fallback) */
+SEC("socket")
+__success __log_level(2)
+__msg("w2 = 1{{ +}}; zext")
+__naked void helper_param_read_as_64bit(void)
+{
+	asm volatile ("					\
+	r1 = r10;					\
+	r1 += -8;					\
+	w2 = 1;						\
+	call %[bpf_trace_printk];			\
+	r0 = 0;						\
+	exit;						\
+"	:
+	: __imm(bpf_trace_printk)
+	: __clobber_all);
+}
+
+static __used __naked int subprog_reads_arg_as_64bit(void)
+{
+	asm volatile ("					\
+	r0 = r1;					\
+	exit;						\
+"	::: __clobber_all);
+}
+
+/* subprogram parameters are conservatively read as 64-bit */
+SEC("socket")
+__success __log_level(2)
+__msg("w1 = w0{{ +}}; zext")
+__naked void subprog_param_read_as_64bit(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	w1 = w0;					\
+	call subprog_reads_arg_as_64bit;		\
+	r0 = 0;						\
+	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+/* kfunc parameters are zero extended */
+SEC("tc")
+__success __log_level(2)
+__msg("w1 = 1{{ +}}; zext")
+__msg("w2 = 1{{ +}}; zext")
+__msg("w3 = 1{{ +}}; zext")
+__msg("w4 = 1{{ +}}; zext")
+__naked void kfunc_param_read_per_btf(void)
+{
+	asm volatile ("					\
+	w1 = 1;						\
+	w2 = 1;						\
+	w3 = 1;						\
+	w4 = 1;						\
+	call bpf_kfunc_call_test4;			\
+	r0 = 0;						\
+	exit;						\
+"	::: __clobber_all);
+}
+
+SEC("socket")
+__success __log_level(2)
+__not_msg("; zext")
+__naked void alu32_and_32bit_conditional(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	w1 = w0;					\
+	if w1 > 42 goto 1f;		/* BPF_K */	\
+	w2 = 28;					\
+	if w2 > w1 goto 1f;		/* BPF_X */	\
+	r0 = 0;						\
+1:	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+SEC("socket")
+__success __log_level(2)
+__msg("w1 = w0{{ +}}; zext")
+__naked void alu32_and_64bit_conditional(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	w1 = w0;					\
+	if r1 > 42 goto 1f;		/* BPF_K */	\
+	r2 = 28;					\
+	if r2 > r1 goto 1f;		/* BPF_X */	\
+	r0 = 0;						\
+1:	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+SEC("socket")
+__success __log_level(2)
+__not_msg("; zext")
+__naked void alu64_and_conditionals(void)
+{
+	asm volatile ("					\
+	call %[bpf_get_prandom_u32];			\
+	r1 = r0;					\
+	if w1 > 42 goto 1f;		/* BPF_K */	\
+	if r1 > 42 goto 1f;		/* BPF_K */	\
+	r2 = 28;					\
+	if w2 > w1 goto 1f;		/* BPF_X */	\
+	if r2 > r1 goto 1f;		/* BPF_X */	\
+	r0 = 0;						\
+1:	exit;						\
+"	:
+	: __imm(bpf_get_prandom_u32)
+	: __clobber_all);
+}
+
+#ifdef __BPF_FEATURE_ADDR_SPACE_CAST
+
+SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
+__arch_s390x
+__xlated("7: w1 = w0")
+__xlated("8: w1 = w1")
+__xlated("9: w1 += 8")
+__xlated("10: w1 = w1")
+__xlated("11: w2 = w1")
+__xlated("12: w2 = w2")
+__xlated("13: *(u64 *)(r1 +0) = r2")
+__naked void arena_ptr(void)
+{
+	asm volatile ("					\
+	r1 = %[arena] ll;				\
+	r2 = 0;						\
+	r3 = 1;						\
+	r4 = 0;						\
+	r5 = 0;						\
+	call %[bpf_arena_alloc_pages];			\
+	r1 = addr_space_cast(r0, 0, 1);		/* needs zext */ \
+	r1 += 8;				/* needs zext */ \
+	r2 = addr_space_cast(r1, 1, 0);		/* needs zext because of BPF_F_NO_USER_CONV */ \
+	*(u64 *)(r1 +0) = r2;				\
+	r0 = 0;						\
+	exit;						\
+"	:
+	: __imm(bpf_arena_alloc_pages),
+	  __imm_addr(arena)
+	: __clobber_all);
+}
+
+#endif
+
+/* Check if probe mem loads keep their zero extension. */
+SEC("socket")
+__success __log_level(2)
+__arch_s390x
+__xlated("3: r1 = *(u64 *)(r0 +0)")
+__xlated("4: r2 = *(u32 *)(r0 +0)")
+__xlated("5: w2 = w2")
+__xlated("6: r3 = *(u16 *)(r0 +0)")
+__xlated("7: w3 = w3")
+__xlated("8: r4 = *(u8 *)(r0 +0)")
+__xlated("9: w4 = w4")
+__naked void probe_mem(void)
+{
+	asm volatile ("					\
+	r1 = 0;						\
+	r2 = 0;						\
+	call %[bpf_rdonly_cast];			\
+	r1 = *(u64 *)(r0 + 0);	/* BPF_PROBE_MEM */	\
+	r2 = *(u32 *)(r0 + 0);	/* BPF_PROBE_MEM */	\
+	r3 = *(u16 *)(r0 + 0);	/* BPF_PROBE_MEM */	\
+	r4 = *(u8 *)(r0 + 0);	/* BPF_PROBE_MEM */	\
+	r0 = r1;		/* make the registers used */ \
+	r0 += r2;					\
+	r0 += r3;					\
+	r0 += r4;					\
+1:	exit;						\
+"	:
+	: __imm(bpf_rdonly_cast)
+	: __clobber_all);
+}
+
+char _license[] SEC("license") = "GPL";

21/3 2026/08/07 21:37 llm kmsan-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 26675
							output: 201
							thoughts: 1492
						

Thoughts:
**KMSAN Fuzzing Session Assessment: BPF Verifier Patch Series**

Okay, so I've got this patch series touching the BPF verifier, specifically messing with how it tracks 32-bit zero-extension and register liveness. My initial thought is, do we need to throw a KMSAN session at this? Let's break it down.

The patch aims to streamline the BPF verifier's zero-extension tracking. It's gotten rid of `subreg_def` within `bpf_reg_state`, opting to compute zero-extension needs using a liveness analysis (`bpf_compute_live_registers`) instead. They've introduced `bpf_insn_def32` and tweaked `compute_insn_live_regs` to differentiate between 32-bit and 64-bit register usage. It also looks like some `bpf_is_reg64` related logic has been touched.

First, I need to pinpoint the risk. The verifier’s now tracking zero-extension differently. The danger, in my mind, is if this new logic *fails* to insert a zero-extension instruction. If that happens, then we could end up with the upper 32 bits of a register being garbage, or in some cases uninitialized memory that was not zeroed out. However, if this happens, this garbage is from registers, not directly from an uninitialized kernel memory allocation that KMSAN is designed to detect.

Now, KMSAN focuses on *uninitialized memory reads* from kernel allocations (stack, heap, etc.). The verifier prevents these loads in the first place, or in the case of JIT, it ensures the registers are loaded correctly. A missing zero-extension means a register contains old data, but not "uninitialized" in the KMSAN sense unless the register itself was loaded from an uninitialized kernel memory region, which the verifier prevents.

Then I realized that KMSAN only instruments the BPF interpreter, not the JITed BPF instructions. Verifier logic can introduce bugs that the interpreter handles, but not the JITed code.

The real risk seems to be if the *verifier itself* has a bug and allows out-of-bounds reads or writes, right? That's what KASAN is there for. So, is there any new uninitialized memory in the *verifier code itself*?

I checked the code (`kernel/bpf/verifier.c`, `kernel/bpf/liveness.c`, `kernel/bpf/fixups.c`). The `insn_live_regs` struct changed from `u16` to `u32` to differentiate between register halves. Thankfully, given `MAX_BPF_REG` is 11, the `u32` is appropriate to accommodate `BIT(n)` (lower 32-bit) and `BIT(n + 16)` (upper 32-bit). We're good. Plus, the removal of `subreg_def` from `struct bpf_reg_state` should actually *reduce* the struct's size, not introduce any issues. All other new allocations/initializations seem safe.

Looking at the rules again, we need KMSAN if the patch: 1) introduces or modifies kernel structures sent to userspace; 2) depends on potentially uninitialized variables or struct fields; 3) involves allocation or initialization of complex data structures with uninitialized fields; or 4) affects bounds checks in a way that allows access to uninitialized bytes.

This patch touches internal verifier logic. It's not exposing new structures to userspace, and no new data is copied to userspace. There are no changes to initialization of complex data structures and no potentially uninitialized fields that I can see. The struct changes look safe. The changes to `bpf_compute_live_registers` initialize the `new_out` and `new_in` variables to 0.

So, *I'm* confident that this doesn't warrant a KMSAN session. The risks are verifier logic errors that KASAN should handle.

Let me double check one more thing... checking if any uninitialized memory in `insn_aux_data`. Nope, it's allocated elsewhere and its fields are populated. The modification `insn_aux[i].zext_dst = def32 >= 0 && (mask_hi(out) & BIT(def32));` is simply setting a boolean. Nothing there.

**Final assessment:** This patch series modifies BPF verifier's internal logic for zero-extension tracking, which does not introduce new kernel structures copied to user space, allocate or initialize complex data structures with uninitialized fields. There is no risk of uninitialized memory being leaked. The verifier changes


22/3 2026/08/07 21:38 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch modifies the BPF verifier's internal logic for tracking 32-bit subregister zero-extension and register liveness. It refactors how zero-extension requirements are computed by replacing the `subreg_def` field in `struct bpf_reg_state` with a liveness-based approach using `struct insn_live_regs`. These changes are purely algorithmic and do not introduce new kernel structures copied to user space, nor do they add complex data structures with potentially uninitialized fields. Any bugs introduced by these changes would manifest as verifier logic errors, potentially leading to out-of-bounds accesses or memory corruption in BPF programs, which are effectively caught by KASAN. There is no risk of uninitialized kernel memory being leaked or used in control flow, so a dedicated KMSAN fuzzing session is not required.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch modifies the BPF verifier's internal logic for tracking 32-bit subregister zero-extension and register liveness. It refactors how zero-extension requirements are computed by replacing the `subreg_def` field in `struct bpf_reg_state` with a liveness-based approach using `struct insn_live_regs`. These changes are purely algorithmic and do not introduce new kernel structures copied to user space, nor do they add complex data structures with potentially uninitialized fields. Any bugs introduced by these changes would manifest as verifier logic errors, potentially leading to out-of-bounds accesses or memory corruption in BPF programs, which are effectively caught by KASAN. There is no risk of uninitialized kernel memory being leaked or used in control flow, so a dedicated KMSAN fuzzing session is not required.",
  "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)