| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/07/08 20:14 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": [
"BPF_JIT_KASAN"
],
"FocusSymbols": [
"bpf_int_jit_compile",
"bpf_do_misc_fixups",
"check_mem_access"
],
"Reasoning": "The patch adds KASAN support to the BPF JIT compiler on x86_64, which is a functional change to core kernel logic.",
"WorthFuzzing": true
} |
| 1/1 | 2026/07/08 20:14 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 1d6ddf9706a869bc6d255e1c7d0ae6fbadb799b8\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Wed Jul 8 20:14:15 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/arch/x86/Kconfig b/arch/x86/Kconfig\nindex bdad90f210e4bf..705a9ab85f3a95 100644\n--- a/arch/x86/Kconfig\n+++ b/arch/x86/Kconfig\n@@ -233,6 +233,7 @@ config X86\n \tselect HAVE_SAMPLE_FTRACE_DIRECT\tif X86_64\n \tselect HAVE_SAMPLE_FTRACE_DIRECT_MULTI\tif X86_64\n \tselect HAVE_EBPF_JIT\n+\tselect HAVE_EBPF_JIT_KASAN\t\tif X86_64\n \tselect HAVE_EFFICIENT_UNALIGNED_ACCESS\n \tselect HAVE_EISA\t\t\tif X86_32\n \tselect HAVE_EXIT_THREAD\ndiff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c\nindex de7515ea1beae8..65a00e84799cd2 100644\n--- a/arch/x86/net/bpf_jit_comp.c\n+++ b/arch/x86/net/bpf_jit_comp.c\n@@ -21,6 +21,17 @@\n #include \u003casm/unwind.h\u003e\n #include \u003casm/cfi.h\u003e\n \n+#if IS_ENABLED(CONFIG_BPF_JIT_KASAN)\n+void __asan_load1(void *p);\n+void __asan_store1(void *p);\n+void __asan_load2(void *p);\n+void __asan_store2(void *p);\n+void __asan_load4(void *p);\n+void __asan_store4(void *p);\n+void __asan_load8(void *p);\n+void __asan_store8(void *p);\n+#endif\n+\n static bool all_callee_regs_used[4] = {true, true, true, true};\n \n static u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len)\n@@ -1110,6 +1121,90 @@ static void maybe_emit_1mod(u8 **pprog, u32 reg, bool is64)\n \t*pprog = prog;\n }\n \n+static int emit_kasan_check(struct bpf_verifier_env *env, u8 **pprog,\n+\t\t\t u32 addr_reg, struct bpf_insn *insn, u8 *ip,\n+\t\t\t bool is_write, bool accesses_stack_only)\n+{\n+#ifdef CONFIG_BPF_JIT_KASAN\n+\tu32 bpf_size = BPF_SIZE(insn-\u003ecode);\n+\ts32 off = insn-\u003eoff;\n+\tu8 *prog = *pprog;\n+\tvoid *kasan_func;\n+\n+\tif (!env)\n+\t\treturn 0;\n+\n+\tif (accesses_stack_only)\n+\t\treturn 0;\n+\n+\t/* Derive KASAN check function from access type and size */\n+\tswitch (bpf_size) {\n+\tcase BPF_B:\n+\t\tkasan_func = is_write ? __asan_store1 : __asan_load1;\n+\t\tbreak;\n+\tcase BPF_H:\n+\t\tkasan_func = is_write ? __asan_store2 : __asan_load2;\n+\t\tbreak;\n+\tcase BPF_W:\n+\t\tkasan_func = is_write ? __asan_store4 : __asan_load4;\n+\t\tbreak;\n+\tcase BPF_DW:\n+\t\tkasan_func = is_write ? __asan_store8 : __asan_load8;\n+\t\tbreak;\n+\tdefault:\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* Save rax */\n+\tEMIT1(0x50);\n+\t/* Save rcx */\n+\tEMIT1(0x51);\n+\t/* Save rdx */\n+\tEMIT1(0x52);\n+\t/* Save rsi */\n+\tEMIT1(0x56);\n+\t/* Save rdi */\n+\tEMIT1(0x57);\n+\t/* Save r8 */\n+\tEMIT2(0x41, 0x50);\n+\t/* Save r9 */\n+\tEMIT2(0x41, 0x51);\n+\n+\t/* mov rdi, addr_reg */\n+\tEMIT_mov(BPF_REG_1, addr_reg);\n+\n+\t/* add rdi, off (if offset is non-zero) */\n+\tif (off) {\n+\t\tif (is_imm8(off)) {\n+\t\t\t/* add rdi, imm8 */\n+\t\t\tEMIT4(0x48, 0x83, 0xC7, (u8)off);\n+\t\t} else {\n+\t\t\t/* add rdi, imm32 */\n+\t\t\tEMIT3_off32(0x48, 0x81, 0xC7, off);\n+\t\t}\n+\t}\n+\n+\t/* Adjust ip to account for the instrumentation generated so far */\n+\tip += (prog - *pprog);\n+\t/* We emit a call, so update call depth counting */\n+\tip += x86_call_depth_emit_accounting(\u0026prog, kasan_func, ip);\n+\t/* call kasan_func */\n+\tif (emit_call(\u0026prog, kasan_func, ip))\n+\t\treturn -ERANGE;\n+\n+\tEMIT2(0x41, 0x59);\n+\tEMIT2(0x41, 0x58);\n+\tEMIT1(0x5F);\n+\tEMIT1(0x5E);\n+\tEMIT1(0x5A);\n+\tEMIT1(0x59);\n+\tEMIT1(0x58);\n+\n+\t*pprog = prog;\n+#endif /* CONFIG_BPF_JIT_KASAN */\n+\treturn 0;\n+}\n+\n /* LDX: dst_reg = *(u8*)(src_reg + off) */\n static void emit_ldx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)\n {\n@@ -1315,6 +1410,64 @@ static void emit_st_index(u8 **pprog, u32 size, u32 dst_reg, u32 index_reg, int\n \t*pprog = prog;\n }\n \n+static void emit_st(u8 **pprog, struct bpf_insn *insn, int dst_reg,\n+\t\t s32 outgoing_arg_base, u16 outgoing_rsp)\n+{\n+\ts32 imm32 = insn-\u003eimm;\n+\tu8 *prog = *pprog;\n+\ts32 insn_off;\n+\n+\tswitch (BPF_SIZE(insn-\u003ecode)) {\n+\tcase BPF_B:\n+\t\tif (is_ereg(dst_reg))\n+\t\t\tEMIT2(0x41, 0xC6);\n+\t\telse\n+\t\t\tEMIT1(0xC6);\n+\t\tbreak;\n+\tcase BPF_H:\n+\t\tif (is_ereg(dst_reg))\n+\t\t\tEMIT3(0x66, 0x41, 0xC7);\n+\t\telse\n+\t\t\tEMIT2(0x66, 0xC7);\n+\t\tbreak;\n+\tcase BPF_W:\n+\t\tif (is_ereg(dst_reg))\n+\t\t\tEMIT2(0x41, 0xC7);\n+\t\telse\n+\t\t\tEMIT1(0xC7);\n+\t\tbreak;\n+\tcase BPF_DW:\n+\t\tif (dst_reg == BPF_REG_PARAMS \u0026\u0026 insn-\u003eoff == -8) {\n+\t\t\t/* Arg 6: store immediate in r9 register */\n+\t\t\temit_mov_imm64(\u0026prog, X86_REG_R9, imm32 \u003e\u003e 31,\n+\t\t\t\t (u32)imm32);\n+\t\t\t*pprog = prog;\n+\t\t\treturn;\n+\t\t}\n+\t\tEMIT2(add_1mod(0x48, dst_reg), 0xC7);\n+\t\tbreak;\n+\t}\n+\n+\tinsn_off = insn-\u003eoff;\n+\tif (dst_reg == BPF_REG_PARAMS) {\n+\t\t/*\n+\t\t * Args 7+: reverse BPF negative offsets to\n+\t\t * x86 positive rsp offsets.\n+\t\t * BPF off=-16 → [rsp+0], off=-24 → [rsp+8], ...\n+\t\t */\n+\t\tinsn_off = outgoing_arg_base - outgoing_rsp -\n+\t\t\tinsn_off - 16;\n+\t\tdst_reg = BPF_REG_FP;\n+\t}\n+\tif (is_imm8(insn_off))\n+\t\tEMIT2(add_1reg(0x40, dst_reg), insn_off);\n+\telse\n+\t\tEMIT1_off32(add_1reg(0x80, dst_reg), insn_off);\n+\n+\tEMIT(imm32, bpf_size_to_x86_bytes(BPF_SIZE(insn-\u003ecode)));\n+\t*pprog = prog;\n+}\n+\n static void emit_st_r12(u8 **pprog, u32 size, u32 dst_reg, int off, int imm)\n {\n \temit_st_index(pprog, size, dst_reg, X86_REG_R12, off, imm);\n@@ -1423,17 +1576,31 @@ static int emit_atomic_rmw_index(u8 **pprog, u32 atomic_op, u32 size,\n \treturn 0;\n }\n \n-static int emit_atomic_ld_st(u8 **pprog, u32 atomic_op, u32 dst_reg,\n-\t\t\t u32 src_reg, s16 off, u8 bpf_size)\n+static int emit_atomic_ld_st(struct bpf_verifier_env *env, u8 **pprog,\n+\t\t\t struct bpf_insn *insn, u8 *ip, u32 dst_reg,\n+\t\t\t u32 src_reg, bool accesses_stack_only)\n {\n+\tu32 atomic_op = insn-\u003eimm;\n+\tint err;\n+\n \tswitch (atomic_op) {\n \tcase BPF_LOAD_ACQ:\n+\t\terr = emit_kasan_check(env, pprog, src_reg, insn, ip, false,\n+\t\t\t\t accesses_stack_only);\n+\t\tif (err)\n+\t\t\treturn err;\n \t\t/* dst_reg = smp_load_acquire(src_reg + off16) */\n-\t\temit_ldx(pprog, bpf_size, dst_reg, src_reg, off);\n+\t\temit_ldx(pprog, BPF_SIZE(insn-\u003ecode), dst_reg, src_reg,\n+\t\t\t insn-\u003eoff);\n \t\tbreak;\n \tcase BPF_STORE_REL:\n+\t\terr = emit_kasan_check(env, pprog, dst_reg, insn, ip, true,\n+\t\t\t\t accesses_stack_only);\n+\t\tif (err)\n+\t\t\treturn err;\n \t\t/* smp_store_release(dst_reg + off16, src_reg) */\n-\t\temit_stx(pprog, bpf_size, dst_reg, src_reg, off);\n+\t\temit_stx(pprog, BPF_SIZE(insn-\u003ecode), dst_reg, src_reg,\n+\t\t\t insn-\u003eoff);\n \t\tbreak;\n \tdefault:\n \t\tpr_err(\"bpf_jit: unknown atomic load/store opcode %02x\\n\",\n@@ -1811,10 +1978,12 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *\n \t\tconst s32 imm32 = insn-\u003eimm;\n \t\tu32 dst_reg = insn-\u003edst_reg;\n \t\tu32 src_reg = insn-\u003esrc_reg;\n+\t\tbool accesses_stack_only;\n \t\tu8 b2 = 0, b3 = 0;\n \t\tu8 *start_of_ldx;\n \t\ts64 jmp_offset;\n \t\ts32 insn_off;\n+\t\tint insn_idx;\n \t\tu8 jmp_cond;\n \t\tu8 *func;\n \t\tint nops;\n@@ -1831,6 +2000,10 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *\n \t\t\tEMIT_ENDBR();\n \n \t\tip = image + addrs[i - 1] + (prog - temp);\n+\t\tinsn_idx = i - 1 + bpf_prog-\u003eaux-\u003esubprog_start;\n+\t\taccesses_stack_only =\n+\t\t\tenv ? !env-\u003einsn_aux_data[insn_idx].non_stack_access :\n+\t\t\t false;\n \n \t\tswitch (insn-\u003ecode) {\n \t\t\t/* ALU */\n@@ -2207,49 +2380,17 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *\n \t\t\tEMIT_LFENCE();\n \t\t\tbreak;\n \n-\t\t\t/* ST: *(u8*)(dst_reg + off) = imm */\n \t\tcase BPF_ST | BPF_MEM | BPF_B:\n-\t\t\tif (is_ereg(dst_reg))\n-\t\t\t\tEMIT2(0x41, 0xC6);\n-\t\t\telse\n-\t\t\t\tEMIT1(0xC6);\n-\t\t\tgoto st;\n \t\tcase BPF_ST | BPF_MEM | BPF_H:\n-\t\t\tif (is_ereg(dst_reg))\n-\t\t\t\tEMIT3(0x66, 0x41, 0xC7);\n-\t\t\telse\n-\t\t\t\tEMIT2(0x66, 0xC7);\n-\t\t\tgoto st;\n \t\tcase BPF_ST | BPF_MEM | BPF_W:\n-\t\t\tif (is_ereg(dst_reg))\n-\t\t\t\tEMIT2(0x41, 0xC7);\n-\t\t\telse\n-\t\t\t\tEMIT1(0xC7);\n-\t\t\tgoto st;\n \t\tcase BPF_ST | BPF_MEM | BPF_DW:\n-\t\t\tif (dst_reg == BPF_REG_PARAMS \u0026\u0026 insn-\u003eoff == -8) {\n-\t\t\t\t/* Arg 6: store immediate in r9 register */\n-\t\t\t\temit_mov_imm64(\u0026prog, X86_REG_R9, imm32 \u003e\u003e 31, (u32)imm32);\n-\t\t\t\tbreak;\n-\t\t\t}\n-\t\t\tEMIT2(add_1mod(0x48, dst_reg), 0xC7);\n-\n-st:\t\t\tinsn_off = insn-\u003eoff;\n-\t\t\tif (dst_reg == BPF_REG_PARAMS) {\n-\t\t\t\t/*\n-\t\t\t\t * Args 7+: reverse BPF negative offsets to\n-\t\t\t\t * x86 positive rsp offsets.\n-\t\t\t\t * BPF off=-16 → [rsp+0], off=-24 → [rsp+8], ...\n-\t\t\t\t */\n-\t\t\t\tinsn_off = outgoing_arg_base - outgoing_rsp - insn_off - 16;\n-\t\t\t\tdst_reg = BPF_REG_FP;\n-\t\t\t}\n-\t\t\tif (is_imm8(insn_off))\n-\t\t\t\tEMIT2(add_1reg(0x40, dst_reg), insn_off);\n-\t\t\telse\n-\t\t\t\tEMIT1_off32(add_1reg(0x80, dst_reg), insn_off);\n+\t\t\terr = emit_kasan_check(env, \u0026prog, dst_reg, insn, ip,\n+\t\t\t\t\t true, accesses_stack_only);\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n \n-\t\t\tEMIT(imm32, bpf_size_to_x86_bytes(BPF_SIZE(insn-\u003ecode)));\n+\t\t\temit_st(\u0026prog, insn, dst_reg, outgoing_arg_base,\n+\t\t\t\toutgoing_rsp);\n \t\t\tbreak;\n \n \t\t\t/* STX: *(u8*)(dst_reg + off) = src_reg */\n@@ -2267,6 +2408,10 @@ st:\t\t\tinsn_off = insn-\u003eoff;\n \t\t\t\tinsn_off = outgoing_arg_base - outgoing_rsp - insn_off - 16;\n \t\t\t\tdst_reg = BPF_REG_FP;\n \t\t\t}\n+\t\t\terr = emit_kasan_check(env, \u0026prog, dst_reg, insn, ip,\n+\t\t\t\t\t true, accesses_stack_only);\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n \t\t\temit_stx(\u0026prog, BPF_SIZE(insn-\u003ecode), dst_reg, src_reg, insn_off);\n \t\t\tbreak;\n \n@@ -2428,6 +2573,12 @@ st:\t\t\tinsn_off = insn-\u003eoff;\n \t\t\t\t/* populate jmp_offset for JAE above to jump to start_of_ldx */\n \t\t\t\tstart_of_ldx = prog;\n \t\t\t\tend_of_jmp[-1] = start_of_ldx - end_of_jmp;\n+\t\t\t} else {\n+\t\t\t\terr = emit_kasan_check(env, \u0026prog, src_reg,\n+\t\t\t\t\t\t insn, ip, false,\n+\t\t\t\t\t\t accesses_stack_only);\n+\t\t\t\tif (err)\n+\t\t\t\t\treturn err;\n \t\t\t}\n \t\t\tif (BPF_MODE(insn-\u003ecode) == BPF_PROBE_MEMSX ||\n \t\t\t BPF_MODE(insn-\u003ecode) == BPF_MEMSX)\n@@ -2489,15 +2640,16 @@ st:\t\t\tinsn_off = insn-\u003eoff;\n \t\t\t}\n \t\t\tfallthrough;\n \t\tcase BPF_STX | BPF_ATOMIC | BPF_W:\n-\t\tcase BPF_STX | BPF_ATOMIC | BPF_DW:\n-\t\t\tif (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\t\tbool is64 = BPF_SIZE(insn-\u003ecode) == BPF_DW;\n-\t\t\t\tu32 real_src_reg = src_reg;\n-\t\t\t\tu32 real_dst_reg = dst_reg;\n-\t\t\t\tu8 *branch_target;\n-\n+\t\tcase BPF_STX | BPF_ATOMIC | BPF_DW: {\n+\t\t\tbool is64 = BPF_SIZE(insn-\u003ecode) == BPF_DW;\n+\t\t\tu32 real_src_reg = src_reg;\n+\t\t\tu32 real_dst_reg = dst_reg;\n+\t\t\tu8 *branch_target;\n+\t\t\tbool is_atomic_fetch =\n+\t\t\t\t(insn-\u003eimm == (BPF_AND | BPF_FETCH) ||\n+\t\t\t\t insn-\u003eimm == (BPF_OR | BPF_FETCH) ||\n+\t\t\t\t insn-\u003eimm == (BPF_XOR | BPF_FETCH));\n+\t\t\tif (is_atomic_fetch) {\n \t\t\t\t/*\n \t\t\t\t * Can't be implemented with a single x86 insn.\n \t\t\t\t * Need to do a CMPXCHG loop.\n@@ -2510,7 +2662,17 @@ st:\t\t\tinsn_off = insn-\u003eoff;\n \t\t\t\tif (dst_reg == BPF_REG_0)\n \t\t\t\t\treal_dst_reg = BPF_REG_AX;\n \n+\t\t\t\tip += 3;\n+\t\t\t}\n+\t\t\tif (!bpf_atomic_is_load_store(insn)) {\n+\t\t\t\terr = emit_kasan_check(env, \u0026prog, real_dst_reg,\n+\t\t\t\t\t\t insn, ip, false,\n+\t\t\t\t\t\t accesses_stack_only);\n+\t\t\t\tif (err)\n+\t\t\t\t\treturn err;\n \t\t\t\tbranch_target = prog;\n+\t\t\t}\n+\t\t\tif (is_atomic_fetch) {\n \t\t\t\t/* Load old value */\n \t\t\t\temit_ldx(\u0026prog, BPF_SIZE(insn-\u003ecode),\n \t\t\t\t\t BPF_REG_0, real_dst_reg, insn-\u003eoff);\n@@ -2542,15 +2704,16 @@ st:\t\t\tinsn_off = insn-\u003eoff;\n \t\t\t}\n \n \t\t\tif (bpf_atomic_is_load_store(insn))\n-\t\t\t\terr = emit_atomic_ld_st(\u0026prog, insn-\u003eimm, dst_reg, src_reg,\n-\t\t\t\t\t\t\tinsn-\u003eoff, BPF_SIZE(insn-\u003ecode));\n+\t\t\t\terr = emit_atomic_ld_st(env, \u0026prog, insn, ip,\n+\t\t\t\t\t\t\tdst_reg, src_reg,\n+\t\t\t\t\t\t\taccesses_stack_only);\n \t\t\telse\n \t\t\t\terr = emit_atomic_rmw(\u0026prog, insn-\u003eimm, dst_reg, src_reg,\n \t\t\t\t\t\t insn-\u003eoff, BPF_SIZE(insn-\u003ecode));\n \t\t\tif (err)\n \t\t\t\treturn err;\n \t\t\tbreak;\n-\n+\t\t}\n \t\tcase BPF_STX | BPF_PROBE_ATOMIC | BPF_B:\n \t\tcase BPF_STX | BPF_PROBE_ATOMIC | BPF_H:\n \t\t\tif (!bpf_atomic_is_load_store(insn)) {\ndiff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h\nindex 76b8b7627a1085..868101ef5002ab 100644\n--- a/include/linux/bpf_verifier.h\n+++ b/include/linux/bpf_verifier.h\n@@ -723,6 +723,8 @@ struct bpf_insn_aux_data {\n \tu16 const_reg_map_mask;\n \tu16 const_reg_subprog_mask;\n \tu32 const_reg_vals[10];\n+\t/* instruction can access non-stack memory */\n+\tbool non_stack_access;\n };\n \n #define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */\ndiff --git a/include/linux/filter.h b/include/linux/filter.h\nindex 14acb2455746fc..1ebcd247ef4e3f 100644\n--- a/include/linux/filter.h\n+++ b/include/linux/filter.h\n@@ -1210,13 +1210,17 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,\n \n #ifdef CONFIG_BPF_SYSCALL\n struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,\n-\t\t\t\t const struct bpf_insn *patch, u32 len);\n+\t\t\t\t const struct bpf_insn *patch, u32 len,\n+\t\t\t\t s32 insn_off_in_patch);\n struct bpf_insn_aux_data *bpf_dup_insn_aux_data(struct bpf_verifier_env *env);\n void bpf_restore_insn_aux_data(struct bpf_verifier_env *env,\n \t\t\t struct bpf_insn_aux_data *orig_insn_aux);\n #else\n-static inline struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,\n-\t\t\t\t\t\t const struct bpf_insn *patch, u32 len)\n+static inline struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env,\n+\t\t\t\t\t\t u32 off,\n+\t\t\t\t\t\t const struct bpf_insn *patch,\n+\t\t\t\t\t\t u32 len,\n+\t\t\t\t\t\t s32 insn_off_in_patch)\n {\n \treturn ERR_PTR(-ENOTSUPP);\n }\ndiff --git a/kernel/bpf/Kconfig b/kernel/bpf/Kconfig\nindex eb3de35734f092..e1f3850d2f5a07 100644\n--- a/kernel/bpf/Kconfig\n+++ b/kernel/bpf/Kconfig\n@@ -17,6 +17,10 @@ config HAVE_CBPF_JIT\n config HAVE_EBPF_JIT\n \tbool\n \n+# KASAN support for JIT compiler\n+config HAVE_EBPF_JIT_KASAN\n+\tbool\n+\n # Used by archs to tell that they want the BPF JIT compiler enabled by\n # default for kernels that were compiled with BPF JIT support.\n config ARCH_WANT_DEFAULT_BPF_JIT\n@@ -101,4 +105,17 @@ config BPF_LSM\n \n \t If you are unsure how to answer this question, answer N.\n \n+config BPF_JIT_KASAN\n+\tbool\n+\tdepends on HAVE_EBPF_JIT_KASAN\n+\tdepends on KASAN_GENERIC\n+\tdepends on KASAN_VMALLOC\n+\tdepends on BPF_JIT\n+\tdefault y if KASAN\n+\thelp\n+\t Makes JIT compiler insert generic outline KASAN checks in BPF\n+\t programs when they are inserted in the kernel. This feature is\n+\t automatically enabled if the needed set of KASAN and BPF\n+\t configuration options is enabled.\n+\n endmenu # \"BPF subsystem\"\ndiff --git a/kernel/bpf/core.c b/kernel/bpf/core.c\nindex 6e19a030da6f13..1e18b999f1b565 100644\n--- a/kernel/bpf/core.c\n+++ b/kernel/bpf/core.c\n@@ -1598,7 +1598,7 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bp\n \t\t\tcontinue;\n \n \t\tif (env)\n-\t\t\ttmp = bpf_patch_insn_data(env, i, insn_buff, rewritten);\n+\t\t\ttmp = bpf_patch_insn_data(env, i, insn_buff, rewritten, rewritten - 1);\n \t\telse\n \t\t\ttmp = bpf_patch_insn_single(clone, i, insn_buff, rewritten);\n \ndiff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c\nindex 12a8a4eb757f18..feee1f33de9d55 100644\n--- a/kernel/bpf/fixups.c\n+++ b/kernel/bpf/fixups.c\n@@ -152,12 +152,25 @@ static int get_callee_stack_depth(struct bpf_verifier_env *env,\n }\n #endif\n \n+static bool is_mem_insn(struct bpf_insn *insn)\n+{\n+\tif (BPF_CLASS(insn-\u003ecode) != BPF_ST \u0026\u0026\n+\t BPF_CLASS(insn-\u003ecode) != BPF_STX \u0026\u0026\n+\t BPF_CLASS(insn-\u003ecode) != BPF_LDX)\n+\t\treturn false;\n+\n+\treturn (BPF_MODE(insn-\u003ecode) == BPF_MEM ||\n+\t\tBPF_MODE(insn-\u003ecode) == BPF_MEMSX ||\n+\t\tBPF_MODE(insn-\u003ecode) == BPF_ATOMIC);\n+}\n+\n /* single env-\u003eprog-\u003einsni[off] instruction was replaced with the range\n * insni[off, off + cnt). Adjust corresponding insn_aux_data by copying\n * [0, off) and [off, end) to new locations, so the patched range stays zero\n */\n static void adjust_insn_aux_data(struct bpf_verifier_env *env,\n-\t\t\t\t struct bpf_prog *new_prog, u32 off, u32 cnt)\n+\t\t\t\t struct bpf_prog *new_prog, u32 off, u32 cnt,\n+\t\t\t\t s32 insn_off_in_patch)\n {\n \tstruct bpf_insn_aux_data *data = env-\u003einsn_aux_data;\n \tstruct bpf_insn *insn = new_prog-\u003einsnsi;\n@@ -171,8 +184,14 @@ static void adjust_insn_aux_data(struct bpf_verifier_env *env,\n \t */\n \tdata[off].zext_dst = insn_has_def32(insn + off + cnt - 1);\n \n-\tif (cnt == 1)\n+\tif (cnt == 1) {\n+\t\t/* A non-memory accessing insn could have been replaced by a\n+\t\t * memory accessing insn, systematically mark it for non-stack\n+\t\t * access\n+\t\t */\n+\t\tdata[off].non_stack_access = is_mem_insn(insn);\n \t\treturn;\n+\t}\n \tprog_len = new_prog-\u003elen;\n \n \tmemmove(data + off + cnt - 1, data + off,\n@@ -182,7 +201,19 @@ static void adjust_insn_aux_data(struct bpf_verifier_env *env,\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\tif (i == off + insn_off_in_patch) {\n+\t\t\tdata[i].non_stack_access = data[off + cnt - 1].non_stack_access;\n+\t\t\tdata[off + cnt - 1].non_stack_access = false;\n+\t\t} else if (is_mem_insn(insn + i)) {\n+\t\t\tdata[i].non_stack_access = true;\n+\t\t}\n \t}\n+\t/*\n+\t * Last slot instruction could be a newly generated\n+\t * BPF_ST/BPF_LDX/BPF_STX\n+\t */\n+\tif (is_mem_insn(insn + off + cnt - 1) \u0026\u0026 insn_off_in_patch != cnt - 1)\n+\t\tdata[off + cnt - 1].non_stack_access = true;\n \n \t/*\n \t * The indirect_target flag of the original instruction was moved to the last of the\n@@ -245,7 +276,8 @@ static void adjust_poke_descs(struct bpf_prog *prog, u32 off, u32 len)\n }\n \n struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,\n-\t\t\t\t const struct bpf_insn *patch, u32 len)\n+\t\t\t\t const struct bpf_insn *patch, u32 len,\n+\t\t\t\t s32 insn_off_in_patch)\n {\n \tstruct bpf_prog *new_prog;\n \tstruct bpf_insn_aux_data *new_data = NULL;\n@@ -269,7 +301,7 @@ struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,\n \t\t\t\tenv-\u003einsn_aux_data[off].orig_idx);\n \t\treturn NULL;\n \t}\n-\tadjust_insn_aux_data(env, new_prog, off, len);\n+\tadjust_insn_aux_data(env, new_prog, off, len, insn_off_in_patch);\n \tadjust_subprog_starts(env, off, len);\n \tadjust_insn_arrays(env, off, len);\n \tadjust_poke_descs(new_prog, off, len);\n@@ -668,7 +700,7 @@ int bpf_opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,\n \t\tpatch = zext_patch;\n \t\tpatch_len = 2;\n apply_patch_buffer:\n-\t\tnew_prog = bpf_patch_insn_data(env, adj_idx, patch, patch_len);\n+\t\tnew_prog = bpf_patch_insn_data(env, adj_idx, patch, patch_len, 0);\n \t\tif (!new_prog)\n \t\t\treturn -ENOMEM;\n \t\tenv-\u003eprog = new_prog;\n@@ -713,7 +745,7 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[cnt++] = BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_1,\n \t\t\t\t\t\t -subprogs[0].stack_depth);\n \t\t\tinsn_buf[cnt++] = env-\u003eprog-\u003einsnsi[0];\n-\t\t\tnew_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt, 1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \t\t\tenv-\u003eprog = new_prog;\n@@ -736,7 +768,8 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)\n \t\t\tverifier_bug(env, \"prologue is too long\");\n \t\t\treturn -EFAULT;\n \t\t} else if (cnt) {\n-\t\t\tnew_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt,\n+\t\t\t\t\t\t cnt - 1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -759,6 +792,7 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)\n \n \tfor (i = 0; i \u003c insn_cnt; i++, insn++) {\n \t\tbpf_convert_ctx_access_t convert_ctx_access;\n+\t\ts32 insn_off_in_patch = -1;\n \t\tu8 mode;\n \n \t\tif (env-\u003einsn_aux_data[i + delta].nospec) {\n@@ -768,7 +802,8 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)\n \t\t\t*patch++ = BPF_ST_NOSPEC();\n \t\t\t*patch++ = *insn;\n \t\t\tcnt = patch - insn_buf;\n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, cnt - 1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -841,7 +876,8 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)\n \t\t\t*patch++ = *insn;\n \t\t\t*patch++ = BPF_ST_NOSPEC();\n \t\t\tcnt = patch - insn_buf;\n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, 0);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -856,16 +892,20 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)\n \t\t\tif (!ops-\u003econvert_ctx_access)\n \t\t\t\tcontinue;\n \t\t\tconvert_ctx_access = ops-\u003econvert_ctx_access;\n+\t\t\tinsn_off_in_patch = 0;\n \t\t\tbreak;\n \t\tcase PTR_TO_SOCKET:\n \t\tcase PTR_TO_SOCK_COMMON:\n \t\t\tconvert_ctx_access = bpf_sock_convert_ctx_access;\n+\t\t\tinsn_off_in_patch = 0;\n \t\t\tbreak;\n \t\tcase PTR_TO_TCP_SOCK:\n \t\t\tconvert_ctx_access = bpf_tcp_sock_convert_ctx_access;\n+\t\t\tinsn_off_in_patch = 0;\n \t\t\tbreak;\n \t\tcase PTR_TO_XDP_SOCK:\n \t\t\tconvert_ctx_access = bpf_xdp_sock_convert_ctx_access;\n+\t\t\tinsn_off_in_patch = 0;\n \t\t\tbreak;\n \t\tcase PTR_TO_BTF_ID:\n \t\tcase PTR_TO_BTF_ID | PTR_UNTRUSTED:\n@@ -971,7 +1011,8 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)\n \t\t\t\t\t\t size * 8, 0);\n \n patch_insn_buf:\n-\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt,\n+\t\t\t\t\t insn_off_in_patch);\n \t\tif (!new_prog)\n \t\t\treturn -ENOMEM;\n \n@@ -1464,7 +1505,7 @@ static int add_hidden_subprog(struct bpf_verifier_env *env, struct bpf_insn *pat\n \t * ones for the hidden subprog. Hence all of the adjustment operations\n \t * in bpf_patch_insn_data are no-ops.\n \t */\n-\tprog = bpf_patch_insn_data(env, env-\u003eprog-\u003elen - 1, patch, len);\n+\tprog = bpf_patch_insn_data(env, env-\u003eprog-\u003elen - 1, patch, len, -1);\n \tif (!prog)\n \t\treturn -ENOMEM;\n \tenv-\u003eprog = prog;\n@@ -1549,7 +1590,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \n \t\t\tcnt = patch - insn_buf;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, 0);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -1569,6 +1611,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tbool is_sdiv = isdiv \u0026\u0026 insn-\u003eoff == 1;\n \t\t\tbool is_smod = !isdiv \u0026\u0026 insn-\u003eoff == 1;\n \t\t\tstruct bpf_insn *patch = insn_buf;\n+\t\t\ts32 insn_off_in_patch;\n \n \t\t\tif (is_sdiv) {\n \t\t\t\t/* [R,W]x sdiv 0 -\u003e 0\n@@ -1595,6 +1638,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\t*patch++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);\n \t\t\t\t*patch++ = *insn;\n \t\t\t\tcnt = patch - insn_buf;\n+\t\t\t\tinsn_off_in_patch = cnt - 1;\n \t\t\t} else if (is_smod) {\n \t\t\t\t/* [R,W]x mod 0 -\u003e [R,W]x */\n \t\t\t\t/* [R,W]x mod -1 -\u003e 0 */\n@@ -1611,6 +1655,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\t*patch++ = BPF_MOV32_IMM(insn-\u003edst_reg, 0);\n \t\t\t\t*patch++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);\n \t\t\t\t*patch++ = *insn;\n+\t\t\t\tinsn_off_in_patch = patch - insn_buf - 1;\n \n \t\t\t\tif (!is64) {\n \t\t\t\t\t*patch++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);\n@@ -1626,12 +1671,14 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\t*patch++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);\n \t\t\t\t*patch++ = *insn;\n \t\t\t\tcnt = patch - insn_buf;\n+\t\t\t\tinsn_off_in_patch = cnt - 1;\n \t\t\t} else {\n \t\t\t\t/* [R,W]x mod 0 -\u003e [R,W]x */\n \t\t\t\t*patch++ = BPF_RAW_INSN((is64 ? BPF_JMP : BPF_JMP32) |\n \t\t\t\t\t\t\tBPF_JEQ | BPF_K, insn-\u003esrc_reg,\n \t\t\t\t\t\t\t0, 1 + (is64 ? 0 : 1), 0);\n \t\t\t\t*patch++ = *insn;\n+\t\t\t\tinsn_off_in_patch = patch - insn_buf - 1;\n \n \t\t\t\tif (!is64) {\n \t\t\t\t\t*patch++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);\n@@ -1640,7 +1687,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\tcnt = patch - insn_buf;\n \t\t\t}\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, insn_off_in_patch);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -1656,6 +1704,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t BPF_MODE(insn-\u003ecode) == BPF_PROBE_MEMSX)) {\n \t\t\tstruct bpf_insn *patch = insn_buf;\n \t\t\tu64 uaddress_limit = bpf_arch_uaddress_limit();\n+\t\t\ts32 insn_off_in_patch;\n \n \t\t\tif (!uaddress_limit)\n \t\t\t\tgoto next_insn;\n@@ -1666,11 +1715,13 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t*patch++ = BPF_ALU64_IMM(BPF_RSH, BPF_REG_AX, 32);\n \t\t\t*patch++ = BPF_JMP_IMM(BPF_JLE, BPF_REG_AX, uaddress_limit \u003e\u003e 32, 2);\n \t\t\t*patch++ = *insn;\n+\t\t\tinsn_off_in_patch = patch - insn_buf - 1;\n \t\t\t*patch++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);\n \t\t\t*patch++ = BPF_MOV64_IMM(insn-\u003edst_reg, 0);\n \n \t\t\tcnt = patch - insn_buf;\n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, insn_off_in_patch);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -1690,7 +1741,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\treturn -EFAULT;\n \t\t\t}\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, 0);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -1743,7 +1795,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\t*patch++ = BPF_ALU64_IMM(BPF_MUL, off_reg, -1);\n \t\t\tcnt = patch - insn_buf;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, -1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -1788,7 +1841,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[6] = BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_AX, stack_off_cnt);\n \t\t\tcnt = 7;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, 3);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -1809,7 +1863,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[3] = BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_AX, stack_off);\n \t\t\tcnt = 4;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, 2);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -1830,7 +1885,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tif (cnt == 0)\n \t\t\t\tgoto next_insn;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, cnt - 1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -1915,7 +1971,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\t\t\t\t\t map)-\u003eindex_mask);\n \t\t\tinsn_buf[2] = *insn;\n \t\t\tcnt = 3;\n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, cnt - 1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -1948,7 +2005,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[2] = *insn;\n \t\t\tcnt = 3;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, cnt - 1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -1967,7 +2025,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[1] = *insn;\n \t\t\tcnt = 2;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, cnt - 1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -2007,8 +2066,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\t\treturn -EFAULT;\n \t\t\t\t}\n \n-\t\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta,\n-\t\t\t\t\t\t\t insn_buf, cnt);\n+\t\t\t\tnew_prog = bpf_patch_insn_data(\n+\t\t\t\t\tenv, i + delta, insn_buf, cnt, -1);\n \t\t\t\tif (!new_prog)\n \t\t\t\t\treturn -ENOMEM;\n \n@@ -2091,7 +2150,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tcnt = 3;\n \n \t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n-\t\t\t\t\t\t cnt);\n+\t\t\t\t\t\t cnt, -1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -2119,7 +2178,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[0] = BPF_ALU32_REG(BPF_XOR, BPF_REG_0, BPF_REG_0);\n \t\t\tcnt = 1;\n #endif\n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, -1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -2137,7 +2197,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[2] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0);\n \t\t\tcnt = 3;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, -1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -2171,7 +2232,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[cnt++] = BPF_JMP_A(1);\n \t\t\tinsn_buf[cnt++] = BPF_MOV64_IMM(BPF_REG_0, -EINVAL);\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, -1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -2203,7 +2265,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\tcnt = 1;\n \t\t\t}\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, -1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -2229,7 +2292,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t\tcnt = 2;\n \t\t\t}\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, -1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -2245,7 +2309,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\t/* Load IP address from ctx - 16 */\n \t\t\tinsn_buf[0] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, -16);\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, 1);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t 1, -1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -2300,7 +2365,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[10] = BPF_MOV64_IMM(BPF_REG_0, -ENOENT);\n \t\t\tcnt = 11;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, -1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -2318,7 +2384,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t\tinsn_buf[1] = BPF_ATOMIC_OP(BPF_DW, BPF_XCHG, BPF_REG_1, BPF_REG_0, 0);\n \t\t\tcnt = 2;\n \n-\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\n+\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\n+\t\t\t\t\t\t cnt, -1);\n \t\t\tif (!new_prog)\n \t\t\t\treturn -ENOMEM;\n \n@@ -2386,7 +2453,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n \t\t/* Copy first actual insn to preserve it */\n \t\tinsn_buf[cnt++] = env-\u003eprog-\u003einsnsi[subprog_start];\n \n-\t\tnew_prog = bpf_patch_insn_data(env, subprog_start, insn_buf, cnt);\n+\t\tnew_prog = bpf_patch_insn_data(env, subprog_start, insn_buf, cnt, cnt - 1);\n \t\tif (!new_prog)\n \t\t\treturn -ENOMEM;\n \t\tenv-\u003eprog = prog = new_prog;\n@@ -2485,7 +2552,7 @@ static struct bpf_prog *inline_bpf_loop(struct bpf_verifier_env *env,\n \tinsn_buf[cnt++] = BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_10, r8_offset);\n \n \t*total_cnt = cnt;\n-\tnew_prog = bpf_patch_insn_data(env, position, insn_buf, cnt);\n+\tnew_prog = bpf_patch_insn_data(env, position, insn_buf, cnt, -1);\n \tif (!new_prog)\n \t\treturn new_prog;\n \ndiff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c\nindex 51f7965d42e324..1246ce8f5f0f46 100644\n--- a/kernel/bpf/verifier.c\n+++ b/kernel/bpf/verifier.c\n@@ -3144,6 +3144,11 @@ static void mark_indirect_target(struct bpf_verifier_env *env, int idx)\n \tenv-\u003einsn_aux_data[idx].indirect_target = true;\n }\n \n+static void mark_non_stack_access(struct bpf_verifier_env *env, int idx)\n+{\n+\tenv-\u003einsn_aux_data[idx].non_stack_access = true;\n+}\n+\n #define LR_FRAMENO_BITS\t4\n #define LR_SPI_BITS\t6\n #define LR_ENTRY_BITS\t(LR_SPI_BITS + LR_FRAMENO_BITS + 1)\n@@ -6333,6 +6338,10 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b\n \t\telse\n \t\t\tcoerce_reg_to_size_sx(\u0026regs[value_regno], size);\n \t}\n+\n+\tif (!err \u0026\u0026 reg-\u003etype != PTR_TO_STACK)\n+\t\tmark_non_stack_access(env, insn_idx);\n+\n \treturn err;\n }\n \ndiff --git a/tools/testing/selftests/bpf/prog_tests/bpf_insn_array.c b/tools/testing/selftests/bpf/prog_tests/bpf_insn_array.c\nindex 0222a9a5d07614..37c405fc2b0d23 100644\n--- a/tools/testing/selftests/bpf/prog_tests/bpf_insn_array.c\n+++ b/tools/testing/selftests/bpf/prog_tests/bpf_insn_array.c\n@@ -227,42 +227,6 @@ static void check_incorrect_index(void)\n \tcheck_mid_insn_index();\n }\n \n-static int set_bpf_jit_harden(char *level)\n-{\n-\tchar old_level;\n-\tint err = -1;\n-\tint fd = -1;\n-\n-\tfd = open(\"/proc/sys/net/core/bpf_jit_harden\", O_RDWR | O_NONBLOCK);\n-\tif (fd \u003c 0) {\n-\t\tASSERT_FAIL(\"open .../bpf_jit_harden returned %d (errno=%d)\", fd, errno);\n-\t\treturn -1;\n-\t}\n-\n-\terr = read(fd, \u0026old_level, 1);\n-\tif (err != 1) {\n-\t\tASSERT_FAIL(\"read from .../bpf_jit_harden returned %d (errno=%d)\", err, errno);\n-\t\terr = -1;\n-\t\tgoto end;\n-\t}\n-\n-\tlseek(fd, 0, SEEK_SET);\n-\n-\terr = write(fd, level, 1);\n-\tif (err != 1) {\n-\t\tASSERT_FAIL(\"write to .../bpf_jit_harden returned %d (errno=%d)\", err, errno);\n-\t\terr = -1;\n-\t\tgoto end;\n-\t}\n-\n-\terr = 0;\n-\t*level = old_level;\n-end:\n-\tif (fd \u003e= 0)\n-\t\tclose(fd);\n-\treturn err;\n-}\n-\n static void check_blindness(void)\n {\n \tstruct bpf_insn insns[] = {\n@@ -272,7 +236,7 @@ static void check_blindness(void)\n \t\tBPF_MOV64_IMM(BPF_REG_0, 1),\n \t\tBPF_EXIT_INSN(),\n \t};\n-\tint prog_fd = -1, map_fd;\n+\tint prog_fd = -1, map_fd, ret;\n \tstruct bpf_insn_array_value val = {};\n \tchar bpf_jit_harden = '@'; /* non-exizsting value */\n \tint i;\n@@ -291,7 +255,8 @@ static void check_blindness(void)\n \t\tgoto cleanup;\n \n \tbpf_jit_harden = '2';\n-\tif (set_bpf_jit_harden(\u0026bpf_jit_harden)) {\n+\tret = set_bpf_jit_harden(\u0026bpf_jit_harden);\n+\tif (!ASSERT_OK(ret, \"set bpf_jit_harden\")) {\n \t\tbpf_jit_harden = '@'; /* open, read or write failed =\u003e no write was done */\n \t\tgoto cleanup;\n \t}\ndiff --git a/tools/testing/selftests/bpf/prog_tests/kasan.c b/tools/testing/selftests/bpf/prog_tests/kasan.c\nnew file mode 100644\nindex 00000000000000..f006b92ee131ac\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/prog_tests/kasan.c\n@@ -0,0 +1,425 @@\n+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause\n+\n+/*\n+ * Tests validating that KASAN reports are properly instrumented and\n+ * generated on a wide variety of instructions. The running kernel needs\n+ * kasan_multi_shot to run multiple kasan-generating subtests at once\n+ */\n+#include \u003cbpf/bpf.h\u003e\n+#include \u003cerrno.h\u003e\n+#include \u003cfcntl.h\u003e\n+#include \u003clinux/if_ether.h\u003e\n+#include \u003cunistd.h\u003e\n+#include \u003ctest_progs.h\u003e\n+#include \u003cunpriv_helpers.h\u003e\n+#include \"kasan.skel.h\"\n+#include \"kasan_harden.skel.h\"\n+\n+#define SUBTEST_NAME_MAX_LEN\t128\n+#define PROG_NAME_MAX_LEN\t128\n+\n+#define MAX_LOG_SIZE\t\t(8 * 1024)\n+#define READ_CHUNK_SIZE\t\t256\n+\n+#define KASAN_PATTERN_SLAB_UAF \"BUG: KASAN: slab-use-after-free \" \\\n+\t\"in bpf_prog_%02x%02x%02x%02x%02x%02x%02x%02x_%s\"\n+#define KASAN_PATTERN_REPORT \"%s of size %d at addr\"\n+\n+static char klog_buffer[MAX_LOG_SIZE];\n+static char record[MAX_LOG_SIZE];\n+\n+struct test_spec {\n+\tchar *prog_type;\n+\tbool is_write;\n+\tbool only_32_or_64;\n+\tbool needs_load_acq_store_rel;\n+\tbool skip_multi_size_testing;\n+\tbool skip_on_stack_testing;\n+\tint run_size;\n+\tbool expect_no_report;\n+\tbool rnd_hi32;\n+};\n+\n+struct kasan_write_val {\n+\t__u8 data_1;\n+\t__u16 data_2;\n+\t__u32 data_4;\n+\t__u64 data_8;\n+};\n+\n+struct test_ctx {\n+\t__u8 prog_tag[BPF_TAG_SIZE];\n+\tstruct bpf_object *obj;\n+\tint *access_size;\n+\tbool skip_load_acq_store_rel;\n+\tstruct bpf_program *prog;\n+\tchar prog_name[SUBTEST_NAME_MAX_LEN];\n+\tint klog_fd;\n+};\n+\n+static int open_kernel_logs(void)\n+{\n+\tint fd;\n+\n+\tfd = open(\"/dev/kmsg\", O_RDONLY | O_NONBLOCK);\n+\n+\treturn fd;\n+}\n+\n+static void skip_kernel_logs(int fd)\n+{\n+\tlseek(fd, 0, SEEK_END);\n+}\n+\n+static int read_kernel_logs(int fd, char *buf, size_t max_len)\n+{\n+\tsize_t total = 0;\n+\tssize_t n;\n+\n+\tbuf[0] = '\\0';\n+\twhile (1) {\n+\t\tchar *msg, *eol;\n+\t\tsize_t len;\n+\n+\t\tn = read(fd, record, sizeof(record) - 1);\n+\t\tif (n \u003c 0) {\n+\t\t\tif (errno == EAGAIN)\n+\t\t\t\tbreak;\n+\t\t\treturn n;\n+\t\t}\n+\t\trecord[n] = '\\0';\n+\n+\t\t/*\n+\t\t * Each kmsg record starts with some metadata, separated\n+\t\t * from the actual content by a semi-colon\n+\t\t */\n+\t\tmsg = strchr(record, ';');\n+\t\tif (!msg)\n+\t\t\tcontinue;\n+\t\tmsg++;\n+\t\teol = strchr(msg, '\\n');\n+\t\tif (eol)\n+\t\t\t*eol = '\\0';\n+\n+\t\tlen = strlen(msg);\n+\t\tif (total + len + 2 \u003e max_len)\n+\t\t\tbreak;\n+\t\tmemcpy(buf + total, msg, len);\n+\t\ttotal += len;\n+\t\tbuf[total++] = '\\n';\n+\t\tbuf[total] = '\\0';\n+\t}\n+\n+\treturn total;\n+}\n+\n+static int check_kasan_report_in_kernel_logs(char *buf, struct test_ctx *ctx,\n+\t\t\t\t\t bool is_write, int size)\n+{\n+\tchar access_log[READ_CHUNK_SIZE];\n+\tchar *kasan_report_start;\n+\tint nsize;\n+\n+\tsnprintf(access_log, READ_CHUNK_SIZE, KASAN_PATTERN_SLAB_UAF,\n+\t\t ctx-\u003eprog_tag[0], ctx-\u003eprog_tag[1], ctx-\u003eprog_tag[2],\n+\t\t ctx-\u003eprog_tag[3], ctx-\u003eprog_tag[4], ctx-\u003eprog_tag[5],\n+\t\t ctx-\u003eprog_tag[6], ctx-\u003eprog_tag[7], ctx-\u003eprog_name);\n+\t/*\n+\t * Searched kasan report is valid if\n+\t * - it contains the expected kasan pattern\n+\t * - the description of the faulty access is found somewhere\n+\t * after the header (not necessarily on the very next line,\n+\t * because other kernel messages may interleave)\n+\t * - faulty access properties match the tested type and size\n+\t */\n+\tkasan_report_start = strstr(buf, access_log);\n+\n+\tif (!kasan_report_start)\n+\t\treturn 1;\n+\n+\tnsize = snprintf(access_log, READ_CHUNK_SIZE, KASAN_PATTERN_REPORT,\n+\t\t\t is_write ? \"Write\" : \"Read\", size);\n+\n+\tif (!strstr(kasan_report_start, access_log))\n+\t\treturn 1;\n+\n+\treturn 0;\n+}\n+\n+static void exec_subtest(struct test_ctx *ctx, struct test_spec *test,\n+\t\t\t int access_size, bool on_stack)\n+{\n+\tLIBBPF_OPTS(bpf_test_run_opts, topts);\n+\tstruct bpf_prog_info info;\n+\tuint8_t buf[ETH_HLEN] = {0};\n+\tint ret, prog_fd;\n+\t__u32 info_len;\n+\n+\tctx-\u003eprog = bpf_object__find_program_by_name(ctx-\u003eobj,\n+\t\t\t\t\t\t ctx-\u003eprog_name);\n+\tif (!ASSERT_OK_PTR(ctx-\u003eprog, \"find test prog\"))\n+\t\treturn;\n+\n+\tinfo_len = sizeof(info);\n+\tmemset(\u0026info, 0, info_len);\n+\tprog_fd = bpf_program__fd(ctx-\u003eprog);\n+\tif (!ASSERT_OK_FD(prog_fd, \"get prog fd\"))\n+\t\treturn;\n+\tret = bpf_prog_get_info_by_fd(prog_fd, \u0026info, \u0026info_len);\n+\tif (!ASSERT_OK(ret, \"fetch loaded program info\"))\n+\t\treturn;\n+\tmemcpy(ctx-\u003eprog_tag, info.tag, BPF_TAG_SIZE);\n+\n+\tskip_kernel_logs(ctx-\u003eklog_fd);\n+\n+\ttopts.sz = sizeof(struct bpf_test_run_opts);\n+\ttopts.data_size_in = ETH_HLEN;\n+\ttopts.data_in = buf;\n+\tif (ctx-\u003eaccess_size)\n+\t\t*ctx-\u003eaccess_size = access_size;\n+\tret = bpf_prog_test_run_opts(bpf_program__fd(ctx-\u003eprog),\n+\t\t\t\t \u0026topts);\n+\tif (!ASSERT_OK(ret, \"run prog\"))\n+\t\treturn;\n+\n+\tret = read_kernel_logs(ctx-\u003eklog_fd, klog_buffer, MAX_LOG_SIZE);\n+\tif (!ASSERT_GE(ret, 0, \"read kernel logs\"))\n+\t\treturn;\n+\n+\tret = check_kasan_report_in_kernel_logs(klog_buffer, ctx,\n+\t\t\t\t\t\ttest-\u003eis_write, access_size);\n+\tif (on_stack || test-\u003eexpect_no_report)\n+\t\tASSERT_NEQ(ret, 0, \"no report should be generated\");\n+\telse\n+\t\tASSERT_OK(ret, \"report should be generated\");\n+}\n+\n+static void run_subtest_with_size_and_location(struct test_ctx *ctx,\n+\t\t\t\t\t struct test_spec *test,\n+\t\t\t\t\t int access_size,\n+\t\t\t\t\t bool on_stack)\n+{\n+\tchar subtest_name[SUBTEST_NAME_MAX_LEN];\n+\n+\tif (test-\u003eskip_multi_size_testing) {\n+\t\tsnprintf(subtest_name, SUBTEST_NAME_MAX_LEN, \"%s\",\n+\t\t\t test-\u003eprog_type);\n+\t\tstrncpy(ctx-\u003eprog_name, test-\u003eprog_type, PROG_NAME_MAX_LEN);\n+\t} else {\n+\t\tsnprintf(subtest_name, SUBTEST_NAME_MAX_LEN, \"%s_%d_%s\",\n+\t\t\t test-\u003eprog_type, access_size,\n+\t\t\t on_stack ? \"on_stack\" : \"not_on_stack\");\n+\t\tsnprintf(ctx-\u003eprog_name, PROG_NAME_MAX_LEN, \"%s_%s\",\n+\t\t\t test-\u003eprog_type,\n+\t\t\t on_stack ? \"on_stack\" : \"not_on_stack\");\n+\t}\n+\n+\tif (!test__start_subtest(subtest_name))\n+\t\treturn;\n+\n+\tif (test-\u003eneeds_load_acq_store_rel \u0026\u0026 ctx-\u003eskip_load_acq_store_rel) {\n+\t\ttest__skip();\n+\t\treturn;\n+\t}\n+\n+\texec_subtest(ctx, test, access_size, on_stack);\n+}\n+\n+static void run_subtest_with_size(struct test_ctx *ctx, struct test_spec *test,\n+\t\t\t\t int size)\n+{\n+\trun_subtest_with_size_and_location(ctx, test, size, false);\n+\tif (!test-\u003eskip_on_stack_testing)\n+\t\trun_subtest_with_size_and_location(ctx, test, size, true);\n+}\n+\n+static void run_subtest(struct test_ctx *ctx, struct test_spec *test)\n+{\n+\tif (test-\u003eskip_multi_size_testing) {\n+\t\trun_subtest_with_size(ctx, test, test-\u003erun_size);\n+\t\treturn;\n+\t}\n+\n+\tif (!test-\u003eonly_32_or_64) {\n+\t\trun_subtest_with_size(ctx, test, 1);\n+\t\trun_subtest_with_size(ctx, test, 2);\n+\t}\n+\trun_subtest_with_size(ctx, test, 4);\n+\trun_subtest_with_size(ctx, test, 8);\n+}\n+\n+static void run_blinding_subtest(void)\n+{\n+\tstruct test_spec blinding_spec = {\n+\t\t.prog_type = \"st_blinded\",\n+\t\t.is_write = true,\n+\t};\n+\tchar bpf_jit_harden = '2';\n+\tstruct kasan_harden *skel;\n+\tstruct test_ctx *ctx;\n+\n+\tif (!test__start_subtest(\"st_blinded\"))\n+\t\treturn;\n+\n+\tctx = calloc(1, sizeof(*ctx));\n+\tif (!ASSERT_OK_PTR(ctx, \"alloc blinding ctx\"))\n+\t\treturn;\n+\tctx-\u003eklog_fd = -1;\n+\n+\tif (set_bpf_jit_harden(\u0026bpf_jit_harden))\n+\t\tgoto free_ctx;\n+\n+\tskel = kasan_harden__open_and_load();\n+\tif (!ASSERT_OK_PTR(skel, \"open and load blinded prog\"))\n+\t\tgoto restore;\n+\n+\tctx-\u003eklog_fd = open_kernel_logs();\n+\tif (!ASSERT_OK_FD(ctx-\u003eklog_fd, \"open kernel logs\"))\n+\t\tgoto destroy;\n+\n+\tctx-\u003eobj = skel-\u003eobj;\n+\tstrncpy(ctx-\u003eprog_name, \"st_blinded\", PROG_NAME_MAX_LEN);\n+\n+\texec_subtest(ctx, \u0026blinding_spec, 1, false);\n+\n+destroy:\n+\tclose(ctx-\u003eklog_fd);\n+\tkasan_harden__destroy(skel);\n+restore:\n+\tset_bpf_jit_harden(\u0026bpf_jit_harden);\n+free_ctx:\n+\tfree(ctx);\n+}\n+\n+static struct test_spec tests[] = {\n+\t{\n+\t\t.prog_type = \"st\",\n+\t\t.is_write = true\n+\t},\n+\t{\n+\t\t.prog_type = \"stx\",\n+\t\t.is_write = true\n+\t},\n+\t{\n+\t\t.prog_type = \"ldx\",\n+\t\t.is_write = false\n+\t},\n+\t{\n+\t\t.prog_type = \"simple_atomic\",\n+\t\t.is_write = false,\n+\t\t.only_32_or_64 = true\n+\t},\n+\t{\n+\t\t.prog_type = \"load_acquire\",\n+\t\t.is_write = false,\n+\t\t.needs_load_acq_store_rel = true\n+\t},\n+\t{\n+\t\t.prog_type = \"store_release\",\n+\t\t.is_write = true,\n+\t\t.needs_load_acq_store_rel = true\n+\t},\n+\t{\n+\t\t.prog_type = \"ldx_patched\",\n+\t\t.is_write = false,\n+\t\t.skip_multi_size_testing = true,\n+\t\t.skip_on_stack_testing = true,\n+\t\t.run_size = 4,\n+\t\t.rnd_hi32 = true\n+\t},\n+\t{\n+\t\t.prog_type = \"ldx_patched_on_stack\",\n+\t\t.is_write = false,\n+\t\t.skip_multi_size_testing = true,\n+\t\t.skip_on_stack_testing = true,\n+\t\t.run_size = 4,\n+\t\t.expect_no_report = true,\n+\t\t.rnd_hi32 = true\n+\t},\n+\t{\n+\t\t.prog_type = \"verifier_paths_stack_and_non_stack\",\n+\t\t.is_write = true,\n+\t\t.skip_multi_size_testing = true,\n+\t\t.skip_on_stack_testing = true,\n+\t\t.run_size = 1\n+\t}\n+};\n+\n+void test_kasan(void)\n+{\n+\tstruct kasan_write_val val;\n+\tstruct test_spec *test;\n+\tstruct test_ctx *ctx;\n+\tstruct kasan *skel;\n+\t__u32 key = 0;\n+\tint i, ret;\n+\n+\tctx = calloc(1, sizeof(struct test_ctx));\n+\tif (!ASSERT_OK_PTR(ctx, \"alloc test ctx\"))\n+\t\treturn;\n+\n+\tif (!is_jit_enabled() || !get_kasan_jit_enabled()) {\n+\t\ttest__skip();\n+\t\tgoto end;\n+\t}\n+\n+\tskel = kasan__open();\n+\tif (!ASSERT_OK_PTR(skel, \"open prog\"))\n+\t\tgoto end;\n+\n+\tfor (i = 0; i \u003c ARRAY_SIZE(tests); i++) {\n+\t\tstruct bpf_program *prog;\n+\n+\t\tif (!tests[i].rnd_hi32)\n+\t\t\tcontinue;\n+\n+\t\tprog = bpf_object__find_program_by_name(skel-\u003eobj,\n+\t\t\t\t\t\t\ttests[i].prog_type);\n+\t\tif (!ASSERT_OK_PTR(prog, \"find rnd_hi32 prog\"))\n+\t\t\tgoto destroy;\n+\t\tbpf_program__set_flags(prog, BPF_F_TEST_RND_HI32);\n+\t}\n+\n+\tif (!ASSERT_OK(kasan__load(skel), \"load prog\"))\n+\t\tgoto destroy;\n+\n+\tctx-\u003eobj = skel-\u003eobj;\n+\tctx-\u003eaccess_size = \u0026skel-\u003ebss-\u003eaccess_size;\n+\tctx-\u003eskip_load_acq_store_rel = skel-\u003edata-\u003eskip_load_acq_store_rel_tests;\n+\n+\tctx-\u003eklog_fd = open_kernel_logs();\n+\tif (!ASSERT_OK_FD(ctx-\u003eklog_fd, \"open kernel logs\"))\n+\t\tgoto destroy;\n+\n+\t/* Fill map with recognizable values */\n+\tret = bpf_map__lookup_elem(skel-\u003emaps.test_map, \u0026key, sizeof(key),\n+\t\t\t\t \u0026val, sizeof(val), 0);\n+\tif (!ASSERT_OK(ret, \"get map\"))\n+\t\tgoto close;\n+\tval.data_1 = 0xAA;\n+\tval.data_2 = 0xBBBB;\n+\tval.data_4 = 0xCCCCCCCC;\n+\tval.data_8 = 0xDDDDDDDDDDDDDDDD;\n+\tret = bpf_map__update_elem(skel-\u003emaps.test_map, \u0026key, sizeof(key),\n+\t\t\t\t \u0026val, sizeof(val), 0);\n+\tif (!ASSERT_OK(ret, \"set map\"))\n+\t\tgoto close;\n+\n+\tfor (i = 0; i \u003c ARRAY_SIZE(tests); i++) {\n+\t\ttest = \u0026tests[i];\n+\t\trun_subtest(ctx, test);\n+\t}\n+\n+\t/*\n+\t * Blinding subtest is handled differently as it needs the\n+\t * corresponding program to be loaded with bpf_jit_harden raised\n+\t */\n+\trun_blinding_subtest();\n+\n+close:\n+\tclose(ctx-\u003eklog_fd);\n+destroy:\n+\tkasan__destroy(skel);\n+end:\n+\tfree(ctx);\n+}\ndiff --git a/tools/testing/selftests/bpf/progs/kasan.c b/tools/testing/selftests/bpf/progs/kasan.c\nnew file mode 100644\nindex 00000000000000..82ddce3059aee1\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/kasan.c\n@@ -0,0 +1,394 @@\n+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause\n+\n+#include \u003clinux/bpf.h\u003e\n+#include \u003cbpf/bpf_helpers.h\u003e\n+#include \u003cbpf/bpf_tracing.h\u003e\n+#include \"bpf_misc.h\"\n+#include \u003cstdbool.h\u003e\n+\n+extern void bpf_kfunc_kasan_poison(void *mem, __u32 mem__sz) __ksym;\n+extern void bpf_kfunc_kasan_unpoison(void *mem, __u32 mem__sz) __ksym;\n+\n+int access_size;\n+\n+struct kasan_write_val {\n+\t__u8 data_1;\n+\t__u16 data_2;\n+\t__u32 data_4;\n+\t__u64 data_8;\n+};\n+\n+struct {\n+\t__uint(type, BPF_MAP_TYPE_ARRAY);\n+\t__uint(max_entries, 1);\n+\t__type(key, __u32);\n+\t__type(value, struct kasan_write_val);\n+} test_map SEC(\".maps\");\n+\n+SEC(\"tcx/ingress\")\n+int st_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_write_val val;\n+\n+\tbpf_kfunc_kasan_poison(\u0026val, sizeof(struct kasan_write_val));\n+\tswitch (access_size) {\n+\tcase 1:\n+\t\tval.data_1 = 0xAA;\n+\t\tbreak;\n+\tcase 2:\n+\t\tval.data_2 = 0xAA;\n+\t\tbreak;\n+\tcase 4:\n+\t\tval.data_4 = 0xAA;\n+\t\tbreak;\n+\tcase 8:\n+\t\tval.data_8 = 0xAA;\n+\t\tbreak;\n+\t}\n+\tbpf_kfunc_kasan_unpoison(\u0026val, sizeof(struct kasan_write_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int st_not_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_write_val *val;\n+\t__u32 key = 0;\n+\n+\tval = bpf_map_lookup_elem(\u0026test_map, \u0026key);\n+\tif (!val)\n+\t\treturn 0;\n+\n+\tbpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));\n+\tswitch (access_size) {\n+\tcase 1:\n+\t\tval-\u003edata_1 = 0xAA;\n+\t\tbreak;\n+\tcase 2:\n+\t\tval-\u003edata_2 = 0xAA;\n+\t\tbreak;\n+\tcase 4:\n+\t\tval-\u003edata_4 = 0xAA;\n+\t\tbreak;\n+\tcase 8:\n+\t\tval-\u003edata_8 = 0xAA;\n+\t\tbreak;\n+\t}\n+\tbpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int stx_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_write_val val;\n+\n+\tbpf_kfunc_kasan_poison(\u0026val, sizeof(struct kasan_write_val));\n+\tswitch (access_size) {\n+\tcase 1:\n+\t\tval.data_1 = access_size;\n+\t\tbreak;\n+\tcase 2:\n+\t\tval.data_2 = access_size;\n+\t\tbreak;\n+\tcase 4:\n+\t\tval.data_4 = access_size;\n+\t\tbreak;\n+\tcase 8:\n+\t\tval.data_8 = access_size;\n+\t\tbreak;\n+\t}\n+\tbpf_kfunc_kasan_unpoison(\u0026val, sizeof(struct kasan_write_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int stx_not_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_write_val *val;\n+\t__u32 key = 0;\n+\n+\tval = bpf_map_lookup_elem(\u0026test_map, \u0026key);\n+\tif (!val)\n+\t\treturn 0;\n+\n+\tbpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));\n+\tswitch (access_size) {\n+\tcase 1:\n+\t\tval-\u003edata_1 = access_size;\n+\t\tbreak;\n+\tcase 2:\n+\t\tval-\u003edata_2 = access_size;\n+\t\tbreak;\n+\tcase 4:\n+\t\tval-\u003edata_4 = access_size;\n+\t\tbreak;\n+\tcase 8:\n+\t\tval-\u003edata_8 = access_size;\n+\t\tbreak;\n+\t}\n+\tbpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int ldx_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_write_val val;\n+\n+\tbpf_kfunc_kasan_poison(\u0026val, sizeof(struct kasan_write_val));\n+\tswitch (access_size) {\n+\tcase 1:\n+\t\t__sink(val.data_1);\n+\t\tbreak;\n+\tcase 2:\n+\t\t__sink(val.data_2);\n+\t\tbreak;\n+\tcase 4:\n+\t\t__sink(val.data_4);\n+\t\tbreak;\n+\tcase 8:\n+\t\t__sink(val.data_8);\n+\t\tbreak;\n+\t}\n+\tbpf_kfunc_kasan_unpoison(\u0026val, sizeof(struct kasan_write_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int ldx_not_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_write_val *val;\n+\t__u32 key = 0;\n+\n+\tval = bpf_map_lookup_elem(\u0026test_map, \u0026key);\n+\tif (!val)\n+\t\treturn 0;\n+\n+\tbpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));\n+\tswitch (access_size) {\n+\tcase 1:\n+\t\t__sink(val-\u003edata_1);\n+\t\tbreak;\n+\tcase 2:\n+\t\t__sink(val-\u003edata_2);\n+\t\tbreak;\n+\tcase 4:\n+\t\t__sink(val-\u003edata_4);\n+\t\tbreak;\n+\tcase 8:\n+\t\t__sink(val-\u003edata_8);\n+\t\tbreak;\n+\t}\n+\tbpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int ldx_patched(struct __sk_buff *skb)\n+{\n+\tstruct kasan_write_val *val;\n+\t__u32 key = 0;\n+\n+\tval = bpf_map_lookup_elem(\u0026test_map, \u0026key);\n+\tif (!val)\n+\t\treturn 0;\n+\n+\tbpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));\n+\t__sink(val-\u003edata_4);\n+\tbpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));\n+\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int ldx_patched_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_write_val val;\n+\n+\tbpf_kfunc_kasan_poison(\u0026val, sizeof(struct kasan_write_val));\n+\t__sink(val.data_4);\n+\tbpf_kfunc_kasan_unpoison(\u0026val, sizeof(struct kasan_write_val));\n+\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int simple_atomic_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_write_val val;\n+\n+\tbpf_kfunc_kasan_poison(\u0026val, sizeof(struct kasan_write_val));\n+\tswitch (access_size) {\n+\tcase 4:\n+\t\t__sync_fetch_and_add(\u0026val.data_4, 4);\n+\t\tbreak;\n+\tcase 8:\n+\t\t__sync_fetch_and_add(\u0026val.data_8, 8);\n+\t\tbreak;\n+\t}\n+\tbpf_kfunc_kasan_unpoison(\u0026val, sizeof(struct kasan_write_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int simple_atomic_not_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_write_val *val;\n+\t__u32 key = 0;\n+\n+\tval = bpf_map_lookup_elem(\u0026test_map, \u0026key);\n+\tif (!val)\n+\t\treturn 0;\n+\n+\tbpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));\n+\tswitch (access_size) {\n+\tcase 4:\n+\t\t__sync_fetch_and_add(\u0026val-\u003edata_4, 4);\n+\t\tbreak;\n+\tcase 8:\n+\t\t__sync_fetch_and_add(\u0026val-\u003edata_8, 8);\n+\t\tbreak;\n+\t}\n+\tbpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));\n+\treturn 0;\n+}\n+\n+#ifdef __BPF_FEATURE_LOAD_ACQ_STORE_REL\n+bool skip_load_acq_store_rel_tests SEC(\".data\") = 0;\n+\n+SEC(\"tcx/ingress\")\n+int load_acquire_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_write_val val;\n+\n+\tbpf_kfunc_kasan_poison(\u0026val, sizeof(struct kasan_write_val));\n+\tswitch (access_size) {\n+\tcase 1:\n+\t\t__atomic_load_n(\u0026val.data_1, __ATOMIC_ACQUIRE);\n+\t\tbreak;\n+\tcase 2:\n+\t\t__atomic_load_n(\u0026val.data_2, __ATOMIC_ACQUIRE);\n+\t\tbreak;\n+\tcase 4:\n+\t\t__atomic_load_n(\u0026val.data_4, __ATOMIC_ACQUIRE);\n+\t\tbreak;\n+\tcase 8:\n+\t\t__atomic_load_n(\u0026val.data_8, __ATOMIC_ACQUIRE);\n+\t\tbreak;\n+\t}\n+\tbpf_kfunc_kasan_unpoison(\u0026val, sizeof(struct kasan_write_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int load_acquire_not_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_write_val *val;\n+\t__u32 key = 0;\n+\n+\tval = bpf_map_lookup_elem(\u0026test_map, \u0026key);\n+\tif (!val)\n+\t\treturn 0;\n+\n+\tbpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));\n+\tswitch (access_size) {\n+\tcase 1:\n+\t\t__atomic_load_n(\u0026val-\u003edata_1, __ATOMIC_ACQUIRE);\n+\t\tbreak;\n+\tcase 2:\n+\t\t__atomic_load_n(\u0026val-\u003edata_2, __ATOMIC_ACQUIRE);\n+\t\tbreak;\n+\tcase 4:\n+\t\t__atomic_load_n(\u0026val-\u003edata_4, __ATOMIC_ACQUIRE);\n+\t\tbreak;\n+\tcase 8:\n+\t\t__atomic_load_n(\u0026val-\u003edata_8, __ATOMIC_ACQUIRE);\n+\t\tbreak;\n+\t}\n+\tbpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int store_release_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_write_val val;\n+\n+\tbpf_kfunc_kasan_poison(\u0026val, sizeof(struct kasan_write_val));\n+\tswitch (access_size) {\n+\tcase 1:\n+\t\t__atomic_store_n(\u0026val.data_1, 0xAA, __ATOMIC_RELEASE);\n+\t\tbreak;\n+\tcase 2:\n+\t\t__atomic_store_n(\u0026val.data_2, 0xBBBB, __ATOMIC_RELEASE);\n+\t\tbreak;\n+\tcase 4:\n+\t\t__atomic_store_n(\u0026val.data_4, 0xCCCCCCCC, __ATOMIC_RELEASE);\n+\t\tbreak;\n+\tcase 8:\n+\t\t__atomic_store_n(\u0026val.data_8, 0xDDDDDDDDDDDDDDDD,\n+\t\t\t\t __ATOMIC_RELEASE);\n+\t\tbreak;\n+\t}\n+\tbpf_kfunc_kasan_unpoison(\u0026val, sizeof(struct kasan_write_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int store_release_not_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_write_val *val;\n+\t__u32 key = 0;\n+\n+\tval = bpf_map_lookup_elem(\u0026test_map, \u0026key);\n+\tif (!val)\n+\t\treturn 0;\n+\n+\tbpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));\n+\tswitch (access_size) {\n+\tcase 1:\n+\t\t__atomic_store_n(\u0026val-\u003edata_1, 0xAA, __ATOMIC_RELEASE);\n+\t\tbreak;\n+\tcase 2:\n+\t\t__atomic_store_n(\u0026val-\u003edata_2, 0xBBBB, __ATOMIC_RELEASE);\n+\t\tbreak;\n+\tcase 4:\n+\t\t__atomic_store_n(\u0026val-\u003edata_4, 0xCCCCCCCC, __ATOMIC_RELEASE);\n+\t\tbreak;\n+\tcase 8:\n+\t\t__atomic_store_n(\u0026val-\u003edata_8, 0xDDDDDDDDDDDDDDDD,\n+\t\t\t\t __ATOMIC_RELEASE);\n+\t\tbreak;\n+\t}\n+\tbpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));\n+\treturn 0;\n+}\n+#else\n+bool skip_load_acq_store_rel_tests SEC(\".data\") = 1;\n+#endif\n+\n+SEC(\"tcx/ingress\")\n+int verifier_paths_stack_and_non_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_write_val stack_val = {};\n+\tstruct kasan_write_val *val;\n+\tvoid *ptr;\n+\t__u32 key = 0;\n+\n+\tval = bpf_map_lookup_elem(\u0026test_map, \u0026key);\n+\tif (!val)\n+\t\treturn 0;\n+\n+\tif (access_size)\n+\t\tptr = val;\n+\telse\n+\t\tptr = \u0026stack_val;\n+\n+\tbpf_kfunc_kasan_poison(val, sizeof(*val));\n+\t*(__u8 *)ptr = 0xAA;\n+\tbpf_kfunc_kasan_unpoison(val, sizeof(*val));\n+\treturn 0;\n+}\n+\n+char LICENSE[] SEC(\"license\") = \"GPL\";\ndiff --git a/tools/testing/selftests/bpf/progs/kasan_harden.c b/tools/testing/selftests/bpf/progs/kasan_harden.c\nnew file mode 100644\nindex 00000000000000..808d7da6442162\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/kasan_harden.c\n@@ -0,0 +1,41 @@\n+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause\n+\n+#include \u003clinux/bpf.h\u003e\n+#include \u003cbpf/bpf_helpers.h\u003e\n+#include \u003cbpf/bpf_tracing.h\u003e\n+\n+extern void bpf_kfunc_kasan_poison(void *mem, __u32 mem__sz) __ksym;\n+extern void bpf_kfunc_kasan_unpoison(void *mem, __u32 mem__sz) __ksym;\n+\n+struct kasan_write_val {\n+\t__u8 data_1;\n+\t__u16 data_2;\n+\t__u32 data_4;\n+\t__u64 data_8;\n+};\n+\n+struct {\n+\t__uint(type, BPF_MAP_TYPE_ARRAY);\n+\t__uint(max_entries, 1);\n+\t__type(key, __u32);\n+\t__type(value, struct kasan_write_val);\n+} test_map SEC(\".maps\");\n+\n+SEC(\"tcx/ingress\")\n+int st_blinded(struct __sk_buff *skb)\n+{\n+\tstruct kasan_write_val *val;\n+\t__u32 key = 0;\n+\n+\tval = bpf_map_lookup_elem(\u0026test_map, \u0026key);\n+\tif (!val)\n+\t\treturn 0;\n+\n+\tbpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));\n+\tval-\u003edata_1 = 0xAA;\n+\tbpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));\n+\n+\treturn 0;\n+}\n+\n+char LICENSE[] SEC(\"license\") = \"GPL\";\ndiff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c\nindex 30f1cd23093cbe..5d6a834f7d7575 100644\n--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c\n+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c\n@@ -271,6 +271,26 @@ __bpf_kfunc void bpf_kfunc_put_default_trusted_ptr_test(struct prog_test_member\n \t */\n }\n \n+#ifdef CONFIG_BPF_JIT_KASAN\n+\n+extern void kasan_poison(const void *addr, size_t size, u8 value, bool init);\n+\n+#define KASAN_SLAB_FREE 0xFB\n+\n+__bpf_kfunc void bpf_kfunc_kasan_poison(void *mem, u32 mem__sz)\n+{\n+\tkasan_poison(mem, mem__sz, KASAN_SLAB_FREE, false);\n+}\n+\n+__bpf_kfunc void bpf_kfunc_kasan_unpoison(void *mem, u32 mem__sz)\n+{\n+\tkasan_poison(mem, mem__sz, 0x00, false);\n+}\n+#else\n+__bpf_kfunc void bpf_kfunc_kasan_poison(void *mem, u32 mem__sz) { }\n+__bpf_kfunc void bpf_kfunc_kasan_unpoison(void *mem, u32 mem__sz) { }\n+#endif\n+\n __bpf_kfunc struct bpf_testmod_ctx *\n bpf_testmod_ctx_create(int *err)\n {\n@@ -740,6 +760,8 @@ BTF_ID_FLAGS(func, bpf_testmod_ops3_call_test_1)\n BTF_ID_FLAGS(func, bpf_testmod_ops3_call_test_2)\n BTF_ID_FLAGS(func, bpf_kfunc_get_default_trusted_ptr_test);\n BTF_ID_FLAGS(func, bpf_kfunc_put_default_trusted_ptr_test);\n+BTF_ID_FLAGS(func, bpf_kfunc_kasan_poison)\n+BTF_ID_FLAGS(func, bpf_kfunc_kasan_unpoison)\n BTF_KFUNCS_END(bpf_testmod_common_kfunc_ids)\n \n BTF_ID_LIST(bpf_testmod_dtor_ids)\ndiff --git a/tools/testing/selftests/bpf/testing_helpers.c b/tools/testing/selftests/bpf/testing_helpers.c\nindex c970e7793dfcbc..737f668b35e23b 100644\n--- a/tools/testing/selftests/bpf/testing_helpers.c\n+++ b/tools/testing/selftests/bpf/testing_helpers.c\n@@ -519,6 +519,38 @@ bool is_jit_enabled(void)\n \treturn enabled;\n }\n \n+int set_bpf_jit_harden(char *level)\n+{\n+\tchar old_level;\n+\tint err = -1;\n+\tint fd = -1;\n+\n+\tfd = open(\"/proc/sys/net/core/bpf_jit_harden\", O_RDWR | O_NONBLOCK);\n+\tif (fd \u003c 0)\n+\t\treturn -1;\n+\n+\terr = read(fd, \u0026old_level, 1);\n+\tif (err != 1) {\n+\t\terr = -1;\n+\t\tgoto end;\n+\t}\n+\n+\tlseek(fd, 0, SEEK_SET);\n+\n+\terr = write(fd, level, 1);\n+\tif (err != 1) {\n+\t\terr = -1;\n+\t\tgoto end;\n+\t}\n+\n+\terr = 0;\n+\t*level = old_level;\n+end:\n+\tif (fd \u003e= 0)\n+\t\tclose(fd);\n+\treturn err;\n+}\n+\n int stack_mprotect(void)\n {\n \tvoid *buf;\ndiff --git a/tools/testing/selftests/bpf/testing_helpers.h b/tools/testing/selftests/bpf/testing_helpers.h\nindex 2edc6fb7fc5212..e00642afe86f40 100644\n--- a/tools/testing/selftests/bpf/testing_helpers.h\n+++ b/tools/testing/selftests/bpf/testing_helpers.h\n@@ -59,6 +59,7 @@ struct bpf_insn;\n int get_xlated_program(int fd_prog, struct bpf_insn **buf, __u32 *cnt);\n int testing_prog_flags(void);\n bool is_jit_enabled(void);\n+int set_bpf_jit_harden(char *level);\n int stack_mprotect(void);\n \n #endif /* __TESTING_HELPERS_H */\ndiff --git a/tools/testing/selftests/bpf/unpriv_helpers.c b/tools/testing/selftests/bpf/unpriv_helpers.c\nindex f997d7ec8fd086..11201b65a3d4b5 100644\n--- a/tools/testing/selftests/bpf/unpriv_helpers.c\n+++ b/tools/testing/selftests/bpf/unpriv_helpers.c\n@@ -142,3 +142,8 @@ bool get_unpriv_disabled(void)\n \t}\n \treturn mitigations_off;\n }\n+\n+bool get_kasan_jit_enabled(void)\n+{\n+\treturn config_contains(\"CONFIG_BPF_JIT_KASAN=y\") == 1;\n+}\ndiff --git a/tools/testing/selftests/bpf/unpriv_helpers.h b/tools/testing/selftests/bpf/unpriv_helpers.h\nindex 151f6732966522..bc5f4c953c9d6f 100644\n--- a/tools/testing/selftests/bpf/unpriv_helpers.h\n+++ b/tools/testing/selftests/bpf/unpriv_helpers.h\n@@ -5,3 +5,4 @@\n #define UNPRIV_SYSCTL \"kernel/unprivileged_bpf_disabled\"\n \n bool get_unpriv_disabled(void);\n+bool get_kasan_jit_enabled(void);\n"
} |
| 2/1 | 2026/07/08 20:14 | agent | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": [
"BPF_JIT_KASAN"
],
"FocusSymbols": [
"bpf_int_jit_compile",
"bpf_do_misc_fixups",
"check_mem_access"
],
"Reasoning": "The patch adds KASAN support to the BPF JIT compiler on x86_64, which is a functional change to core kernel logic.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit 1d6ddf9706a869bc6d255e1c7d0ae6fbadb799b8
Author: syz-cluster <triage@syzkaller.com>
Date: Wed Jul 8 20:14:15 2026 +0000
syz-cluster: applied patch under review
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index bdad90f210e4bf..705a9ab85f3a95 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -233,6 +233,7 @@ config X86
select HAVE_SAMPLE_FTRACE_DIRECT if X86_64
select HAVE_SAMPLE_FTRACE_DIRECT_MULTI if X86_64
select HAVE_EBPF_JIT
+ select HAVE_EBPF_JIT_KASAN if X86_64
select HAVE_EFFICIENT_UNALIGNED_ACCESS
select HAVE_EISA if X86_32
select HAVE_EXIT_THREAD
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index de7515ea1beae8..65a00e84799cd2 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -21,6 +21,17 @@
#include <asm/unwind.h>
#include <asm/cfi.h>
+#if IS_ENABLED(CONFIG_BPF_JIT_KASAN)
+void __asan_load1(void *p);
+void __asan_store1(void *p);
+void __asan_load2(void *p);
+void __asan_store2(void *p);
+void __asan_load4(void *p);
+void __asan_store4(void *p);
+void __asan_load8(void *p);
+void __asan_store8(void *p);
+#endif
+
static bool all_callee_regs_used[4] = {true, true, true, true};
static u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len)
@@ -1110,6 +1121,90 @@ static void maybe_emit_1mod(u8 **pprog, u32 reg, bool is64)
*pprog = prog;
}
+static int emit_kasan_check(struct bpf_verifier_env *env, u8 **pprog,
+ u32 addr_reg, struct bpf_insn *insn, u8 *ip,
+ bool is_write, bool accesses_stack_only)
+{
+#ifdef CONFIG_BPF_JIT_KASAN
+ u32 bpf_size = BPF_SIZE(insn->code);
+ s32 off = insn->off;
+ u8 *prog = *pprog;
+ void *kasan_func;
+
+ if (!env)
+ return 0;
+
+ if (accesses_stack_only)
+ return 0;
+
+ /* Derive KASAN check function from access type and size */
+ switch (bpf_size) {
+ case BPF_B:
+ kasan_func = is_write ? __asan_store1 : __asan_load1;
+ break;
+ case BPF_H:
+ kasan_func = is_write ? __asan_store2 : __asan_load2;
+ break;
+ case BPF_W:
+ kasan_func = is_write ? __asan_store4 : __asan_load4;
+ break;
+ case BPF_DW:
+ kasan_func = is_write ? __asan_store8 : __asan_load8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Save rax */
+ EMIT1(0x50);
+ /* Save rcx */
+ EMIT1(0x51);
+ /* Save rdx */
+ EMIT1(0x52);
+ /* Save rsi */
+ EMIT1(0x56);
+ /* Save rdi */
+ EMIT1(0x57);
+ /* Save r8 */
+ EMIT2(0x41, 0x50);
+ /* Save r9 */
+ EMIT2(0x41, 0x51);
+
+ /* mov rdi, addr_reg */
+ EMIT_mov(BPF_REG_1, addr_reg);
+
+ /* add rdi, off (if offset is non-zero) */
+ if (off) {
+ if (is_imm8(off)) {
+ /* add rdi, imm8 */
+ EMIT4(0x48, 0x83, 0xC7, (u8)off);
+ } else {
+ /* add rdi, imm32 */
+ EMIT3_off32(0x48, 0x81, 0xC7, off);
+ }
+ }
+
+ /* Adjust ip to account for the instrumentation generated so far */
+ ip += (prog - *pprog);
+ /* We emit a call, so update call depth counting */
+ ip += x86_call_depth_emit_accounting(&prog, kasan_func, ip);
+ /* call kasan_func */
+ if (emit_call(&prog, kasan_func, ip))
+ return -ERANGE;
+
+ EMIT2(0x41, 0x59);
+ EMIT2(0x41, 0x58);
+ EMIT1(0x5F);
+ EMIT1(0x5E);
+ EMIT1(0x5A);
+ EMIT1(0x59);
+ EMIT1(0x58);
+
+ *pprog = prog;
+#endif /* CONFIG_BPF_JIT_KASAN */
+ return 0;
+}
+
/* LDX: dst_reg = *(u8*)(src_reg + off) */
static void emit_ldx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)
{
@@ -1315,6 +1410,64 @@ static void emit_st_index(u8 **pprog, u32 size, u32 dst_reg, u32 index_reg, int
*pprog = prog;
}
+static void emit_st(u8 **pprog, struct bpf_insn *insn, int dst_reg,
+ s32 outgoing_arg_base, u16 outgoing_rsp)
+{
+ s32 imm32 = insn->imm;
+ u8 *prog = *pprog;
+ s32 insn_off;
+
+ switch (BPF_SIZE(insn->code)) {
+ case BPF_B:
+ if (is_ereg(dst_reg))
+ EMIT2(0x41, 0xC6);
+ else
+ EMIT1(0xC6);
+ break;
+ case BPF_H:
+ if (is_ereg(dst_reg))
+ EMIT3(0x66, 0x41, 0xC7);
+ else
+ EMIT2(0x66, 0xC7);
+ break;
+ case BPF_W:
+ if (is_ereg(dst_reg))
+ EMIT2(0x41, 0xC7);
+ else
+ EMIT1(0xC7);
+ break;
+ case BPF_DW:
+ if (dst_reg == BPF_REG_PARAMS && insn->off == -8) {
+ /* Arg 6: store immediate in r9 register */
+ emit_mov_imm64(&prog, X86_REG_R9, imm32 >> 31,
+ (u32)imm32);
+ *pprog = prog;
+ return;
+ }
+ EMIT2(add_1mod(0x48, dst_reg), 0xC7);
+ break;
+ }
+
+ insn_off = insn->off;
+ if (dst_reg == BPF_REG_PARAMS) {
+ /*
+ * Args 7+: reverse BPF negative offsets to
+ * x86 positive rsp offsets.
+ * BPF off=-16 → [rsp+0], off=-24 → [rsp+8], ...
+ */
+ insn_off = outgoing_arg_base - outgoing_rsp -
+ insn_off - 16;
+ dst_reg = BPF_REG_FP;
+ }
+ if (is_imm8(insn_off))
+ EMIT2(add_1reg(0x40, dst_reg), insn_off);
+ else
+ EMIT1_off32(add_1reg(0x80, dst_reg), insn_off);
+
+ EMIT(imm32, bpf_size_to_x86_bytes(BPF_SIZE(insn->code)));
+ *pprog = prog;
+}
+
static void emit_st_r12(u8 **pprog, u32 size, u32 dst_reg, int off, int imm)
{
emit_st_index(pprog, size, dst_reg, X86_REG_R12, off, imm);
@@ -1423,17 +1576,31 @@ static int emit_atomic_rmw_index(u8 **pprog, u32 atomic_op, u32 size,
return 0;
}
-static int emit_atomic_ld_st(u8 **pprog, u32 atomic_op, u32 dst_reg,
- u32 src_reg, s16 off, u8 bpf_size)
+static int emit_atomic_ld_st(struct bpf_verifier_env *env, u8 **pprog,
+ struct bpf_insn *insn, u8 *ip, u32 dst_reg,
+ u32 src_reg, bool accesses_stack_only)
{
+ u32 atomic_op = insn->imm;
+ int err;
+
switch (atomic_op) {
case BPF_LOAD_ACQ:
+ err = emit_kasan_check(env, pprog, src_reg, insn, ip, false,
+ accesses_stack_only);
+ if (err)
+ return err;
/* dst_reg = smp_load_acquire(src_reg + off16) */
- emit_ldx(pprog, bpf_size, dst_reg, src_reg, off);
+ emit_ldx(pprog, BPF_SIZE(insn->code), dst_reg, src_reg,
+ insn->off);
break;
case BPF_STORE_REL:
+ err = emit_kasan_check(env, pprog, dst_reg, insn, ip, true,
+ accesses_stack_only);
+ if (err)
+ return err;
/* smp_store_release(dst_reg + off16, src_reg) */
- emit_stx(pprog, bpf_size, dst_reg, src_reg, off);
+ emit_stx(pprog, BPF_SIZE(insn->code), dst_reg, src_reg,
+ insn->off);
break;
default:
pr_err("bpf_jit: unknown atomic load/store opcode %02x\n",
@@ -1811,10 +1978,12 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
const s32 imm32 = insn->imm;
u32 dst_reg = insn->dst_reg;
u32 src_reg = insn->src_reg;
+ bool accesses_stack_only;
u8 b2 = 0, b3 = 0;
u8 *start_of_ldx;
s64 jmp_offset;
s32 insn_off;
+ int insn_idx;
u8 jmp_cond;
u8 *func;
int nops;
@@ -1831,6 +2000,10 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
EMIT_ENDBR();
ip = image + addrs[i - 1] + (prog - temp);
+ insn_idx = i - 1 + bpf_prog->aux->subprog_start;
+ accesses_stack_only =
+ env ? !env->insn_aux_data[insn_idx].non_stack_access :
+ false;
switch (insn->code) {
/* ALU */
@@ -2207,49 +2380,17 @@ static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *
EMIT_LFENCE();
break;
- /* ST: *(u8*)(dst_reg + off) = imm */
case BPF_ST | BPF_MEM | BPF_B:
- if (is_ereg(dst_reg))
- EMIT2(0x41, 0xC6);
- else
- EMIT1(0xC6);
- goto st;
case BPF_ST | BPF_MEM | BPF_H:
- if (is_ereg(dst_reg))
- EMIT3(0x66, 0x41, 0xC7);
- else
- EMIT2(0x66, 0xC7);
- goto st;
case BPF_ST | BPF_MEM | BPF_W:
- if (is_ereg(dst_reg))
- EMIT2(0x41, 0xC7);
- else
- EMIT1(0xC7);
- goto st;
case BPF_ST | BPF_MEM | BPF_DW:
- if (dst_reg == BPF_REG_PARAMS && insn->off == -8) {
- /* Arg 6: store immediate in r9 register */
- emit_mov_imm64(&prog, X86_REG_R9, imm32 >> 31, (u32)imm32);
- break;
- }
- EMIT2(add_1mod(0x48, dst_reg), 0xC7);
-
-st: insn_off = insn->off;
- if (dst_reg == BPF_REG_PARAMS) {
- /*
- * Args 7+: reverse BPF negative offsets to
- * x86 positive rsp offsets.
- * BPF off=-16 → [rsp+0], off=-24 → [rsp+8], ...
- */
- insn_off = outgoing_arg_base - outgoing_rsp - insn_off - 16;
- dst_reg = BPF_REG_FP;
- }
- if (is_imm8(insn_off))
- EMIT2(add_1reg(0x40, dst_reg), insn_off);
- else
- EMIT1_off32(add_1reg(0x80, dst_reg), insn_off);
+ err = emit_kasan_check(env, &prog, dst_reg, insn, ip,
+ true, accesses_stack_only);
+ if (err)
+ return err;
- EMIT(imm32, bpf_size_to_x86_bytes(BPF_SIZE(insn->code)));
+ emit_st(&prog, insn, dst_reg, outgoing_arg_base,
+ outgoing_rsp);
break;
/* STX: *(u8*)(dst_reg + off) = src_reg */
@@ -2267,6 +2408,10 @@ st: insn_off = insn->off;
insn_off = outgoing_arg_base - outgoing_rsp - insn_off - 16;
dst_reg = BPF_REG_FP;
}
+ err = emit_kasan_check(env, &prog, dst_reg, insn, ip,
+ true, accesses_stack_only);
+ if (err)
+ return err;
emit_stx(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn_off);
break;
@@ -2428,6 +2573,12 @@ st: insn_off = insn->off;
/* populate jmp_offset for JAE above to jump to start_of_ldx */
start_of_ldx = prog;
end_of_jmp[-1] = start_of_ldx - end_of_jmp;
+ } else {
+ err = emit_kasan_check(env, &prog, src_reg,
+ insn, ip, false,
+ accesses_stack_only);
+ if (err)
+ return err;
}
if (BPF_MODE(insn->code) == BPF_PROBE_MEMSX ||
BPF_MODE(insn->code) == BPF_MEMSX)
@@ -2489,15 +2640,16 @@ st: insn_off = insn->off;
}
fallthrough;
case BPF_STX | BPF_ATOMIC | BPF_W:
- case BPF_STX | BPF_ATOMIC | BPF_DW:
- if (insn->imm == (BPF_AND | BPF_FETCH) ||
- insn->imm == (BPF_OR | BPF_FETCH) ||
- insn->imm == (BPF_XOR | BPF_FETCH)) {
- bool is64 = BPF_SIZE(insn->code) == BPF_DW;
- u32 real_src_reg = src_reg;
- u32 real_dst_reg = dst_reg;
- u8 *branch_target;
-
+ case BPF_STX | BPF_ATOMIC | BPF_DW: {
+ bool is64 = BPF_SIZE(insn->code) == BPF_DW;
+ u32 real_src_reg = src_reg;
+ u32 real_dst_reg = dst_reg;
+ u8 *branch_target;
+ bool is_atomic_fetch =
+ (insn->imm == (BPF_AND | BPF_FETCH) ||
+ insn->imm == (BPF_OR | BPF_FETCH) ||
+ insn->imm == (BPF_XOR | BPF_FETCH));
+ if (is_atomic_fetch) {
/*
* Can't be implemented with a single x86 insn.
* Need to do a CMPXCHG loop.
@@ -2510,7 +2662,17 @@ st: insn_off = insn->off;
if (dst_reg == BPF_REG_0)
real_dst_reg = BPF_REG_AX;
+ ip += 3;
+ }
+ if (!bpf_atomic_is_load_store(insn)) {
+ err = emit_kasan_check(env, &prog, real_dst_reg,
+ insn, ip, false,
+ accesses_stack_only);
+ if (err)
+ return err;
branch_target = prog;
+ }
+ if (is_atomic_fetch) {
/* Load old value */
emit_ldx(&prog, BPF_SIZE(insn->code),
BPF_REG_0, real_dst_reg, insn->off);
@@ -2542,15 +2704,16 @@ st: insn_off = insn->off;
}
if (bpf_atomic_is_load_store(insn))
- err = emit_atomic_ld_st(&prog, insn->imm, dst_reg, src_reg,
- insn->off, BPF_SIZE(insn->code));
+ err = emit_atomic_ld_st(env, &prog, insn, ip,
+ dst_reg, src_reg,
+ accesses_stack_only);
else
err = emit_atomic_rmw(&prog, insn->imm, dst_reg, src_reg,
insn->off, BPF_SIZE(insn->code));
if (err)
return err;
break;
-
+ }
case BPF_STX | BPF_PROBE_ATOMIC | BPF_B:
case BPF_STX | BPF_PROBE_ATOMIC | BPF_H:
if (!bpf_atomic_is_load_store(insn)) {
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 76b8b7627a1085..868101ef5002ab 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -723,6 +723,8 @@ struct bpf_insn_aux_data {
u16 const_reg_map_mask;
u16 const_reg_subprog_mask;
u32 const_reg_vals[10];
+ /* instruction can access non-stack memory */
+ bool non_stack_access;
};
#define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */
diff --git a/include/linux/filter.h b/include/linux/filter.h
index 14acb2455746fc..1ebcd247ef4e3f 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -1210,13 +1210,17 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off,
#ifdef CONFIG_BPF_SYSCALL
struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
- const struct bpf_insn *patch, u32 len);
+ const struct bpf_insn *patch, u32 len,
+ s32 insn_off_in_patch);
struct bpf_insn_aux_data *bpf_dup_insn_aux_data(struct bpf_verifier_env *env);
void bpf_restore_insn_aux_data(struct bpf_verifier_env *env,
struct bpf_insn_aux_data *orig_insn_aux);
#else
-static inline struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
- const struct bpf_insn *patch, u32 len)
+static inline struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env,
+ u32 off,
+ const struct bpf_insn *patch,
+ u32 len,
+ s32 insn_off_in_patch)
{
return ERR_PTR(-ENOTSUPP);
}
diff --git a/kernel/bpf/Kconfig b/kernel/bpf/Kconfig
index eb3de35734f092..e1f3850d2f5a07 100644
--- a/kernel/bpf/Kconfig
+++ b/kernel/bpf/Kconfig
@@ -17,6 +17,10 @@ config HAVE_CBPF_JIT
config HAVE_EBPF_JIT
bool
+# KASAN support for JIT compiler
+config HAVE_EBPF_JIT_KASAN
+ bool
+
# Used by archs to tell that they want the BPF JIT compiler enabled by
# default for kernels that were compiled with BPF JIT support.
config ARCH_WANT_DEFAULT_BPF_JIT
@@ -101,4 +105,17 @@ config BPF_LSM
If you are unsure how to answer this question, answer N.
+config BPF_JIT_KASAN
+ bool
+ depends on HAVE_EBPF_JIT_KASAN
+ depends on KASAN_GENERIC
+ depends on KASAN_VMALLOC
+ depends on BPF_JIT
+ default y if KASAN
+ help
+ Makes JIT compiler insert generic outline KASAN checks in BPF
+ programs when they are inserted in the kernel. This feature is
+ automatically enabled if the needed set of KASAN and BPF
+ configuration options is enabled.
+
endmenu # "BPF subsystem"
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 6e19a030da6f13..1e18b999f1b565 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -1598,7 +1598,7 @@ struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bp
continue;
if (env)
- tmp = bpf_patch_insn_data(env, i, insn_buff, rewritten);
+ tmp = bpf_patch_insn_data(env, i, insn_buff, rewritten, rewritten - 1);
else
tmp = bpf_patch_insn_single(clone, i, insn_buff, rewritten);
diff --git a/kernel/bpf/fixups.c b/kernel/bpf/fixups.c
index 12a8a4eb757f18..feee1f33de9d55 100644
--- a/kernel/bpf/fixups.c
+++ b/kernel/bpf/fixups.c
@@ -152,12 +152,25 @@ static int get_callee_stack_depth(struct bpf_verifier_env *env,
}
#endif
+static bool is_mem_insn(struct bpf_insn *insn)
+{
+ if (BPF_CLASS(insn->code) != BPF_ST &&
+ BPF_CLASS(insn->code) != BPF_STX &&
+ BPF_CLASS(insn->code) != BPF_LDX)
+ return false;
+
+ return (BPF_MODE(insn->code) == BPF_MEM ||
+ BPF_MODE(insn->code) == BPF_MEMSX ||
+ BPF_MODE(insn->code) == BPF_ATOMIC);
+}
+
/* single env->prog->insni[off] instruction was replaced with the range
* insni[off, off + cnt). Adjust corresponding insn_aux_data by copying
* [0, off) and [off, end) to new locations, so the patched range stays zero
*/
static void adjust_insn_aux_data(struct bpf_verifier_env *env,
- struct bpf_prog *new_prog, u32 off, u32 cnt)
+ struct bpf_prog *new_prog, u32 off, u32 cnt,
+ s32 insn_off_in_patch)
{
struct bpf_insn_aux_data *data = env->insn_aux_data;
struct bpf_insn *insn = new_prog->insnsi;
@@ -171,8 +184,14 @@ static void adjust_insn_aux_data(struct bpf_verifier_env *env,
*/
data[off].zext_dst = insn_has_def32(insn + off + cnt - 1);
- if (cnt == 1)
+ if (cnt == 1) {
+ /* A non-memory accessing insn could have been replaced by a
+ * memory accessing insn, systematically mark it for non-stack
+ * access
+ */
+ data[off].non_stack_access = is_mem_insn(insn);
return;
+ }
prog_len = new_prog->len;
memmove(data + off + cnt - 1, data + off,
@@ -182,7 +201,19 @@ static void adjust_insn_aux_data(struct bpf_verifier_env *env,
/* Expand insni[off]'s seen count to the patched range. */
data[i].seen = old_seen;
data[i].zext_dst = insn_has_def32(insn + i);
+ if (i == off + insn_off_in_patch) {
+ data[i].non_stack_access = data[off + cnt - 1].non_stack_access;
+ data[off + cnt - 1].non_stack_access = false;
+ } else if (is_mem_insn(insn + i)) {
+ data[i].non_stack_access = true;
+ }
}
+ /*
+ * Last slot instruction could be a newly generated
+ * BPF_ST/BPF_LDX/BPF_STX
+ */
+ if (is_mem_insn(insn + off + cnt - 1) && insn_off_in_patch != cnt - 1)
+ data[off + cnt - 1].non_stack_access = true;
/*
* The indirect_target flag of the original instruction was moved to the last of the
@@ -245,7 +276,8 @@ static void adjust_poke_descs(struct bpf_prog *prog, u32 off, u32 len)
}
struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
- const struct bpf_insn *patch, u32 len)
+ const struct bpf_insn *patch, u32 len,
+ s32 insn_off_in_patch)
{
struct bpf_prog *new_prog;
struct bpf_insn_aux_data *new_data = NULL;
@@ -269,7 +301,7 @@ struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
env->insn_aux_data[off].orig_idx);
return NULL;
}
- adjust_insn_aux_data(env, new_prog, off, len);
+ adjust_insn_aux_data(env, new_prog, off, len, insn_off_in_patch);
adjust_subprog_starts(env, off, len);
adjust_insn_arrays(env, off, len);
adjust_poke_descs(new_prog, off, len);
@@ -668,7 +700,7 @@ int bpf_opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,
patch = zext_patch;
patch_len = 2;
apply_patch_buffer:
- new_prog = bpf_patch_insn_data(env, adj_idx, patch, patch_len);
+ new_prog = bpf_patch_insn_data(env, adj_idx, patch, patch_len, 0);
if (!new_prog)
return -ENOMEM;
env->prog = new_prog;
@@ -713,7 +745,7 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)
insn_buf[cnt++] = BPF_STX_MEM(BPF_DW, BPF_REG_FP, BPF_REG_1,
-subprogs[0].stack_depth);
insn_buf[cnt++] = env->prog->insnsi[0];
- new_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt, 1);
if (!new_prog)
return -ENOMEM;
env->prog = new_prog;
@@ -736,7 +768,8 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)
verifier_bug(env, "prologue is too long");
return -EFAULT;
} else if (cnt) {
- new_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt,
+ cnt - 1);
if (!new_prog)
return -ENOMEM;
@@ -759,6 +792,7 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)
for (i = 0; i < insn_cnt; i++, insn++) {
bpf_convert_ctx_access_t convert_ctx_access;
+ s32 insn_off_in_patch = -1;
u8 mode;
if (env->insn_aux_data[i + delta].nospec) {
@@ -768,7 +802,8 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)
*patch++ = BPF_ST_NOSPEC();
*patch++ = *insn;
cnt = patch - insn_buf;
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, cnt - 1);
if (!new_prog)
return -ENOMEM;
@@ -841,7 +876,8 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)
*patch++ = *insn;
*patch++ = BPF_ST_NOSPEC();
cnt = patch - insn_buf;
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, 0);
if (!new_prog)
return -ENOMEM;
@@ -856,16 +892,20 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)
if (!ops->convert_ctx_access)
continue;
convert_ctx_access = ops->convert_ctx_access;
+ insn_off_in_patch = 0;
break;
case PTR_TO_SOCKET:
case PTR_TO_SOCK_COMMON:
convert_ctx_access = bpf_sock_convert_ctx_access;
+ insn_off_in_patch = 0;
break;
case PTR_TO_TCP_SOCK:
convert_ctx_access = bpf_tcp_sock_convert_ctx_access;
+ insn_off_in_patch = 0;
break;
case PTR_TO_XDP_SOCK:
convert_ctx_access = bpf_xdp_sock_convert_ctx_access;
+ insn_off_in_patch = 0;
break;
case PTR_TO_BTF_ID:
case PTR_TO_BTF_ID | PTR_UNTRUSTED:
@@ -971,7 +1011,8 @@ int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)
size * 8, 0);
patch_insn_buf:
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt,
+ insn_off_in_patch);
if (!new_prog)
return -ENOMEM;
@@ -1464,7 +1505,7 @@ static int add_hidden_subprog(struct bpf_verifier_env *env, struct bpf_insn *pat
* ones for the hidden subprog. Hence all of the adjustment operations
* in bpf_patch_insn_data are no-ops.
*/
- prog = bpf_patch_insn_data(env, env->prog->len - 1, patch, len);
+ prog = bpf_patch_insn_data(env, env->prog->len - 1, patch, len, -1);
if (!prog)
return -ENOMEM;
env->prog = prog;
@@ -1549,7 +1590,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
cnt = patch - insn_buf;
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, 0);
if (!new_prog)
return -ENOMEM;
@@ -1569,6 +1611,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
bool is_sdiv = isdiv && insn->off == 1;
bool is_smod = !isdiv && insn->off == 1;
struct bpf_insn *patch = insn_buf;
+ s32 insn_off_in_patch;
if (is_sdiv) {
/* [R,W]x sdiv 0 -> 0
@@ -1595,6 +1638,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
*patch++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);
*patch++ = *insn;
cnt = patch - insn_buf;
+ insn_off_in_patch = cnt - 1;
} else if (is_smod) {
/* [R,W]x mod 0 -> [R,W]x */
/* [R,W]x mod -1 -> 0 */
@@ -1611,6 +1655,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
*patch++ = BPF_MOV32_IMM(insn->dst_reg, 0);
*patch++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);
*patch++ = *insn;
+ insn_off_in_patch = patch - insn_buf - 1;
if (!is64) {
*patch++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);
@@ -1626,12 +1671,14 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
*patch++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);
*patch++ = *insn;
cnt = patch - insn_buf;
+ insn_off_in_patch = cnt - 1;
} else {
/* [R,W]x mod 0 -> [R,W]x */
*patch++ = BPF_RAW_INSN((is64 ? BPF_JMP : BPF_JMP32) |
BPF_JEQ | BPF_K, insn->src_reg,
0, 1 + (is64 ? 0 : 1), 0);
*patch++ = *insn;
+ insn_off_in_patch = patch - insn_buf - 1;
if (!is64) {
*patch++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);
@@ -1640,7 +1687,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
cnt = patch - insn_buf;
}
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, insn_off_in_patch);
if (!new_prog)
return -ENOMEM;
@@ -1656,6 +1704,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
BPF_MODE(insn->code) == BPF_PROBE_MEMSX)) {
struct bpf_insn *patch = insn_buf;
u64 uaddress_limit = bpf_arch_uaddress_limit();
+ s32 insn_off_in_patch;
if (!uaddress_limit)
goto next_insn;
@@ -1666,11 +1715,13 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
*patch++ = BPF_ALU64_IMM(BPF_RSH, BPF_REG_AX, 32);
*patch++ = BPF_JMP_IMM(BPF_JLE, BPF_REG_AX, uaddress_limit >> 32, 2);
*patch++ = *insn;
+ insn_off_in_patch = patch - insn_buf - 1;
*patch++ = BPF_JMP_IMM(BPF_JA, 0, 0, 1);
*patch++ = BPF_MOV64_IMM(insn->dst_reg, 0);
cnt = patch - insn_buf;
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, insn_off_in_patch);
if (!new_prog)
return -ENOMEM;
@@ -1690,7 +1741,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
return -EFAULT;
}
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, 0);
if (!new_prog)
return -ENOMEM;
@@ -1743,7 +1795,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
*patch++ = BPF_ALU64_IMM(BPF_MUL, off_reg, -1);
cnt = patch - insn_buf;
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, -1);
if (!new_prog)
return -ENOMEM;
@@ -1788,7 +1841,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
insn_buf[6] = BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_AX, stack_off_cnt);
cnt = 7;
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, 3);
if (!new_prog)
return -ENOMEM;
@@ -1809,7 +1863,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
insn_buf[3] = BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_AX, stack_off);
cnt = 4;
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, 2);
if (!new_prog)
return -ENOMEM;
@@ -1830,7 +1885,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
if (cnt == 0)
goto next_insn;
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, cnt - 1);
if (!new_prog)
return -ENOMEM;
@@ -1915,7 +1971,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
map)->index_mask);
insn_buf[2] = *insn;
cnt = 3;
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, cnt - 1);
if (!new_prog)
return -ENOMEM;
@@ -1948,7 +2005,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
insn_buf[2] = *insn;
cnt = 3;
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, cnt - 1);
if (!new_prog)
return -ENOMEM;
@@ -1967,7 +2025,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
insn_buf[1] = *insn;
cnt = 2;
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, cnt - 1);
if (!new_prog)
return -ENOMEM;
@@ -2007,8 +2066,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
return -EFAULT;
}
- new_prog = bpf_patch_insn_data(env, i + delta,
- insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(
+ env, i + delta, insn_buf, cnt, -1);
if (!new_prog)
return -ENOMEM;
@@ -2091,7 +2150,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
cnt = 3;
new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
- cnt);
+ cnt, -1);
if (!new_prog)
return -ENOMEM;
@@ -2119,7 +2178,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
insn_buf[0] = BPF_ALU32_REG(BPF_XOR, BPF_REG_0, BPF_REG_0);
cnt = 1;
#endif
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, -1);
if (!new_prog)
return -ENOMEM;
@@ -2137,7 +2197,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
insn_buf[2] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0);
cnt = 3;
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, -1);
if (!new_prog)
return -ENOMEM;
@@ -2171,7 +2232,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
insn_buf[cnt++] = BPF_JMP_A(1);
insn_buf[cnt++] = BPF_MOV64_IMM(BPF_REG_0, -EINVAL);
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, -1);
if (!new_prog)
return -ENOMEM;
@@ -2203,7 +2265,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
cnt = 1;
}
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, -1);
if (!new_prog)
return -ENOMEM;
@@ -2229,7 +2292,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
cnt = 2;
}
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, -1);
if (!new_prog)
return -ENOMEM;
@@ -2245,7 +2309,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
/* Load IP address from ctx - 16 */
insn_buf[0] = BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, -16);
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, 1);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ 1, -1);
if (!new_prog)
return -ENOMEM;
@@ -2300,7 +2365,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
insn_buf[10] = BPF_MOV64_IMM(BPF_REG_0, -ENOENT);
cnt = 11;
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, -1);
if (!new_prog)
return -ENOMEM;
@@ -2318,7 +2384,8 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
insn_buf[1] = BPF_ATOMIC_OP(BPF_DW, BPF_XCHG, BPF_REG_1, BPF_REG_0, 0);
cnt = 2;
- new_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, i + delta, insn_buf,
+ cnt, -1);
if (!new_prog)
return -ENOMEM;
@@ -2386,7 +2453,7 @@ int bpf_do_misc_fixups(struct bpf_verifier_env *env)
/* Copy first actual insn to preserve it */
insn_buf[cnt++] = env->prog->insnsi[subprog_start];
- new_prog = bpf_patch_insn_data(env, subprog_start, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, subprog_start, insn_buf, cnt, cnt - 1);
if (!new_prog)
return -ENOMEM;
env->prog = prog = new_prog;
@@ -2485,7 +2552,7 @@ static struct bpf_prog *inline_bpf_loop(struct bpf_verifier_env *env,
insn_buf[cnt++] = BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_10, r8_offset);
*total_cnt = cnt;
- new_prog = bpf_patch_insn_data(env, position, insn_buf, cnt);
+ new_prog = bpf_patch_insn_data(env, position, insn_buf, cnt, -1);
if (!new_prog)
return new_prog;
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 51f7965d42e324..1246ce8f5f0f46 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -3144,6 +3144,11 @@ static void mark_indirect_target(struct bpf_verifier_env *env, int idx)
env->insn_aux_data[idx].indirect_target = true;
}
+static void mark_non_stack_access(struct bpf_verifier_env *env, int idx)
+{
+ env->insn_aux_data[idx].non_stack_access = true;
+}
+
#define LR_FRAMENO_BITS 4
#define LR_SPI_BITS 6
#define LR_ENTRY_BITS (LR_SPI_BITS + LR_FRAMENO_BITS + 1)
@@ -6333,6 +6338,10 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b
else
coerce_reg_to_size_sx(®s[value_regno], size);
}
+
+ if (!err && reg->type != PTR_TO_STACK)
+ mark_non_stack_access(env, insn_idx);
+
return err;
}
diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_insn_array.c b/tools/testing/selftests/bpf/prog_tests/bpf_insn_array.c
index 0222a9a5d07614..37c405fc2b0d23 100644
--- a/tools/testing/selftests/bpf/prog_tests/bpf_insn_array.c
+++ b/tools/testing/selftests/bpf/prog_tests/bpf_insn_array.c
@@ -227,42 +227,6 @@ static void check_incorrect_index(void)
check_mid_insn_index();
}
-static int set_bpf_jit_harden(char *level)
-{
- char old_level;
- int err = -1;
- int fd = -1;
-
- fd = open("/proc/sys/net/core/bpf_jit_harden", O_RDWR | O_NONBLOCK);
- if (fd < 0) {
- ASSERT_FAIL("open .../bpf_jit_harden returned %d (errno=%d)", fd, errno);
- return -1;
- }
-
- err = read(fd, &old_level, 1);
- if (err != 1) {
- ASSERT_FAIL("read from .../bpf_jit_harden returned %d (errno=%d)", err, errno);
- err = -1;
- goto end;
- }
-
- lseek(fd, 0, SEEK_SET);
-
- err = write(fd, level, 1);
- if (err != 1) {
- ASSERT_FAIL("write to .../bpf_jit_harden returned %d (errno=%d)", err, errno);
- err = -1;
- goto end;
- }
-
- err = 0;
- *level = old_level;
-end:
- if (fd >= 0)
- close(fd);
- return err;
-}
-
static void check_blindness(void)
{
struct bpf_insn insns[] = {
@@ -272,7 +236,7 @@ static void check_blindness(void)
BPF_MOV64_IMM(BPF_REG_0, 1),
BPF_EXIT_INSN(),
};
- int prog_fd = -1, map_fd;
+ int prog_fd = -1, map_fd, ret;
struct bpf_insn_array_value val = {};
char bpf_jit_harden = '@'; /* non-exizsting value */
int i;
@@ -291,7 +255,8 @@ static void check_blindness(void)
goto cleanup;
bpf_jit_harden = '2';
- if (set_bpf_jit_harden(&bpf_jit_harden)) {
+ ret = set_bpf_jit_harden(&bpf_jit_harden);
+ if (!ASSERT_OK(ret, "set bpf_jit_harden")) {
bpf_jit_harden = '@'; /* open, read or write failed => no write was done */
goto cleanup;
}
diff --git a/tools/testing/selftests/bpf/prog_tests/kasan.c b/tools/testing/selftests/bpf/prog_tests/kasan.c
new file mode 100644
index 00000000000000..f006b92ee131ac
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/kasan.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+/*
+ * Tests validating that KASAN reports are properly instrumented and
+ * generated on a wide variety of instructions. The running kernel needs
+ * kasan_multi_shot to run multiple kasan-generating subtests at once
+ */
+#include <bpf/bpf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/if_ether.h>
+#include <unistd.h>
+#include <test_progs.h>
+#include <unpriv_helpers.h>
+#include "kasan.skel.h"
+#include "kasan_harden.skel.h"
+
+#define SUBTEST_NAME_MAX_LEN 128
+#define PROG_NAME_MAX_LEN 128
+
+#define MAX_LOG_SIZE (8 * 1024)
+#define READ_CHUNK_SIZE 256
+
+#define KASAN_PATTERN_SLAB_UAF "BUG: KASAN: slab-use-after-free " \
+ "in bpf_prog_%02x%02x%02x%02x%02x%02x%02x%02x_%s"
+#define KASAN_PATTERN_REPORT "%s of size %d at addr"
+
+static char klog_buffer[MAX_LOG_SIZE];
+static char record[MAX_LOG_SIZE];
+
+struct test_spec {
+ char *prog_type;
+ bool is_write;
+ bool only_32_or_64;
+ bool needs_load_acq_store_rel;
+ bool skip_multi_size_testing;
+ bool skip_on_stack_testing;
+ int run_size;
+ bool expect_no_report;
+ bool rnd_hi32;
+};
+
+struct kasan_write_val {
+ __u8 data_1;
+ __u16 data_2;
+ __u32 data_4;
+ __u64 data_8;
+};
+
+struct test_ctx {
+ __u8 prog_tag[BPF_TAG_SIZE];
+ struct bpf_object *obj;
+ int *access_size;
+ bool skip_load_acq_store_rel;
+ struct bpf_program *prog;
+ char prog_name[SUBTEST_NAME_MAX_LEN];
+ int klog_fd;
+};
+
+static int open_kernel_logs(void)
+{
+ int fd;
+
+ fd = open("/dev/kmsg", O_RDONLY | O_NONBLOCK);
+
+ return fd;
+}
+
+static void skip_kernel_logs(int fd)
+{
+ lseek(fd, 0, SEEK_END);
+}
+
+static int read_kernel_logs(int fd, char *buf, size_t max_len)
+{
+ size_t total = 0;
+ ssize_t n;
+
+ buf[0] = '\0';
+ while (1) {
+ char *msg, *eol;
+ size_t len;
+
+ n = read(fd, record, sizeof(record) - 1);
+ if (n < 0) {
+ if (errno == EAGAIN)
+ break;
+ return n;
+ }
+ record[n] = '\0';
+
+ /*
+ * Each kmsg record starts with some metadata, separated
+ * from the actual content by a semi-colon
+ */
+ msg = strchr(record, ';');
+ if (!msg)
+ continue;
+ msg++;
+ eol = strchr(msg, '\n');
+ if (eol)
+ *eol = '\0';
+
+ len = strlen(msg);
+ if (total + len + 2 > max_len)
+ break;
+ memcpy(buf + total, msg, len);
+ total += len;
+ buf[total++] = '\n';
+ buf[total] = '\0';
+ }
+
+ return total;
+}
+
+static int check_kasan_report_in_kernel_logs(char *buf, struct test_ctx *ctx,
+ bool is_write, int size)
+{
+ char access_log[READ_CHUNK_SIZE];
+ char *kasan_report_start;
+ int nsize;
+
+ snprintf(access_log, READ_CHUNK_SIZE, KASAN_PATTERN_SLAB_UAF,
+ ctx->prog_tag[0], ctx->prog_tag[1], ctx->prog_tag[2],
+ ctx->prog_tag[3], ctx->prog_tag[4], ctx->prog_tag[5],
+ ctx->prog_tag[6], ctx->prog_tag[7], ctx->prog_name);
+ /*
+ * Searched kasan report is valid if
+ * - it contains the expected kasan pattern
+ * - the description of the faulty access is found somewhere
+ * after the header (not necessarily on the very next line,
+ * because other kernel messages may interleave)
+ * - faulty access properties match the tested type and size
+ */
+ kasan_report_start = strstr(buf, access_log);
+
+ if (!kasan_report_start)
+ return 1;
+
+ nsize = snprintf(access_log, READ_CHUNK_SIZE, KASAN_PATTERN_REPORT,
+ is_write ? "Write" : "Read", size);
+
+ if (!strstr(kasan_report_start, access_log))
+ return 1;
+
+ return 0;
+}
+
+static void exec_subtest(struct test_ctx *ctx, struct test_spec *test,
+ int access_size, bool on_stack)
+{
+ LIBBPF_OPTS(bpf_test_run_opts, topts);
+ struct bpf_prog_info info;
+ uint8_t buf[ETH_HLEN] = {0};
+ int ret, prog_fd;
+ __u32 info_len;
+
+ ctx->prog = bpf_object__find_program_by_name(ctx->obj,
+ ctx->prog_name);
+ if (!ASSERT_OK_PTR(ctx->prog, "find test prog"))
+ return;
+
+ info_len = sizeof(info);
+ memset(&info, 0, info_len);
+ prog_fd = bpf_program__fd(ctx->prog);
+ if (!ASSERT_OK_FD(prog_fd, "get prog fd"))
+ return;
+ ret = bpf_prog_get_info_by_fd(prog_fd, &info, &info_len);
+ if (!ASSERT_OK(ret, "fetch loaded program info"))
+ return;
+ memcpy(ctx->prog_tag, info.tag, BPF_TAG_SIZE);
+
+ skip_kernel_logs(ctx->klog_fd);
+
+ topts.sz = sizeof(struct bpf_test_run_opts);
+ topts.data_size_in = ETH_HLEN;
+ topts.data_in = buf;
+ if (ctx->access_size)
+ *ctx->access_size = access_size;
+ ret = bpf_prog_test_run_opts(bpf_program__fd(ctx->prog),
+ &topts);
+ if (!ASSERT_OK(ret, "run prog"))
+ return;
+
+ ret = read_kernel_logs(ctx->klog_fd, klog_buffer, MAX_LOG_SIZE);
+ if (!ASSERT_GE(ret, 0, "read kernel logs"))
+ return;
+
+ ret = check_kasan_report_in_kernel_logs(klog_buffer, ctx,
+ test->is_write, access_size);
+ if (on_stack || test->expect_no_report)
+ ASSERT_NEQ(ret, 0, "no report should be generated");
+ else
+ ASSERT_OK(ret, "report should be generated");
+}
+
+static void run_subtest_with_size_and_location(struct test_ctx *ctx,
+ struct test_spec *test,
+ int access_size,
+ bool on_stack)
+{
+ char subtest_name[SUBTEST_NAME_MAX_LEN];
+
+ if (test->skip_multi_size_testing) {
+ snprintf(subtest_name, SUBTEST_NAME_MAX_LEN, "%s",
+ test->prog_type);
+ strncpy(ctx->prog_name, test->prog_type, PROG_NAME_MAX_LEN);
+ } else {
+ snprintf(subtest_name, SUBTEST_NAME_MAX_LEN, "%s_%d_%s",
+ test->prog_type, access_size,
+ on_stack ? "on_stack" : "not_on_stack");
+ snprintf(ctx->prog_name, PROG_NAME_MAX_LEN, "%s_%s",
+ test->prog_type,
+ on_stack ? "on_stack" : "not_on_stack");
+ }
+
+ if (!test__start_subtest(subtest_name))
+ return;
+
+ if (test->needs_load_acq_store_rel && ctx->skip_load_acq_store_rel) {
+ test__skip();
+ return;
+ }
+
+ exec_subtest(ctx, test, access_size, on_stack);
+}
+
+static void run_subtest_with_size(struct test_ctx *ctx, struct test_spec *test,
+ int size)
+{
+ run_subtest_with_size_and_location(ctx, test, size, false);
+ if (!test->skip_on_stack_testing)
+ run_subtest_with_size_and_location(ctx, test, size, true);
+}
+
+static void run_subtest(struct test_ctx *ctx, struct test_spec *test)
+{
+ if (test->skip_multi_size_testing) {
+ run_subtest_with_size(ctx, test, test->run_size);
+ return;
+ }
+
+ if (!test->only_32_or_64) {
+ run_subtest_with_size(ctx, test, 1);
+ run_subtest_with_size(ctx, test, 2);
+ }
+ run_subtest_with_size(ctx, test, 4);
+ run_subtest_with_size(ctx, test, 8);
+}
+
+static void run_blinding_subtest(void)
+{
+ struct test_spec blinding_spec = {
+ .prog_type = "st_blinded",
+ .is_write = true,
+ };
+ char bpf_jit_harden = '2';
+ struct kasan_harden *skel;
+ struct test_ctx *ctx;
+
+ if (!test__start_subtest("st_blinded"))
+ return;
+
+ ctx = calloc(1, sizeof(*ctx));
+ if (!ASSERT_OK_PTR(ctx, "alloc blinding ctx"))
+ return;
+ ctx->klog_fd = -1;
+
+ if (set_bpf_jit_harden(&bpf_jit_harden))
+ goto free_ctx;
+
+ skel = kasan_harden__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open and load blinded prog"))
+ goto restore;
+
+ ctx->klog_fd = open_kernel_logs();
+ if (!ASSERT_OK_FD(ctx->klog_fd, "open kernel logs"))
+ goto destroy;
+
+ ctx->obj = skel->obj;
+ strncpy(ctx->prog_name, "st_blinded", PROG_NAME_MAX_LEN);
+
+ exec_subtest(ctx, &blinding_spec, 1, false);
+
+destroy:
+ close(ctx->klog_fd);
+ kasan_harden__destroy(skel);
+restore:
+ set_bpf_jit_harden(&bpf_jit_harden);
+free_ctx:
+ free(ctx);
+}
+
+static struct test_spec tests[] = {
+ {
+ .prog_type = "st",
+ .is_write = true
+ },
+ {
+ .prog_type = "stx",
+ .is_write = true
+ },
+ {
+ .prog_type = "ldx",
+ .is_write = false
+ },
+ {
+ .prog_type = "simple_atomic",
+ .is_write = false,
+ .only_32_or_64 = true
+ },
+ {
+ .prog_type = "load_acquire",
+ .is_write = false,
+ .needs_load_acq_store_rel = true
+ },
+ {
+ .prog_type = "store_release",
+ .is_write = true,
+ .needs_load_acq_store_rel = true
+ },
+ {
+ .prog_type = "ldx_patched",
+ .is_write = false,
+ .skip_multi_size_testing = true,
+ .skip_on_stack_testing = true,
+ .run_size = 4,
+ .rnd_hi32 = true
+ },
+ {
+ .prog_type = "ldx_patched_on_stack",
+ .is_write = false,
+ .skip_multi_size_testing = true,
+ .skip_on_stack_testing = true,
+ .run_size = 4,
+ .expect_no_report = true,
+ .rnd_hi32 = true
+ },
+ {
+ .prog_type = "verifier_paths_stack_and_non_stack",
+ .is_write = true,
+ .skip_multi_size_testing = true,
+ .skip_on_stack_testing = true,
+ .run_size = 1
+ }
+};
+
+void test_kasan(void)
+{
+ struct kasan_write_val val;
+ struct test_spec *test;
+ struct test_ctx *ctx;
+ struct kasan *skel;
+ __u32 key = 0;
+ int i, ret;
+
+ ctx = calloc(1, sizeof(struct test_ctx));
+ if (!ASSERT_OK_PTR(ctx, "alloc test ctx"))
+ return;
+
+ if (!is_jit_enabled() || !get_kasan_jit_enabled()) {
+ test__skip();
+ goto end;
+ }
+
+ skel = kasan__open();
+ if (!ASSERT_OK_PTR(skel, "open prog"))
+ goto end;
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ struct bpf_program *prog;
+
+ if (!tests[i].rnd_hi32)
+ continue;
+
+ prog = bpf_object__find_program_by_name(skel->obj,
+ tests[i].prog_type);
+ if (!ASSERT_OK_PTR(prog, "find rnd_hi32 prog"))
+ goto destroy;
+ bpf_program__set_flags(prog, BPF_F_TEST_RND_HI32);
+ }
+
+ if (!ASSERT_OK(kasan__load(skel), "load prog"))
+ goto destroy;
+
+ ctx->obj = skel->obj;
+ ctx->access_size = &skel->bss->access_size;
+ ctx->skip_load_acq_store_rel = skel->data->skip_load_acq_store_rel_tests;
+
+ ctx->klog_fd = open_kernel_logs();
+ if (!ASSERT_OK_FD(ctx->klog_fd, "open kernel logs"))
+ goto destroy;
+
+ /* Fill map with recognizable values */
+ ret = bpf_map__lookup_elem(skel->maps.test_map, &key, sizeof(key),
+ &val, sizeof(val), 0);
+ if (!ASSERT_OK(ret, "get map"))
+ goto close;
+ val.data_1 = 0xAA;
+ val.data_2 = 0xBBBB;
+ val.data_4 = 0xCCCCCCCC;
+ val.data_8 = 0xDDDDDDDDDDDDDDDD;
+ ret = bpf_map__update_elem(skel->maps.test_map, &key, sizeof(key),
+ &val, sizeof(val), 0);
+ if (!ASSERT_OK(ret, "set map"))
+ goto close;
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ test = &tests[i];
+ run_subtest(ctx, test);
+ }
+
+ /*
+ * Blinding subtest is handled differently as it needs the
+ * corresponding program to be loaded with bpf_jit_harden raised
+ */
+ run_blinding_subtest();
+
+close:
+ close(ctx->klog_fd);
+destroy:
+ kasan__destroy(skel);
+end:
+ free(ctx);
+}
diff --git a/tools/testing/selftests/bpf/progs/kasan.c b/tools/testing/selftests/bpf/progs/kasan.c
new file mode 100644
index 00000000000000..82ddce3059aee1
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/kasan.c
@@ -0,0 +1,394 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include "bpf_misc.h"
+#include <stdbool.h>
+
+extern void bpf_kfunc_kasan_poison(void *mem, __u32 mem__sz) __ksym;
+extern void bpf_kfunc_kasan_unpoison(void *mem, __u32 mem__sz) __ksym;
+
+int access_size;
+
+struct kasan_write_val {
+ __u8 data_1;
+ __u16 data_2;
+ __u32 data_4;
+ __u64 data_8;
+};
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, struct kasan_write_val);
+} test_map SEC(".maps");
+
+SEC("tcx/ingress")
+int st_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_write_val val;
+
+ bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_write_val));
+ switch (access_size) {
+ case 1:
+ val.data_1 = 0xAA;
+ break;
+ case 2:
+ val.data_2 = 0xAA;
+ break;
+ case 4:
+ val.data_4 = 0xAA;
+ break;
+ case 8:
+ val.data_8 = 0xAA;
+ break;
+ }
+ bpf_kfunc_kasan_unpoison(&val, sizeof(struct kasan_write_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int st_not_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_write_val *val;
+ __u32 key = 0;
+
+ val = bpf_map_lookup_elem(&test_map, &key);
+ if (!val)
+ return 0;
+
+ bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
+ switch (access_size) {
+ case 1:
+ val->data_1 = 0xAA;
+ break;
+ case 2:
+ val->data_2 = 0xAA;
+ break;
+ case 4:
+ val->data_4 = 0xAA;
+ break;
+ case 8:
+ val->data_8 = 0xAA;
+ break;
+ }
+ bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int stx_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_write_val val;
+
+ bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_write_val));
+ switch (access_size) {
+ case 1:
+ val.data_1 = access_size;
+ break;
+ case 2:
+ val.data_2 = access_size;
+ break;
+ case 4:
+ val.data_4 = access_size;
+ break;
+ case 8:
+ val.data_8 = access_size;
+ break;
+ }
+ bpf_kfunc_kasan_unpoison(&val, sizeof(struct kasan_write_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int stx_not_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_write_val *val;
+ __u32 key = 0;
+
+ val = bpf_map_lookup_elem(&test_map, &key);
+ if (!val)
+ return 0;
+
+ bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
+ switch (access_size) {
+ case 1:
+ val->data_1 = access_size;
+ break;
+ case 2:
+ val->data_2 = access_size;
+ break;
+ case 4:
+ val->data_4 = access_size;
+ break;
+ case 8:
+ val->data_8 = access_size;
+ break;
+ }
+ bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int ldx_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_write_val val;
+
+ bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_write_val));
+ switch (access_size) {
+ case 1:
+ __sink(val.data_1);
+ break;
+ case 2:
+ __sink(val.data_2);
+ break;
+ case 4:
+ __sink(val.data_4);
+ break;
+ case 8:
+ __sink(val.data_8);
+ break;
+ }
+ bpf_kfunc_kasan_unpoison(&val, sizeof(struct kasan_write_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int ldx_not_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_write_val *val;
+ __u32 key = 0;
+
+ val = bpf_map_lookup_elem(&test_map, &key);
+ if (!val)
+ return 0;
+
+ bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
+ switch (access_size) {
+ case 1:
+ __sink(val->data_1);
+ break;
+ case 2:
+ __sink(val->data_2);
+ break;
+ case 4:
+ __sink(val->data_4);
+ break;
+ case 8:
+ __sink(val->data_8);
+ break;
+ }
+ bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int ldx_patched(struct __sk_buff *skb)
+{
+ struct kasan_write_val *val;
+ __u32 key = 0;
+
+ val = bpf_map_lookup_elem(&test_map, &key);
+ if (!val)
+ return 0;
+
+ bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
+ __sink(val->data_4);
+ bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));
+
+ return 0;
+}
+
+SEC("tcx/ingress")
+int ldx_patched_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_write_val val;
+
+ bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_write_val));
+ __sink(val.data_4);
+ bpf_kfunc_kasan_unpoison(&val, sizeof(struct kasan_write_val));
+
+ return 0;
+}
+
+SEC("tcx/ingress")
+int simple_atomic_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_write_val val;
+
+ bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_write_val));
+ switch (access_size) {
+ case 4:
+ __sync_fetch_and_add(&val.data_4, 4);
+ break;
+ case 8:
+ __sync_fetch_and_add(&val.data_8, 8);
+ break;
+ }
+ bpf_kfunc_kasan_unpoison(&val, sizeof(struct kasan_write_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int simple_atomic_not_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_write_val *val;
+ __u32 key = 0;
+
+ val = bpf_map_lookup_elem(&test_map, &key);
+ if (!val)
+ return 0;
+
+ bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
+ switch (access_size) {
+ case 4:
+ __sync_fetch_and_add(&val->data_4, 4);
+ break;
+ case 8:
+ __sync_fetch_and_add(&val->data_8, 8);
+ break;
+ }
+ bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));
+ return 0;
+}
+
+#ifdef __BPF_FEATURE_LOAD_ACQ_STORE_REL
+bool skip_load_acq_store_rel_tests SEC(".data") = 0;
+
+SEC("tcx/ingress")
+int load_acquire_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_write_val val;
+
+ bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_write_val));
+ switch (access_size) {
+ case 1:
+ __atomic_load_n(&val.data_1, __ATOMIC_ACQUIRE);
+ break;
+ case 2:
+ __atomic_load_n(&val.data_2, __ATOMIC_ACQUIRE);
+ break;
+ case 4:
+ __atomic_load_n(&val.data_4, __ATOMIC_ACQUIRE);
+ break;
+ case 8:
+ __atomic_load_n(&val.data_8, __ATOMIC_ACQUIRE);
+ break;
+ }
+ bpf_kfunc_kasan_unpoison(&val, sizeof(struct kasan_write_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int load_acquire_not_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_write_val *val;
+ __u32 key = 0;
+
+ val = bpf_map_lookup_elem(&test_map, &key);
+ if (!val)
+ return 0;
+
+ bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
+ switch (access_size) {
+ case 1:
+ __atomic_load_n(&val->data_1, __ATOMIC_ACQUIRE);
+ break;
+ case 2:
+ __atomic_load_n(&val->data_2, __ATOMIC_ACQUIRE);
+ break;
+ case 4:
+ __atomic_load_n(&val->data_4, __ATOMIC_ACQUIRE);
+ break;
+ case 8:
+ __atomic_load_n(&val->data_8, __ATOMIC_ACQUIRE);
+ break;
+ }
+ bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int store_release_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_write_val val;
+
+ bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_write_val));
+ switch (access_size) {
+ case 1:
+ __atomic_store_n(&val.data_1, 0xAA, __ATOMIC_RELEASE);
+ break;
+ case 2:
+ __atomic_store_n(&val.data_2, 0xBBBB, __ATOMIC_RELEASE);
+ break;
+ case 4:
+ __atomic_store_n(&val.data_4, 0xCCCCCCCC, __ATOMIC_RELEASE);
+ break;
+ case 8:
+ __atomic_store_n(&val.data_8, 0xDDDDDDDDDDDDDDDD,
+ __ATOMIC_RELEASE);
+ break;
+ }
+ bpf_kfunc_kasan_unpoison(&val, sizeof(struct kasan_write_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int store_release_not_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_write_val *val;
+ __u32 key = 0;
+
+ val = bpf_map_lookup_elem(&test_map, &key);
+ if (!val)
+ return 0;
+
+ bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
+ switch (access_size) {
+ case 1:
+ __atomic_store_n(&val->data_1, 0xAA, __ATOMIC_RELEASE);
+ break;
+ case 2:
+ __atomic_store_n(&val->data_2, 0xBBBB, __ATOMIC_RELEASE);
+ break;
+ case 4:
+ __atomic_store_n(&val->data_4, 0xCCCCCCCC, __ATOMIC_RELEASE);
+ break;
+ case 8:
+ __atomic_store_n(&val->data_8, 0xDDDDDDDDDDDDDDDD,
+ __ATOMIC_RELEASE);
+ break;
+ }
+ bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));
+ return 0;
+}
+#else
+bool skip_load_acq_store_rel_tests SEC(".data") = 1;
+#endif
+
+SEC("tcx/ingress")
+int verifier_paths_stack_and_non_stack(struct __sk_buff *skb)
+{
+ struct kasan_write_val stack_val = {};
+ struct kasan_write_val *val;
+ void *ptr;
+ __u32 key = 0;
+
+ val = bpf_map_lookup_elem(&test_map, &key);
+ if (!val)
+ return 0;
+
+ if (access_size)
+ ptr = val;
+ else
+ ptr = &stack_val;
+
+ bpf_kfunc_kasan_poison(val, sizeof(*val));
+ *(__u8 *)ptr = 0xAA;
+ bpf_kfunc_kasan_unpoison(val, sizeof(*val));
+ return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/kasan_harden.c b/tools/testing/selftests/bpf/progs/kasan_harden.c
new file mode 100644
index 00000000000000..808d7da6442162
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/kasan_harden.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+extern void bpf_kfunc_kasan_poison(void *mem, __u32 mem__sz) __ksym;
+extern void bpf_kfunc_kasan_unpoison(void *mem, __u32 mem__sz) __ksym;
+
+struct kasan_write_val {
+ __u8 data_1;
+ __u16 data_2;
+ __u32 data_4;
+ __u64 data_8;
+};
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, struct kasan_write_val);
+} test_map SEC(".maps");
+
+SEC("tcx/ingress")
+int st_blinded(struct __sk_buff *skb)
+{
+ struct kasan_write_val *val;
+ __u32 key = 0;
+
+ val = bpf_map_lookup_elem(&test_map, &key);
+ if (!val)
+ return 0;
+
+ bpf_kfunc_kasan_poison(val, sizeof(struct kasan_write_val));
+ val->data_1 = 0xAA;
+ bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_write_val));
+
+ return 0;
+}
+
+char LICENSE[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index 30f1cd23093cbe..5d6a834f7d7575 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -271,6 +271,26 @@ __bpf_kfunc void bpf_kfunc_put_default_trusted_ptr_test(struct prog_test_member
*/
}
+#ifdef CONFIG_BPF_JIT_KASAN
+
+extern void kasan_poison(const void *addr, size_t size, u8 value, bool init);
+
+#define KASAN_SLAB_FREE 0xFB
+
+__bpf_kfunc void bpf_kfunc_kasan_poison(void *mem, u32 mem__sz)
+{
+ kasan_poison(mem, mem__sz, KASAN_SLAB_FREE, false);
+}
+
+__bpf_kfunc void bpf_kfunc_kasan_unpoison(void *mem, u32 mem__sz)
+{
+ kasan_poison(mem, mem__sz, 0x00, false);
+}
+#else
+__bpf_kfunc void bpf_kfunc_kasan_poison(void *mem, u32 mem__sz) { }
+__bpf_kfunc void bpf_kfunc_kasan_unpoison(void *mem, u32 mem__sz) { }
+#endif
+
__bpf_kfunc struct bpf_testmod_ctx *
bpf_testmod_ctx_create(int *err)
{
@@ -740,6 +760,8 @@ BTF_ID_FLAGS(func, bpf_testmod_ops3_call_test_1)
BTF_ID_FLAGS(func, bpf_testmod_ops3_call_test_2)
BTF_ID_FLAGS(func, bpf_kfunc_get_default_trusted_ptr_test);
BTF_ID_FLAGS(func, bpf_kfunc_put_default_trusted_ptr_test);
+BTF_ID_FLAGS(func, bpf_kfunc_kasan_poison)
+BTF_ID_FLAGS(func, bpf_kfunc_kasan_unpoison)
BTF_KFUNCS_END(bpf_testmod_common_kfunc_ids)
BTF_ID_LIST(bpf_testmod_dtor_ids)
diff --git a/tools/testing/selftests/bpf/testing_helpers.c b/tools/testing/selftests/bpf/testing_helpers.c
index c970e7793dfcbc..737f668b35e23b 100644
--- a/tools/testing/selftests/bpf/testing_helpers.c
+++ b/tools/testing/selftests/bpf/testing_helpers.c
@@ -519,6 +519,38 @@ bool is_jit_enabled(void)
return enabled;
}
+int set_bpf_jit_harden(char *level)
+{
+ char old_level;
+ int err = -1;
+ int fd = -1;
+
+ fd = open("/proc/sys/net/core/bpf_jit_harden", O_RDWR | O_NONBLOCK);
+ if (fd < 0)
+ return -1;
+
+ err = read(fd, &old_level, 1);
+ if (err != 1) {
+ err = -1;
+ goto end;
+ }
+
+ lseek(fd, 0, SEEK_SET);
+
+ err = write(fd, level, 1);
+ if (err != 1) {
+ err = -1;
+ goto end;
+ }
+
+ err = 0;
+ *level = old_level;
+end:
+ if (fd >= 0)
+ close(fd);
+ return err;
+}
+
int stack_mprotect(void)
{
void *buf;
diff --git a/tools/testing/selftests/bpf/testing_helpers.h b/tools/testing/selftests/bpf/testing_helpers.h
index 2edc6fb7fc5212..e00642afe86f40 100644
--- a/tools/testing/selftests/bpf/testing_helpers.h
+++ b/tools/testing/selftests/bpf/testing_helpers.h
@@ -59,6 +59,7 @@ struct bpf_insn;
int get_xlated_program(int fd_prog, struct bpf_insn **buf, __u32 *cnt);
int testing_prog_flags(void);
bool is_jit_enabled(void);
+int set_bpf_jit_harden(char *level);
int stack_mprotect(void);
#endif /* __TESTING_HELPERS_H */
diff --git a/tools/testing/selftests/bpf/unpriv_helpers.c b/tools/testing/selftests/bpf/unpriv_helpers.c
index f997d7ec8fd086..11201b65a3d4b5 100644
--- a/tools/testing/selftests/bpf/unpriv_helpers.c
+++ b/tools/testing/selftests/bpf/unpriv_helpers.c
@@ -142,3 +142,8 @@ bool get_unpriv_disabled(void)
}
return mitigations_off;
}
+
+bool get_kasan_jit_enabled(void)
+{
+ return config_contains("CONFIG_BPF_JIT_KASAN=y") == 1;
+}
diff --git a/tools/testing/selftests/bpf/unpriv_helpers.h b/tools/testing/selftests/bpf/unpriv_helpers.h
index 151f6732966522..bc5f4c953c9d6f 100644
--- a/tools/testing/selftests/bpf/unpriv_helpers.h
+++ b/tools/testing/selftests/bpf/unpriv_helpers.h
@@ -5,3 +5,4 @@
#define UNPRIV_SYSCTL "kernel/unprivileged_bpf_disabled"
bool get_unpriv_disabled(void);
+bool get_kasan_jit_enabled(void);
|
| 3/2 | 2026/07/08 20:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 28377 output: 44 thoughts: 1036 Thoughts: **Analyzing the KASAN Support for BPF JIT Compiler** Okay, so we have this patch series introducing KASAN support for the BPF JIT compiler on x86_64. It looks pretty straightforward – a new `CONFIG_BPF_JIT_KASAN` and `CONFIG_HAVE_EBPF_JIT_KASAN`, some modifications to `arch/x86/net/bpf_jit_comp.c` for emitting KASAN checks during JIT compilation, and some adjustments in `kernel/bpf/fixups.c` and `kernel/bpf/verifier.c` to track memory accesses. We also have new tests in `tools/testing/selftests/bpf/prog_tests/kasan.c` and `tools/testing/selftests/bpf/progs/kasan.c`, which is good to see. This is definitely a functional change to a core part of the kernel – the BPF JIT compiler. It's reachable and testable, which is a strong indicator. So, based on that, the first thing is to mark `WorthFuzzing` as `true`. Now, we need to identify the relevant configurations. We have the new `BPF_JIT_KASAN`, and the existing `HAVE_EBPF_JIT_KASAN`, but we can't forget `KASAN_GENERIC`, `KASAN_VMALLOC`, `BPF_JIT` and the base `KASAN`. Next, we need to pinpoint the key functions. In `arch/x86/net/bpf_jit_comp.c`, we have `emit_kasan_check`, `emit_st`, `emit_atomic_ld_st`, and `do_jit`. In `kernel/bpf/fixups.c`, we see `adjust_insn_aux_data`, `bpf_patch_insn_data`, `bpf_do_misc_fixups`, and `bpf_convert_ctx_accesses`. Finally, in `kernel/bpf/verifier.c` we have `mark_non_stack_access` and `check_mem_access`. Let's quickly check which of these can be directly leveraged. Looking at the code, it looks like `emit_kasan_check`, `emit_st`, `emit_atomic_ld_st`, and `do_jit` are static within `arch/x86/net/bpf_jit_comp.c`. So, those are out. But `bpf_patch_insn_data`, `bpf_do_misc_fixups`, `bpf_convert_ctx_accesses`, and `check_mem_access` in `kernel/bpf/*` seem promising. Also, we have `bpf_int_jit_compile`, which calls `do_jit` which, while static, is core to the JIT compilation process. Wait, is `bpf_patch_insn_data` exported? Let me quickly check... yes, it is! Excellent. So, let's use `bpf_patch_insn_data`, `bpf_do_misc_fixups`, and `bpf_int_jit_compile`. Alright, I should call `set-results` now. We will set `WorthFuzzing` to `true`. Our `FocusSymbols` will be `["bpf_patch_insn_data", "bpf_do_misc_fixups", "bpf_int_jit_compile"]`. Our `EnableConfigs` will be `["BPF_JIT_KASAN", "KASAN_GENERIC", "KASAN_VMALLOC", "BPF_JIT", "KASAN"]`. Finally, the `Reasoning` will be: "The patch adds KASAN support to the BPF JIT compiler on x86_64, which is a functional change to core kernel logic." Hmm, now that I think about it, `KASAN_GENERIC` and `KASAN_VMALLOC` are usually enabled in KASAN builds. Let me check if only `BPF_JIT_KASAN` is enough. Yes, `BPF_JIT_KASAN` depends on `HAVE_EBPF_JIT_KASAN`, `KASAN_GENERIC`, `KASAN_VMALLOC`, and `BPF_JIT`. Okay, let me just double-check that `bpf_int_jit_compile` and everything looks correct. Yep, all good |
| 4/2 | 2026/07/08 20:14 | tool | grepper |
0mArgs:{
"Expression": "bpf_int_jit_compile",
"PathPrefix": "arch/x86/net/bpf_jit_comp.c"
}Results: {
"Output": "arch/x86/net/bpf_jit_comp.c=4002=struct x64_jit_data {\n--\narch/x86/net/bpf_jit_comp.c-4013-\narch/x86/net/bpf_jit_comp.c:4014:struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)\narch/x86/net/bpf_jit_comp.c-4015-{\n"
} |
| 5/2 | 2026/07/08 20:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 30630 output: 38 thoughts: 42 Thoughts: Here's my take on the thought process, as requested: **JIT Compilation - Entry Point and Patching** Alright, so `bpf_int_jit_compile` is the key entry point for the Just-In-Time compiler on x86, got it. That's where the magic begins. Now, my next logical step is to dive into how the instructions are actually handled. Looks like I should take a look at `bpf_patch_insn_data` next. I need to understand how the data within instructions gets modified or altered during the compilation and patching process. Knowing how it's handled is essential to optimizing the generated code, right? |
| 6/2 | 2026/07/08 20:14 | tool | grepper |
0mArgs:{
"Expression": "bpf_patch_insn_data",
"PathPrefix": "kernel/bpf/fixups.c"
}Results: {
"Output": "kernel/bpf/fixups.c=264=static void adjust_poke_descs(struct bpf_prog *prog, u32 off, u32 len)\n--\nkernel/bpf/fixups.c-277-\nkernel/bpf/fixups.c:278:struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,\nkernel/bpf/fixups.c-279-\t\t\t\t const struct bpf_insn *patch, u32 len,\n--\nkernel/bpf/fixups.c=614=int bpf_opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,\n--\nkernel/bpf/fixups.c-702-apply_patch_buffer:\nkernel/bpf/fixups.c:703:\t\tnew_prog = bpf_patch_insn_data(env, adj_idx, patch, patch_len, 0);\nkernel/bpf/fixups.c-704-\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c=720=int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)\n--\nkernel/bpf/fixups.c-747-\t\t\tinsn_buf[cnt++] = env-\u003eprog-\u003einsnsi[0];\nkernel/bpf/fixups.c:748:\t\t\tnew_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt, 1);\nkernel/bpf/fixups.c-749-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-770-\t\t} else if (cnt) {\nkernel/bpf/fixups.c:771:\t\t\tnew_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt,\nkernel/bpf/fixups.c-772-\t\t\t\t\t\t cnt - 1);\n--\nkernel/bpf/fixups.c-804-\t\t\tcnt = patch - insn_buf;\nkernel/bpf/fixups.c:805:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-806-\t\t\t\t\t\t cnt, cnt - 1);\n--\nkernel/bpf/fixups.c-878-\t\t\tcnt = patch - insn_buf;\nkernel/bpf/fixups.c:879:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-880-\t\t\t\t\t\t cnt, 0);\n--\nkernel/bpf/fixups.c-1013-patch_insn_buf:\nkernel/bpf/fixups.c:1014:\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt,\nkernel/bpf/fixups.c-1015-\t\t\t\t\t insn_off_in_patch);\n--\nkernel/bpf/fixups.c=1493=static int add_hidden_subprog(struct bpf_verifier_env *env, struct bpf_insn *patch, int len)\n--\nkernel/bpf/fixups.c-1505-\t * ones for the hidden subprog. Hence all of the adjustment operations\nkernel/bpf/fixups.c:1506:\t * in bpf_patch_insn_data are no-ops.\nkernel/bpf/fixups.c-1507-\t */\nkernel/bpf/fixups.c:1508:\tprog = bpf_patch_insn_data(env, env-\u003eprog-\u003elen - 1, patch, len, -1);\nkernel/bpf/fixups.c-1509-\tif (!prog)\n--\nkernel/bpf/fixups.c=1522=int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n--\nkernel/bpf/fixups.c-1592-\nkernel/bpf/fixups.c:1593:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1594-\t\t\t\t\t\t cnt, 0);\n--\nkernel/bpf/fixups.c-1689-\nkernel/bpf/fixups.c:1690:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1691-\t\t\t\t\t\t cnt, insn_off_in_patch);\n--\nkernel/bpf/fixups.c-1722-\t\t\tcnt = patch - insn_buf;\nkernel/bpf/fixups.c:1723:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1724-\t\t\t\t\t\t cnt, insn_off_in_patch);\n--\nkernel/bpf/fixups.c-1743-\nkernel/bpf/fixups.c:1744:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1745-\t\t\t\t\t\t cnt, 0);\n--\nkernel/bpf/fixups.c-1797-\nkernel/bpf/fixups.c:1798:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1799-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-1843-\nkernel/bpf/fixups.c:1844:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1845-\t\t\t\t\t\t cnt, 3);\n--\nkernel/bpf/fixups.c-1865-\nkernel/bpf/fixups.c:1866:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1867-\t\t\t\t\t\t cnt, 2);\n--\nkernel/bpf/fixups.c-1887-\nkernel/bpf/fixups.c:1888:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1889-\t\t\t\t\t\t cnt, cnt - 1);\n--\nkernel/bpf/fixups.c-1973-\t\t\tcnt = 3;\nkernel/bpf/fixups.c:1974:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1975-\t\t\t\t\t\t cnt, cnt - 1);\n--\nkernel/bpf/fixups.c-2007-\nkernel/bpf/fixups.c:2008:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2009-\t\t\t\t\t\t cnt, cnt - 1);\n--\nkernel/bpf/fixups.c-2027-\nkernel/bpf/fixups.c:2028:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2029-\t\t\t\t\t\t cnt, cnt - 1);\n--\nkernel/bpf/fixups.c-2068-\nkernel/bpf/fixups.c:2069:\t\t\t\tnew_prog = bpf_patch_insn_data(\nkernel/bpf/fixups.c-2070-\t\t\t\t\tenv, i + delta, insn_buf, cnt, -1);\n--\nkernel/bpf/fixups.c-2151-\nkernel/bpf/fixups.c:2152:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2153-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-2180-#endif\nkernel/bpf/fixups.c:2181:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2182-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-2199-\nkernel/bpf/fixups.c:2200:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2201-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-2234-\nkernel/bpf/fixups.c:2235:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2236-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-2267-\nkernel/bpf/fixups.c:2268:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2269-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-2294-\nkernel/bpf/fixups.c:2295:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2296-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-2311-\nkernel/bpf/fixups.c:2312:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2313-\t\t\t\t\t\t 1, -1);\n--\nkernel/bpf/fixups.c-2367-\nkernel/bpf/fixups.c:2368:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2369-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-2386-\nkernel/bpf/fixups.c:2387:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2388-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-2455-\nkernel/bpf/fixups.c:2456:\t\tnew_prog = bpf_patch_insn_data(env, subprog_start, insn_buf, cnt, cnt - 1);\nkernel/bpf/fixups.c-2457-\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2463-\t\t * to insn after BPF_ST that inits may_goto count.\nkernel/bpf/fixups.c:2464:\t\t * Adjustment will succeed because bpf_patch_insn_data() didn't fail.\nkernel/bpf/fixups.c-2465-\t\t */\n--\nkernel/bpf/fixups.c=2493=static struct bpf_prog *inline_bpf_loop(struct bpf_verifier_env *env,\n--\nkernel/bpf/fixups.c-2554-\t*total_cnt = cnt;\nkernel/bpf/fixups.c:2555:\tnew_prog = bpf_patch_insn_data(env, position, insn_buf, cnt, -1);\nkernel/bpf/fixups.c-2556-\tif (!new_prog)\n"
} |
| 7/2 | 2026/07/08 20:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 33986 output: 36 thoughts: 266 Thoughts: **My BPF Fuzzing Strategy** Okay, so I'm thinking about how to best approach fuzzing the BPF subsystem. I see that `bpf_patch_insn_data` is defined and exported in `include/linux/filter.h`. I was considering focusing on it, along with `bpf_do_misc_fixups` and `bpf_int_jit_compile`. Hang on, `bpf_patch_insn_data` isn't actually exported as a module symbol, it's just a global function. It could still be fuzzed if syzkaller can reach it somehow, but ideally, I want to target the BPF verifier or the JIT compilation process directly. Therefore, I think a much better set of focus symbols are: * `bpf_int_jit_compile` (the JIT entry point) * `bpf_patch_insn_data` * `bpf_do_misc_fixups` (the fixup entry point) * `bpf_convert_ctx_accesses` Let's quickly check `bpf_patch_insn_data`, and then I will focus on `bpf_int_jit_compile`. |
| 8/2 | 2026/07/08 20:14 | tool | grepper |
0mArgs:{
"Expression": "bpf_int_jit_compile",
"PathPrefix": "kernel/bpf/core.c"
}Results: {
"Output": "kernel/bpf/core.c=2620=static struct bpf_prog *bpf_prog_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)\n--\nkernel/bpf/core.c-2626-\tif (!bpf_prog_need_blind(prog))\nkernel/bpf/core.c:2627:\t\treturn bpf_int_jit_compile(env, prog);\nkernel/bpf/core.c-2628-\n--\nkernel/bpf/core.c-2648-\nkernel/bpf/core.c:2649:\tprog = bpf_int_jit_compile(env, prog);\nkernel/bpf/core.c-2650-\tif (prog-\u003ejited) {\n--\nkernel/bpf/core.c=2667=struct bpf_prog *__bpf_prog_select_runtime(struct bpf_verifier_env *env, struct bpf_prog *fp,\n--\nkernel/bpf/core.c-2686-\t * blinding is active. However, in case of error during\nkernel/bpf/core.c:2687:\t * blinding, bpf_int_jit_compile() must always return a\nkernel/bpf/core.c-2688-\t * valid program, which in this case would simply not\n--\nkernel/bpf/core.c=3209=const struct bpf_func_proto bpf_tail_call_proto = {\n--\nkernel/bpf/core.c-3221-/* Stub for JITs that only support cBPF. eBPF programs are interpreted.\nkernel/bpf/core.c:3222: * It is encouraged to implement bpf_int_jit_compile() instead, so that\nkernel/bpf/core.c-3223- * eBPF and implicitly also cBPF can get JITed!\nkernel/bpf/core.c-3224- */\nkernel/bpf/core.c:3225:struct bpf_prog * __weak bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)\nkernel/bpf/core.c-3226-{\n--\nkernel/bpf/core.c-3230-/* Stub for JITs that support eBPF. All cBPF code gets transformed into\nkernel/bpf/core.c:3231: * eBPF by the kernel and is later compiled by bpf_int_jit_compile().\nkernel/bpf/core.c-3232- */\n"
} |
| 9/2 | 2026/07/08 20:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 35218 output: 34 thoughts: 34 Thoughts: Here's my take on the thought process: **Focusing on BPF JIT Compilation** Alright, it looks like `bpf_int_jit_compile` is the place to start drilling down. That's a solid symbol to anchor on. Now, my next step is to examine what's going on within `bpf_do_misc_fixups`. I'll want to see how this function interacts with the compilation process. This will give me a better understanding of how the compiler manages those "miscellaneous" aspects. |
| 10/2 | 2026/07/08 20:14 | tool | grepper |
0mArgs:{
"Expression": "bpf_do_misc_fixups",
"PathPrefix": "kernel/bpf/"
}Results: {
"Output": "kernel/bpf/fixups.c=1493=static int add_hidden_subprog(struct bpf_verifier_env *env, struct bpf_insn *patch, int len)\n--\nkernel/bpf/fixups.c-1521- */\nkernel/bpf/fixups.c:1522:int bpf_do_misc_fixups(struct bpf_verifier_env *env)\nkernel/bpf/fixups.c-1523-{\n--\nkernel/bpf/verifier.c=19739=int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\n--\nkernel/bpf/verifier.c-19933-\tif (ret == 0)\nkernel/bpf/verifier.c:19934:\t\tret = bpf_do_misc_fixups(env);\nkernel/bpf/verifier.c-19935-\n"
} |
| 11/2 | 2026/07/08 20:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 35653 output: 34 thoughts: 36 Thoughts: Okay, here's my take on those thoughts: **Analyzing BPF Instruction Patching** Alright, it looks like `bpf_do_misc_fixups` is another potential key focus point for this. I'll need to drill down and see how it fits in with the broader context. Now, let's turn our attention to `bpf_patch_insn_data`. Understanding how that routine works is crucial. I need to figure out its role in the process, its dependencies, and how it interacts with the rest of the BPF system. From there I'll be able to trace it back up the stack. |
| 12/2 | 2026/07/08 20:14 | tool | grepper |
0mArgs:{
"Expression": "bpf_patch_insn_data",
"PathPrefix": "kernel/bpf/"
}Results: {
"Output": "kernel/bpf/core.c=1554=struct bpf_prog *bpf_jit_blind_constants(struct bpf_verifier_env *env, struct bpf_prog *prog)\n--\nkernel/bpf/core.c-1568-\nkernel/bpf/core.c:1569:\t/* make sure bpf_patch_insn_data() patches the correct prog */\nkernel/bpf/core.c-1570-\tif (env)\n--\nkernel/bpf/core.c-1600-\t\tif (env)\nkernel/bpf/core.c:1601:\t\t\ttmp = bpf_patch_insn_data(env, i, insn_buff, rewritten, rewritten - 1);\nkernel/bpf/core.c-1602-\t\telse\n--\nkernel/bpf/fixups.c=264=static void adjust_poke_descs(struct bpf_prog *prog, u32 off, u32 len)\n--\nkernel/bpf/fixups.c-277-\nkernel/bpf/fixups.c:278:struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,\nkernel/bpf/fixups.c-279-\t\t\t\t const struct bpf_insn *patch, u32 len,\n--\nkernel/bpf/fixups.c=614=int bpf_opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,\n--\nkernel/bpf/fixups.c-702-apply_patch_buffer:\nkernel/bpf/fixups.c:703:\t\tnew_prog = bpf_patch_insn_data(env, adj_idx, patch, patch_len, 0);\nkernel/bpf/fixups.c-704-\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c=720=int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)\n--\nkernel/bpf/fixups.c-747-\t\t\tinsn_buf[cnt++] = env-\u003eprog-\u003einsnsi[0];\nkernel/bpf/fixups.c:748:\t\t\tnew_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt, 1);\nkernel/bpf/fixups.c-749-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-770-\t\t} else if (cnt) {\nkernel/bpf/fixups.c:771:\t\t\tnew_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt,\nkernel/bpf/fixups.c-772-\t\t\t\t\t\t cnt - 1);\n--\nkernel/bpf/fixups.c-804-\t\t\tcnt = patch - insn_buf;\nkernel/bpf/fixups.c:805:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-806-\t\t\t\t\t\t cnt, cnt - 1);\n--\nkernel/bpf/fixups.c-878-\t\t\tcnt = patch - insn_buf;\nkernel/bpf/fixups.c:879:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-880-\t\t\t\t\t\t cnt, 0);\n--\nkernel/bpf/fixups.c-1013-patch_insn_buf:\nkernel/bpf/fixups.c:1014:\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt,\nkernel/bpf/fixups.c-1015-\t\t\t\t\t insn_off_in_patch);\n--\nkernel/bpf/fixups.c=1493=static int add_hidden_subprog(struct bpf_verifier_env *env, struct bpf_insn *patch, int len)\n--\nkernel/bpf/fixups.c-1505-\t * ones for the hidden subprog. Hence all of the adjustment operations\nkernel/bpf/fixups.c:1506:\t * in bpf_patch_insn_data are no-ops.\nkernel/bpf/fixups.c-1507-\t */\nkernel/bpf/fixups.c:1508:\tprog = bpf_patch_insn_data(env, env-\u003eprog-\u003elen - 1, patch, len, -1);\nkernel/bpf/fixups.c-1509-\tif (!prog)\n--\nkernel/bpf/fixups.c=1522=int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n--\nkernel/bpf/fixups.c-1592-\nkernel/bpf/fixups.c:1593:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1594-\t\t\t\t\t\t cnt, 0);\n--\nkernel/bpf/fixups.c-1689-\nkernel/bpf/fixups.c:1690:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1691-\t\t\t\t\t\t cnt, insn_off_in_patch);\n--\nkernel/bpf/fixups.c-1722-\t\t\tcnt = patch - insn_buf;\nkernel/bpf/fixups.c:1723:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1724-\t\t\t\t\t\t cnt, insn_off_in_patch);\n--\nkernel/bpf/fixups.c-1743-\nkernel/bpf/fixups.c:1744:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1745-\t\t\t\t\t\t cnt, 0);\n--\nkernel/bpf/fixups.c-1797-\nkernel/bpf/fixups.c:1798:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1799-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-1843-\nkernel/bpf/fixups.c:1844:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1845-\t\t\t\t\t\t cnt, 3);\n--\nkernel/bpf/fixups.c-1865-\nkernel/bpf/fixups.c:1866:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1867-\t\t\t\t\t\t cnt, 2);\n--\nkernel/bpf/fixups.c-1887-\nkernel/bpf/fixups.c:1888:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1889-\t\t\t\t\t\t cnt, cnt - 1);\n--\nkernel/bpf/fixups.c-1973-\t\t\tcnt = 3;\nkernel/bpf/fixups.c:1974:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-1975-\t\t\t\t\t\t cnt, cnt - 1);\n--\nkernel/bpf/fixups.c-2007-\nkernel/bpf/fixups.c:2008:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2009-\t\t\t\t\t\t cnt, cnt - 1);\n--\nkernel/bpf/fixups.c-2027-\nkernel/bpf/fixups.c:2028:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2029-\t\t\t\t\t\t cnt, cnt - 1);\n--\nkernel/bpf/fixups.c-2068-\nkernel/bpf/fixups.c:2069:\t\t\t\tnew_prog = bpf_patch_insn_data(\nkernel/bpf/fixups.c-2070-\t\t\t\t\tenv, i + delta, insn_buf, cnt, -1);\n--\nkernel/bpf/fixups.c-2151-\nkernel/bpf/fixups.c:2152:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2153-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-2180-#endif\nkernel/bpf/fixups.c:2181:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2182-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-2199-\nkernel/bpf/fixups.c:2200:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2201-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-2234-\nkernel/bpf/fixups.c:2235:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2236-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-2267-\nkernel/bpf/fixups.c:2268:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2269-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-2294-\nkernel/bpf/fixups.c:2295:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2296-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-2311-\nkernel/bpf/fixups.c:2312:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2313-\t\t\t\t\t\t 1, -1);\n--\nkernel/bpf/fixups.c-2367-\nkernel/bpf/fixups.c:2368:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2369-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-2386-\nkernel/bpf/fixups.c:2387:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2388-\t\t\t\t\t\t cnt, -1);\n--\nkernel/bpf/fixups.c-2455-\nkernel/bpf/fixups.c:2456:\t\tnew_prog = bpf_patch_insn_data(env, subprog_start, insn_buf, cnt, cnt - 1);\nkernel/bpf/fixups.c-2457-\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2463-\t\t * to insn after BPF_ST that inits may_goto count.\nkernel/bpf/fixups.c:2464:\t\t * Adjustment will succeed because bpf_patch_insn_data() didn't fail.\nkernel/bpf/fixups.c-2465-\t\t */\n--\nkernel/bpf/fixups.c=2493=static struct bpf_prog *inline_bpf_loop(struct bpf_verifier_env *env,\n--\nkernel/bpf/fixups.c-2554-\t*total_cnt = cnt;\nkernel/bpf/fixups.c:2555:\tnew_prog = bpf_patch_insn_data(env, position, insn_buf, cnt, -1);\nkernel/bpf/fixups.c-2556-\tif (!new_prog)\n"
} |
| 13/2 | 2026/07/08 20:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 39196 output: 34 thoughts: 179 Thoughts: **My Approach to Analyzing BPF Compilation** Alright, *bpf\_patch\_insn\_data* is clearly a frequently-used function in this space; that jumps out immediately. I need to focus my efforts, though, and I think the best path forward involves looking closely at the compilation pipeline – specifically, *bpf\_int\_jit\_compile*, *bpf\_do\_misc\_fixups*, and *bpf\_convert\_ctx\_accesses*. They seem like good entry points for more in-depth fuzzing. I quickly checked the source for *bpf\_patch\_insn\_data* to confirm my intuition and remind myself how it all fits together. Yes, it's defined in `kernel/bpf/fixups.c` and exported in `include/linux/filter.h`. It is not exported to modules which suggests it is internal. The concern is that Syzkaller can still reach it through direct specification; however, it is better to target the more general entry points of the BPF subsystem. Therefore, my focus for initial analysis will be *bpf\_int\_jit\_compile*, *bpf\_do\_misc\_fixups*, and *bpf\_convert\_ctx\_accesses*. My first target, let's explore *bpf\_convert\_ctx\_accesses*. |
| 14/2 | 2026/07/08 20:14 | tool | grepper |
0mArgs:{
"Expression": "bpf_convert_ctx_accesses",
"PathPrefix": "kernel/bpf/"
}Results: {
"Output": "kernel/bpf/fixups.c=614=int bpf_opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,\n--\nkernel/bpf/fixups.c-719- */\nkernel/bpf/fixups.c:720:int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)\nkernel/bpf/fixups.c-721-{\n--\nkernel/bpf/verifier.c=19739=int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\n--\nkernel/bpf/verifier.c-19930-\t\t/* program is valid, convert *(u32*)(ctx + off) accesses */\nkernel/bpf/verifier.c:19931:\t\tret = bpf_convert_ctx_accesses(env);\nkernel/bpf/verifier.c-19932-\n"
} |
| 15/2 | 2026/07/08 20:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 39949 output: 30 thoughts: 32 Thoughts: Here's my take on the thought process: **Focusing on Memory Access in BPF** Alright, I'm thinking that digging into `bpf_convert_ctx_accesses` might be another productive avenue to explore. It seems like it's likely involved in memory access translations within the BPF context. Now, let's switch gears and go directly to `check_mem_access`. That function probably has the critical checks needed to enforce memory safety within the BPF program. I'll take a look at the logic there. |
| 16/2 | 2026/07/08 20:14 | tool | grepper |
0mArgs:{
"Expression": "check_mem_access",
"PathPrefix": "kernel/bpf/"
}Results: {
"Output": "kernel/bpf/verifier.c=55=static bool bpf_global_percpu_ma_set;\n--\nkernel/bpf/verifier.c-102- * can be: PTR_TO_MAP_VALUE, PTR_TO_CTX, PTR_TO_STACK, PTR_TO_SOCKET. These are\nkernel/bpf/verifier.c:103: * four pointer types recognized by check_mem_access() function.\nkernel/bpf/verifier.c-104- *\n--\nkernel/bpf/verifier.c=847=static bool is_dynptr_reg_valid_uninit(struct bpf_verifier_env *env, struct bpf_reg_state *reg)\n--\nkernel/bpf/verifier.c-857-\t * error because this just means the stack state hasn't been updated yet.\nkernel/bpf/verifier.c:858:\t * We will do check_mem_access to check and update stack bounds later.\nkernel/bpf/verifier.c-859-\t */\n--\nkernel/bpf/verifier.c=1003=static bool is_iter_reg_valid_uninit(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-1009-\t/* For -ERANGE (i.e. spi not falling into allocated stack slots), we\nkernel/bpf/verifier.c:1010:\t * will do check_mem_access to check and update stack bounds later, so\nkernel/bpf/verifier.c-1011-\t * return true for that case.\n--\nkernel/bpf/verifier.c=1148=static bool is_irq_flag_reg_valid_uninit(struct bpf_verifier_env *env, struct bpf_reg_state *reg)\n--\nkernel/bpf/verifier.c-1154-\t/* For -ERANGE (i.e. spi not falling into allocated stack slots), we\nkernel/bpf/verifier.c:1155:\t * will do check_mem_access to check and update stack bounds later, so\nkernel/bpf/verifier.c-1156-\t * return true for that case.\n--\nkernel/bpf/verifier.c=3418=static void scrub_special_slot(struct bpf_func_state *state, int spi)\n--\nkernel/bpf/verifier.c-3430-/* check_stack_{read,write}_fixed_off functions track spill/fill of registers,\nkernel/bpf/verifier.c:3431: * stack boundary and alignment are checked in check_mem_access()\nkernel/bpf/verifier.c-3432- */\n--\nkernel/bpf/verifier.c=4150=static int check_map_access_type(struct bpf_verifier_env *env, struct bpf_reg_state *reg,\n--\nkernel/bpf/verifier.c-4171-/* check read/write into memory region (e.g., map value, ringbuf sample, etc) */\nkernel/bpf/verifier.c:4172:static int __check_mem_access(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,\nkernel/bpf/verifier.c-4173-\t\t\t int off, int size, u32 mem_size,\n--\nkernel/bpf/verifier.c=4210=static int check_mem_region_access(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,\n--\nkernel/bpf/verifier.c-4233-\t}\nkernel/bpf/verifier.c:4234:\terr = __check_mem_access(env, reg, argno, reg_smin(reg) + off, size,\nkernel/bpf/verifier.c-4235-\t\t\t\t mem_size, zero_size_allowed);\n--\nkernel/bpf/verifier.c-4250-\t}\nkernel/bpf/verifier.c:4251:\terr = __check_mem_access(env, reg, argno, reg_umax(reg) + off, size,\nkernel/bpf/verifier.c-4252-\t\t\t\t mem_size, zero_size_allowed);\n--\nkernel/bpf/verifier.c=4661=static int check_packet_access(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno, int off,\n--\nkernel/bpf/verifier.c-4674-\nkernel/bpf/verifier.c:4675:\t/* __check_mem_access has made sure \"off + size - 1\" is within u16.\nkernel/bpf/verifier.c-4676-\t * reg_umax(reg) can't be bigger than MAX_PACKET_OFF which is 0xffff,\nkernel/bpf/verifier.c-4677-\t * otherwise find_good_pkt_pointers would have refused to set range info\nkernel/bpf/verifier.c:4678:\t * that __check_mem_access would have rejected this pkt access.\nkernel/bpf/verifier.c-4679-\t * Therefore, \"off + reg_umax(reg) + size - 1\" won't overflow u32.\n--\nkernel/bpf/verifier.c=6061=static void add_scalar_to_reg(struct bpf_reg_state *dst_reg, s64 val)\n--\nkernel/bpf/verifier.c-6083- */\nkernel/bpf/verifier.c:6084:static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct bpf_reg_state *reg, argno_t argno,\nkernel/bpf/verifier.c-6085-\t\t\t int off, int bpf_size, enum bpf_access_type t,\n--\nkernel/bpf/verifier.c=6351=static int check_load_mem(struct bpf_verifier_env *env, struct bpf_insn *insn,\n--\nkernel/bpf/verifier.c-6383-\t */\nkernel/bpf/verifier.c:6384:\terr = check_mem_access(env, env-\u003einsn_idx, regs + insn-\u003esrc_reg, argno_from_reg(insn-\u003esrc_reg), insn-\u003eoff,\nkernel/bpf/verifier.c-6385-\t\t\t BPF_SIZE(insn-\u003ecode), BPF_READ, insn-\u003edst_reg,\n--\nkernel/bpf/verifier.c=6394=static int check_store_reg(struct bpf_verifier_env *env, struct bpf_insn *insn,\n--\nkernel/bpf/verifier.c-6423-\t/* Check if (dst_reg + off) is writeable. */\nkernel/bpf/verifier.c:6424:\terr = check_mem_access(env, env-\u003einsn_idx, regs + insn-\u003edst_reg, argno_from_reg(insn-\u003edst_reg), insn-\u003eoff,\nkernel/bpf/verifier.c-6425-\t\t\t BPF_SIZE(insn-\u003ecode), BPF_WRITE, insn-\u003esrc_reg,\n--\nkernel/bpf/verifier.c=6432=static int check_atomic_rmw(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-6501-\t */\nkernel/bpf/verifier.c:6502:\terr = check_mem_access(env, env-\u003einsn_idx, dst_reg, argno_from_reg(insn-\u003edst_reg), insn-\u003eoff,\nkernel/bpf/verifier.c-6503-\t\t\t BPF_SIZE(insn-\u003ecode), BPF_READ, -1, true, false);\nkernel/bpf/verifier.c-6504-\tif (!err \u0026\u0026 load_reg \u003e= 0)\nkernel/bpf/verifier.c:6505:\t\terr = check_mem_access(env, env-\u003einsn_idx, dst_reg, argno_from_reg(insn-\u003edst_reg),\nkernel/bpf/verifier.c-6506-\t\t\t\t insn-\u003eoff, BPF_SIZE(insn-\u003ecode),\n--\nkernel/bpf/verifier.c-6516-\t/* Check whether we can write into the same memory. */\nkernel/bpf/verifier.c:6517:\terr = check_mem_access(env, env-\u003einsn_idx, dst_reg, argno_from_reg(insn-\u003edst_reg), insn-\u003eoff,\nkernel/bpf/verifier.c-6518-\t\t\t BPF_SIZE(insn-\u003ecode), BPF_WRITE, -1, true, false);\n--\nkernel/bpf/verifier.c=7248=static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_state *reg,\n--\nkernel/bpf/verifier.c-7284-\t\tfor (i = 0; i \u003c BPF_DYNPTR_SIZE; i += 8) {\nkernel/bpf/verifier.c:7285:\t\t\terr = check_mem_access(env, insn_idx, reg, argno,\nkernel/bpf/verifier.c-7286-\t\t\t\t\t i, BPF_DW, BPF_WRITE, -1, false, false);\n--\nkernel/bpf/verifier.c=7368=static int process_iter_arg(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno, int insn_idx,\n--\nkernel/bpf/verifier.c-7405-\t\tfor (i = 0; i \u003c nr_slots * 8; i += BPF_REG_SIZE) {\nkernel/bpf/verifier.c:7406:\t\t\terr = check_mem_access(env, insn_idx, reg, argno,\nkernel/bpf/verifier.c-7407-\t\t\t\t\t i, BPF_DW, BPF_WRITE, -1, false, false);\n--\nkernel/bpf/verifier.c=10175=static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n--\nkernel/bpf/verifier.c-10261-\tfor (i = 0; i \u003c meta.access_size; i++) {\nkernel/bpf/verifier.c:10262:\t\terr = check_mem_access(env, insn_idx, regs + meta.regno, argno_from_reg(meta.regno), i, BPF_B,\nkernel/bpf/verifier.c-10263-\t\t\t\t BPF_WRITE, -1, false, false);\n--\nkernel/bpf/verifier.c-10509-\t\t\t\t * it will confuse the check of PTR_TO_BTF_ID in\nkernel/bpf/verifier.c:10510:\t\t\t\t * check_mem_access().\nkernel/bpf/verifier.c-10511-\t\t\t\t */\n--\nkernel/bpf/verifier.c=11438=static int process_irq_flag(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,\n--\nkernel/bpf/verifier.c-11465-\nkernel/bpf/verifier.c:11466:\t\terr = check_mem_access(env, env-\u003einsn_idx, reg, argno, 0, BPF_DW,\nkernel/bpf/verifier.c-11467-\t\t\t\t BPF_WRITE, -1, false, false);\n--\nkernel/bpf/verifier.c=17241=static int do_check_insn(struct bpf_verifier_env *env, bool *do_print_state)\n--\nkernel/bpf/verifier.c-17278-\nkernel/bpf/verifier.c:17279:\t\terr = check_mem_access(env, env-\u003einsn_idx, cur_regs(env) + insn-\u003edst_reg, argno_from_reg(insn-\u003edst_reg),\nkernel/bpf/verifier.c-17280-\t\t\t\t insn-\u003eoff, BPF_SIZE(insn-\u003ecode),\n"
} |
| 17/2 | 2026/07/08 20:14 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 43217 output: 104 thoughts: 275 Thoughts: **My Analysis of the Patch and Fuzzing Strategy** Okay, the function `check_mem_access` is definitely the key symbol to focus on here, that's a good find. Let's get this analysis down concisely. The patch's overall worthiness for fuzzing is definitely *true*. The important function symbols that are modified, according to my review of the changes, are `bpf_int_jit_compile`, `bpf_do_misc_fixups`, and *especially* `check_mem_access`. Now, the patch also introduces the configuration option `BPF_JIT_KASAN`, enabling KASAN support for the BPF JIT compiler on x86_64, which is undeniably a functional change within core kernel logic. It looks like it's a good change to fuzz. Ah, but hold on, the instructions say to avoid listing excessively generic configurations, as we probably have those enabled by default. So, that means I need to refine the config list. `KASAN_GENERIC`, `KASAN_VMALLOC`, `BPF_JIT`, and `KASAN` are all broad and likely already enabled, based on my knowledge of the kernel's build configurations. So, I need to focus *specifically* on those configurations that directly impact the modified code. Good, I see that the configuration introduced by this patch *is* `BPF_JIT_KASAN`. That appears to be sufficient. That's the new config introduced by the patch. Alright, that covers everything. Time to call `set-results`. |
| 18/2 | 2026/07/08 20:15 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"BPF_JIT_KASAN"
],
"FocusSymbols": [
"bpf_int_jit_compile",
"bpf_do_misc_fixups",
"check_mem_access"
],
"Reasoning": "The patch adds KASAN support to the BPF JIT compiler on x86_64, which is a functional change to core kernel logic.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"BPF_JIT_KASAN"
],
"FocusSymbols": [
"bpf_int_jit_compile",
"bpf_do_misc_fixups",
"check_mem_access"
],
"Reasoning": "The patch adds KASAN support to the BPF JIT compiler on x86_64, which is a functional change to core kernel logic.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|