| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/08/04 18:03 | flow | patch-triage |
1mArgs:null Results: {
"EnableConfigs": [
"BPF_JIT",
"KASAN_GENERIC",
"KASAN_VMALLOC"
],
"FocusSymbols": [
"bpf_int_jit_compile",
"bpf_patch_insn_data",
"bpf_check"
],
"Reasoning": "The patch adds KASAN instrumentation to the eBPF JIT compiler on x86_64. It modifies the JIT compiler to emit KASAN checks for memory accesses, and updates the BPF verifier and fixup logic to track whether instructions access non-stack memory. This is a functional change to core kernel logic (BPF JIT and verifier) that can be reached from user space by loading and executing BPF programs. Fuzzing is highly recommended to ensure the JIT emits correct KASAN checks and doesn't introduce regressions or crashes.",
"WorthFuzzing": true
} |
| 1/1 | 2026/08/04 18:03 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit d2070d895c40d50807e270b97c18771cbe784f7e\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Tue Aug 4 18:03:42 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/arch/x86/Kconfig b/arch/x86/Kconfig\nindex bdad90f210e4b..705a9ab85f3a9 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 01e7ce569c1ed..b7881b9953107 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,92 @@ 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)\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+\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+\t/*\n+\t * SystemV ABI states that we should also save r10/r11, but in\n+\t * practice those registers are _not_ used by the limited set of\n+\t * kasan helpers we are calling here, so that's fine not to save those.\n+\t */\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 +1412,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 +1578,35 @@ 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\tif (!accesses_stack_only) {\n+\t\t\terr = emit_kasan_check(env, pprog, src_reg, insn, ip,\n+\t\t\t\t\t false);\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\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\tif (!accesses_stack_only) {\n+\t\t\terr = emit_kasan_check(env, pprog, dst_reg, insn, ip,\n+\t\t\t\t\t true);\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\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 +1984,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 +2006,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 +2386,19 @@ 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\tif (!accesses_stack_only) {\n+\t\t\t\terr = emit_kasan_check(env, \u0026prog, dst_reg,\n+\t\t\t\t\t\t insn, ip, true);\n+\t\t\t\tif (err)\n+\t\t\t\t\treturn err;\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 \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 +2416,12 @@ 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\tif (!accesses_stack_only) {\n+\t\t\t\terr = emit_kasan_check(env, \u0026prog, dst_reg,\n+\t\t\t\t\t\t insn, ip, true);\n+\t\t\t\tif (err)\n+\t\t\t\t\treturn err;\n+\t\t\t}\n \t\t\temit_stx(\u0026prog, BPF_SIZE(insn-\u003ecode), dst_reg, src_reg, insn_off);\n \t\t\tbreak;\n \n@@ -2428,6 +2583,11 @@ 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 if (!accesses_stack_only) {\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\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,28 +2649,42 @@ 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\tu8 *pprog;\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 \t\t\t\t */\n \n \t\t\t\t/* Will need RAX as a CMPXCHG operand so save R0 */\n+\t\t\t\tpprog = prog;\n \t\t\t\temit_mov_reg(\u0026prog, true, BPF_REG_AX, BPF_REG_0);\n \t\t\t\tif (src_reg == BPF_REG_0)\n \t\t\t\t\treal_src_reg = BPF_REG_AX;\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 += (prog - pprog);\n+\t\t\t}\n+\t\t\tif (!bpf_atomic_is_load_store(insn)) {\n+\t\t\t\tif (!accesses_stack_only) {\n+\t\t\t\t\terr = emit_kasan_check(env, \u0026prog,\n+\t\t\t\t\t\t\t real_dst_reg,\n+\t\t\t\t\t\t\t insn, ip, true);\n+\t\t\t\t\tif (err)\n+\t\t\t\t\t\treturn err;\n+\t\t\t\t}\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 +2716,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 a2a40caca0a0e..6c1376b2a3c92 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/kernel/bpf/Kconfig b/kernel/bpf/Kconfig\nindex eb3de35734f09..d7d25477ef481 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\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/fixups.c b/kernel/bpf/fixups.c\nindex a0bddada7964e..6d69bf9db6ece 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 struct bpf_insn *original_insn)\n {\n \tstruct bpf_insn_aux_data *data = env-\u003einsn_aux_data;\n \tstruct bpf_insn *insn = new_prog-\u003einsnsi;\n@@ -171,8 +184,15 @@ 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/*\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 + off);\n \t\treturn;\n+\t}\n \tprog_len = new_prog-\u003elen;\n \n \tmemmove(data + off + cnt - 1, data + off,\n@@ -182,8 +202,25 @@ 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 (!memcmp(insn + i, original_insn, sizeof(struct bpf_insn))) {\n+\t\t\tdata[i].non_stack_access =\n+\t\t\t\tdata[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 \n+\t/*\n+\t * Last slot instruction could be a newly generated\n+\t * BPF_ST/BPF_LDX/BPF_STX, systematically mark it for non-stack access\n+\t * if it is not the original instruction, otherwise keep the\n+\t * original marking\n+\t */\n+\tif (is_mem_insn(insn + off + cnt - 1) \u0026\u0026\n+\t memcmp(insn + off + cnt - 1, original_insn, sizeof(struct bpf_insn)))\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 \t * new instructions by the above memmove and memset, but the indirect jump target is\n@@ -249,6 +286,7 @@ struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,\n {\n \tstruct bpf_prog *new_prog;\n \tstruct bpf_insn_aux_data *new_data = NULL;\n+\tstruct bpf_insn original_insn;\n \n \tif (len \u003e 1) {\n \t\tnew_data = vrealloc(env-\u003einsn_aux_data,\n@@ -261,6 +299,7 @@ struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,\n \t\tenv-\u003einsn_aux_data = new_data;\n \t}\n \n+\tmemcpy(\u0026original_insn, env-\u003eprog-\u003einsnsi + off, sizeof(struct bpf_insn));\n \tnew_prog = bpf_patch_insn_single(env-\u003eprog, off, patch, len);\n \tif (IS_ERR(new_prog)) {\n \t\tif (PTR_ERR(new_prog) == -ERANGE)\n@@ -269,7 +308,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, \u0026original_insn);\n \tadjust_subprog_starts(env, off, len);\n \tadjust_insn_arrays(env, off, len);\n \tadjust_poke_descs(new_prog, off, len);\ndiff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c\nindex 7439afdc851a7..3392d93c73f82 100644\n--- a/kernel/bpf/verifier.c\n+++ b/kernel/bpf/verifier.c\n@@ -3213,6 +3213,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@@ -6433,6 +6438,10 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b\n \t\t\t\tclear_scalar_id(\u0026regs[value_regno]);\n \t\t}\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 0222a9a5d0761..815f3e04540ff 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}\n@@ -313,7 +278,8 @@ static void check_blindness(void)\n cleanup:\n \t/* restore the old one */\n \tif (bpf_jit_harden != '@')\n-\t\tset_bpf_jit_harden(\u0026bpf_jit_harden);\n+\t\tASSERT_OK(set_bpf_jit_harden(\u0026bpf_jit_harden),\n+\t\t\t \"restore hardening configuration\");\n \n \tclose(prog_fd);\n \tclose(map_fd);\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 0000000000000..2b424767a0f3f\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/prog_tests/kasan.c\n@@ -0,0 +1,454 @@\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_SLAB_OOB \"BUG: KASAN: slab-out-of-bounds \" \\\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+\tbool is_oob;\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 == 0)\n+\t\t\tbreak;\n+\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+\t\t\t\t\t bool is_oob)\n+{\n+\tchar access_log[READ_CHUNK_SIZE];\n+\tconst char *pattern;\n+\tchar *kasan_report_start;\n+\tint nsize;\n+\n+\tpattern = is_oob ? KASAN_PATTERN_SLAB_OOB : KASAN_PATTERN_SLAB_UAF;\n+\tnsize = snprintf(access_log, READ_CHUNK_SIZE, pattern,\n+\t\t\t ctx-\u003eprog_tag[0], ctx-\u003eprog_tag[1], ctx-\u003eprog_tag[2],\n+\t\t\t ctx-\u003eprog_tag[3], ctx-\u003eprog_tag[4], ctx-\u003eprog_tag[5],\n+\t\t\t ctx-\u003eprog_tag[6], ctx-\u003eprog_tag[7], ctx-\u003eprog_name);\n+\tif (!ASSERT_GE(nsize, 0, \"format kasan access header line\"))\n+\t\treturn nsize;\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+\tif (!ASSERT_GE(nsize, 0, \"format kasan access report line\"))\n+\t\treturn nsize;\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+\t\t\t\t\t\ttest-\u003eis_oob);\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%s\",\n+\t\t\t test-\u003eprog_type,\n+\t\t\t test-\u003eskip_on_stack_testing ? \"\" :\n+\t\t\t on_stack\t\t ? \"_on_stack\" :\n+\t\t\t\t\t\t \"_not_on_stack\");\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}\n+\n+\tsnprintf(ctx-\u003eprog_name, PROG_NAME_MAX_LEN, \"%s%s\", test-\u003eprog_type,\n+\t\t test-\u003eskip_on_stack_testing ? \"\" :\n+\t\t on_stack\t\t ? \"_on_stack\" :\n+\t\t\t\t\t \"_not_on_stack\");\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 = true,\n+\t\t.only_32_or_64 = true\n+\t},\n+\t{\n+\t\t.prog_type = \"simple_atomic_fetch\",\n+\t\t.is_write = true,\n+\t\t.skip_multi_size_testing = true,\n+\t\t.run_size = 8,\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.run_size = 4,\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+\t{\n+\t\t.prog_type = \"ldx_oob\",\n+\t\t.is_write = false,\n+\t\t.skip_on_stack_testing = true,\n+\t\t.is_oob = true\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 !get_kasan_multi_shot_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\tchar prog_name[SUBTEST_NAME_MAX_LEN];\n+\t\tstruct bpf_program *prog;\n+\n+\t\tif (!tests[i].rnd_hi32)\n+\t\t\tcontinue;\n+\n+\t\tsnprintf(prog_name, SUBTEST_NAME_MAX_LEN, \"%s_%s\",\n+\t\t\t tests[i].prog_type, \"on_stack\");\n+\t\tprog = bpf_object__find_program_by_name(skel-\u003eobj, prog_name);\n+\t\tif (!ASSERT_OK_PTR(prog, \"find rnd_hi32 on_stack prog\"))\n+\t\t\tgoto destroy;\n+\t\tbpf_program__set_flags(prog, BPF_F_TEST_RND_HI32);\n+\t\tsnprintf(prog_name, SUBTEST_NAME_MAX_LEN, \"%s_%s\",\n+\t\t\t tests[i].prog_type, \"not_on_stack\");\n+\t\tprog = bpf_object__find_program_by_name(skel-\u003eobj, prog_name);\n+\t\tif (!ASSERT_OK_PTR(prog, \"find rnd_hi32 not_on_stack 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 0000000000000..ea29197646b0b\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/kasan.c\n@@ -0,0 +1,462 @@\n+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause\n+\n+#include \u003cstdbool.h\u003e\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+\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 bpf_testmod_oob {\n+\t__u8 data;\n+\tunion {\n+\t\t__u8 redzone_1;\n+\t\t__u16 redzone_2;\n+\t\t__u32 redzone_4;\n+\t\t__u64 redzone_8;\n+\t};\n+};\n+\n+extern struct bpf_testmod_oob *bpf_testmod_oob_alloc(void) __ksym;\n+extern void bpf_testmod_oob_free(struct bpf_testmod_oob *oob) __ksym;\n+\n+int access_size;\n+\n+struct kasan_test_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_test_val);\n+} test_map SEC(\".maps\");\n+\n+SEC(\"tcx/ingress\")\n+int st_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_test_val val;\n+\n+\tbpf_kfunc_kasan_poison(\u0026val, sizeof(struct kasan_test_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_test_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int st_not_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_test_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_test_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_test_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int stx_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_test_val val;\n+\n+\tbpf_kfunc_kasan_poison(\u0026val, sizeof(struct kasan_test_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_test_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int stx_not_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_test_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_test_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_test_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int ldx_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_test_val val;\n+\n+\tbpf_kfunc_kasan_poison(\u0026val, sizeof(struct kasan_test_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_test_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int ldx_not_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_test_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_test_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_test_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int ldx_patched_not_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_test_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_test_val));\n+\t__sink(val-\u003edata_4);\n+\tbpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_test_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_test_val val;\n+\n+\tbpf_kfunc_kasan_poison(\u0026val, sizeof(struct kasan_test_val));\n+\t__sink(val.data_4);\n+\tbpf_kfunc_kasan_unpoison(\u0026val, sizeof(struct kasan_test_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_test_val val;\n+\n+\tbpf_kfunc_kasan_poison(\u0026val, sizeof(struct kasan_test_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_test_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_test_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_test_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_test_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int simple_atomic_fetch_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_test_val val;\n+\n+\tbpf_kfunc_kasan_poison(\u0026val, sizeof(struct kasan_test_val));\n+\t__sync_fetch_and_or(\u0026val.data_8, 8);\n+\tbpf_kfunc_kasan_unpoison(\u0026val, sizeof(struct kasan_test_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int simple_atomic_fetch_not_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_test_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_test_val));\n+\t__sync_fetch_and_or(\u0026val-\u003edata_8, 8);\n+\tbpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_test_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_test_val val;\n+\n+\tbpf_kfunc_kasan_poison(\u0026val, sizeof(struct kasan_test_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_test_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_test_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_test_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_test_val));\n+\treturn 0;\n+}\n+\n+SEC(\"tcx/ingress\")\n+int store_release_on_stack(struct __sk_buff *skb)\n+{\n+\tstruct kasan_test_val val;\n+\n+\tbpf_kfunc_kasan_poison(\u0026val, sizeof(struct kasan_test_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_test_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_test_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_test_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_test_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_test_val stack_val = {};\n+\tstruct kasan_test_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+SEC(\"tcx/ingress\")\n+int ldx_oob(struct __sk_buff *skb)\n+{\n+\tstruct bpf_testmod_oob *val;\n+\tstruct kasan_test_val volatile tmp;\n+\n+\tval = bpf_testmod_oob_alloc();\n+\tif (!val)\n+\t\treturn 0;\n+\n+\tswitch (access_size) {\n+\tcase 1:\n+\t\ttmp.data_1 = (__u8)val-\u003eredzone_1;\n+\t\tbreak;\n+\tcase 2:\n+\t\ttmp.data_2 = (__u16)val-\u003eredzone_2;\n+\t\tbreak;\n+\tcase 4:\n+\t\ttmp.data_4 = (__u32)val-\u003eredzone_4;\n+\t\tbreak;\n+\tcase 8:\n+\t\ttmp.data_8 = (__u64)val-\u003eredzone_8;\n+\t\tbreak;\n+\t}\n+\tbpf_testmod_oob_free(val);\n+\treturn tmp.data_1;\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 0000000000000..a2756bbfd5292\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_test_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_test_val);\n+} test_map SEC(\".maps\");\n+\n+SEC(\"tcx/ingress\")\n+int st_blinded(struct __sk_buff *skb)\n+{\n+\tstruct kasan_test_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_test_val));\n+\tval-\u003edata_1 = 0xAA;\n+\tbpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_test_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 eb0f9b5e18d85..b59f176228bbb 100644\n--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c\n+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c\n@@ -75,6 +75,16 @@ union bpf_testmod_union_arg_2 {\n \tstruct bpf_testmod_struct_arg_2 arg;\n };\n \n+struct bpf_testmod_oob {\n+\t__u8 data;\n+\tunion {\n+\t\t__u8 redzone_1;\n+\t\t__u16 redzone_2;\n+\t\t__u32 redzone_4;\n+\t\t__u64 redzone_8;\n+\t};\n+};\n+\n __bpf_hook_start();\n \n noinline int\n@@ -298,6 +308,47 @@ __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_oob *bpf_testmod_oob_alloc(void)\n+{\n+\tstruct bpf_testmod_oob *p;\n+\n+\t/*\n+\t * Only allocate size of data (and so, voluntarily use kmalloc\n+\t * instead of kmalloc_obj), not the rest of the structure, so\n+\t * that programs under test trying to access the rest of the\n+\t * structure trigger OoB accesses\n+\t */\n+\tp = kmalloc(sizeof(p-\u003edata), GFP_ATOMIC);\n+\tif (!p)\n+\t\treturn NULL;\n+\treturn p;\n+}\n+\n+__bpf_kfunc void bpf_testmod_oob_free(struct bpf_testmod_oob *oob)\n+{\n+\tkfree(oob);\n+}\n+\n __bpf_kfunc struct bpf_testmod_ctx *\n bpf_testmod_ctx_create(int *err)\n {\n@@ -772,6 +823,10 @@ 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_ID_FLAGS(func, bpf_testmod_oob_alloc, KF_ACQUIRE | KF_RET_NULL)\n+BTF_ID_FLAGS(func, bpf_testmod_oob_free, KF_RELEASE)\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 c970e7793dfcb..737f668b35e23 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 2edc6fb7fc521..e00642afe86f4 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 f997d7ec8fd08..2b0a48e729dee 100644\n--- a/tools/testing/selftests/bpf/unpriv_helpers.c\n+++ b/tools/testing/selftests/bpf/unpriv_helpers.c\n@@ -72,8 +72,9 @@ static int config_contains(const char *pat)\n \n static bool cmdline_contains(const char *pat)\n {\n+\tint fd, cnt, ret = false;\n \tchar cmdline[4096], *c;\n-\tint fd, ret = false;\n+\tsize_t pat_len;\n \n \tfd = open(\"/proc/cmdline\", O_RDONLY);\n \tif (fd \u003c 0) {\n@@ -81,14 +82,16 @@ static bool cmdline_contains(const char *pat)\n \t\treturn false;\n \t}\n \n-\tif (read(fd, cmdline, sizeof(cmdline) - 1) \u003c 0) {\n+\tcnt = read(fd, cmdline, sizeof(cmdline) - 1);\n+\tif (cnt \u003c 0) {\n \t\tperror(\"read /proc/cmdline\");\n \t\tgoto out;\n \t}\n \n-\tcmdline[sizeof(cmdline) - 1] = '\\0';\n+\tcmdline[cnt] = '\\0';\n+\tpat_len = strlen(pat);\n \tfor (c = strtok(cmdline, \" \\n\"); c; c = strtok(NULL, \" \\n\")) {\n-\t\tif (strncmp(c, pat, strlen(c)))\n+\t\tif (strlen(c) != pat_len || strcmp(c, pat))\n \t\t\tcontinue;\n \t\tret = true;\n \t\tbreak;\n@@ -142,3 +145,13 @@ 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+}\n+\n+bool get_kasan_multi_shot_enabled(void)\n+{\n+\treturn cmdline_contains(\"kasan_multi_shot\");\n+}\ndiff --git a/tools/testing/selftests/bpf/unpriv_helpers.h b/tools/testing/selftests/bpf/unpriv_helpers.h\nindex 151f673296652..a7ceb51577cdc 100644\n--- a/tools/testing/selftests/bpf/unpriv_helpers.h\n+++ b/tools/testing/selftests/bpf/unpriv_helpers.h\n@@ -5,3 +5,5 @@\n #define UNPRIV_SYSCTL \"kernel/unprivileged_bpf_disabled\"\n \n bool get_unpriv_disabled(void);\n+bool get_kasan_jit_enabled(void);\n+bool get_kasan_multi_shot_enabled(void);\n"
} |
| 2/1 | 2026/08/04 18:03 | agent | patch-evaluator |
1mModel:best-expensive Args: null Results: {
"EnableConfigs": [
"BPF_JIT",
"KASAN_GENERIC",
"KASAN_VMALLOC"
],
"FocusSymbols": [
"bpf_int_jit_compile",
"bpf_patch_insn_data",
"bpf_check"
],
"Reasoning": "The patch adds KASAN instrumentation to the eBPF JIT compiler on x86_64. It modifies the JIT compiler to emit KASAN checks for memory accesses, and updates the BPF verifier and fixup logic to track whether instructions access non-stack memory. This is a functional change to core kernel logic (BPF JIT and verifier) that can be reached from user space by loading and executing BPF programs. Fuzzing is highly recommended to ensure the JIT emits correct KASAN checks and doesn't introduce regressions or crashes.",
"WorthFuzzing": true
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). - Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed, ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU. - Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove) that are executed only during PCI hot-unplug or sysfs driver unbind operations. If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. Prefer non-static, non-inlined API entrypoint functions over internal static helper functions (which are inlined by the compiler and do not have distinct symbol addresses). 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit d2070d895c40d50807e270b97c18771cbe784f7e
Author: syz-cluster <triage@syzkaller.com>
Date: Tue Aug 4 18:03:42 2026 +0000
syz-cluster: applied patch under review
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index bdad90f210e4b..705a9ab85f3a9 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 01e7ce569c1ed..b7881b9953107 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,92 @@ 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)
+{
+#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;
+
+ /* 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);
+ /*
+ * SystemV ABI states that we should also save r10/r11, but in
+ * practice those registers are _not_ used by the limited set of
+ * kasan helpers we are calling here, so that's fine not to save those.
+ */
+
+ /* 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 +1412,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 +1578,35 @@ 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:
+ if (!accesses_stack_only) {
+ err = emit_kasan_check(env, pprog, src_reg, insn, ip,
+ false);
+ 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:
+ if (!accesses_stack_only) {
+ err = emit_kasan_check(env, pprog, dst_reg, insn, ip,
+ true);
+ 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 +1984,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 +2006,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 +2386,19 @@ 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 (!accesses_stack_only) {
+ err = emit_kasan_check(env, &prog, dst_reg,
+ insn, ip, true);
+ if (err)
+ return err;
}
- 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)));
+ emit_st(&prog, insn, dst_reg, outgoing_arg_base,
+ outgoing_rsp);
break;
/* STX: *(u8*)(dst_reg + off) = src_reg */
@@ -2267,6 +2416,12 @@ st: insn_off = insn->off;
insn_off = outgoing_arg_base - outgoing_rsp - insn_off - 16;
dst_reg = BPF_REG_FP;
}
+ if (!accesses_stack_only) {
+ err = emit_kasan_check(env, &prog, dst_reg,
+ insn, ip, true);
+ if (err)
+ return err;
+ }
emit_stx(&prog, BPF_SIZE(insn->code), dst_reg, src_reg, insn_off);
break;
@@ -2428,6 +2583,11 @@ 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 if (!accesses_stack_only) {
+ err = emit_kasan_check(env, &prog, src_reg,
+ insn, ip, false);
+ if (err)
+ return err;
}
if (BPF_MODE(insn->code) == BPF_PROBE_MEMSX ||
BPF_MODE(insn->code) == BPF_MEMSX)
@@ -2489,28 +2649,42 @@ 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;
+ u8 *pprog;
+ 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.
*/
/* Will need RAX as a CMPXCHG operand so save R0 */
+ pprog = prog;
emit_mov_reg(&prog, true, BPF_REG_AX, BPF_REG_0);
if (src_reg == BPF_REG_0)
real_src_reg = BPF_REG_AX;
if (dst_reg == BPF_REG_0)
real_dst_reg = BPF_REG_AX;
-
+ ip += (prog - pprog);
+ }
+ if (!bpf_atomic_is_load_store(insn)) {
+ if (!accesses_stack_only) {
+ err = emit_kasan_check(env, &prog,
+ real_dst_reg,
+ insn, ip, true);
+ 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 +2716,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 a2a40caca0a0e..6c1376b2a3c92 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/kernel/bpf/Kconfig b/kernel/bpf/Kconfig
index eb3de35734f09..d7d25477ef481 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
+ 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/fixups.c b/kernel/bpf/fixups.c
index a0bddada7964e..6d69bf9db6ece 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,
+ struct bpf_insn *original_insn)
{
struct bpf_insn_aux_data *data = env->insn_aux_data;
struct bpf_insn *insn = new_prog->insnsi;
@@ -171,8 +184,15 @@ 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 + off);
return;
+ }
prog_len = new_prog->len;
memmove(data + off + cnt - 1, data + off,
@@ -182,8 +202,25 @@ 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 (!memcmp(insn + i, original_insn, sizeof(struct bpf_insn))) {
+ 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, systematically mark it for non-stack access
+ * if it is not the original instruction, otherwise keep the
+ * original marking
+ */
+ if (is_mem_insn(insn + off + cnt - 1) &&
+ memcmp(insn + off + cnt - 1, original_insn, sizeof(struct bpf_insn)))
+ data[off + cnt - 1].non_stack_access = true;
+
/*
* The indirect_target flag of the original instruction was moved to the last of the
* new instructions by the above memmove and memset, but the indirect jump target is
@@ -249,6 +286,7 @@ struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
{
struct bpf_prog *new_prog;
struct bpf_insn_aux_data *new_data = NULL;
+ struct bpf_insn original_insn;
if (len > 1) {
new_data = vrealloc(env->insn_aux_data,
@@ -261,6 +299,7 @@ struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,
env->insn_aux_data = new_data;
}
+ memcpy(&original_insn, env->prog->insnsi + off, sizeof(struct bpf_insn));
new_prog = bpf_patch_insn_single(env->prog, off, patch, len);
if (IS_ERR(new_prog)) {
if (PTR_ERR(new_prog) == -ERANGE)
@@ -269,7 +308,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, &original_insn);
adjust_subprog_starts(env, off, len);
adjust_insn_arrays(env, off, len);
adjust_poke_descs(new_prog, off, len);
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 7439afdc851a7..3392d93c73f82 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -3213,6 +3213,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)
@@ -6433,6 +6438,10 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct b
clear_scalar_id(®s[value_regno]);
}
}
+
+ 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 0222a9a5d0761..815f3e04540ff 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;
}
@@ -313,7 +278,8 @@ static void check_blindness(void)
cleanup:
/* restore the old one */
if (bpf_jit_harden != '@')
- set_bpf_jit_harden(&bpf_jit_harden);
+ ASSERT_OK(set_bpf_jit_harden(&bpf_jit_harden),
+ "restore hardening configuration");
close(prog_fd);
close(map_fd);
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 0000000000000..2b424767a0f3f
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/kasan.c
@@ -0,0 +1,454 @@
+// 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_SLAB_OOB "BUG: KASAN: slab-out-of-bounds " \
+ "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;
+ bool is_oob;
+};
+
+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)
+ break;
+
+ 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,
+ bool is_oob)
+{
+ char access_log[READ_CHUNK_SIZE];
+ const char *pattern;
+ char *kasan_report_start;
+ int nsize;
+
+ pattern = is_oob ? KASAN_PATTERN_SLAB_OOB : KASAN_PATTERN_SLAB_UAF;
+ nsize = snprintf(access_log, READ_CHUNK_SIZE, pattern,
+ 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);
+ if (!ASSERT_GE(nsize, 0, "format kasan access header line"))
+ return nsize;
+ /*
+ * 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 (!ASSERT_GE(nsize, 0, "format kasan access report line"))
+ return nsize;
+
+ 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,
+ test->is_oob);
+ 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%s",
+ test->prog_type,
+ test->skip_on_stack_testing ? "" :
+ on_stack ? "_on_stack" :
+ "_not_on_stack");
+ } 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,
+ test->skip_on_stack_testing ? "" :
+ 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 = true,
+ .only_32_or_64 = true
+ },
+ {
+ .prog_type = "simple_atomic_fetch",
+ .is_write = true,
+ .skip_multi_size_testing = true,
+ .run_size = 8,
+ },
+ {
+ .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,
+ .run_size = 4,
+ .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
+ },
+ {
+ .prog_type = "ldx_oob",
+ .is_write = false,
+ .skip_on_stack_testing = true,
+ .is_oob = true
+ },
+};
+
+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() ||
+ !get_kasan_multi_shot_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++) {
+ char prog_name[SUBTEST_NAME_MAX_LEN];
+ struct bpf_program *prog;
+
+ if (!tests[i].rnd_hi32)
+ continue;
+
+ snprintf(prog_name, SUBTEST_NAME_MAX_LEN, "%s_%s",
+ tests[i].prog_type, "on_stack");
+ prog = bpf_object__find_program_by_name(skel->obj, prog_name);
+ if (!ASSERT_OK_PTR(prog, "find rnd_hi32 on_stack prog"))
+ goto destroy;
+ bpf_program__set_flags(prog, BPF_F_TEST_RND_HI32);
+ snprintf(prog_name, SUBTEST_NAME_MAX_LEN, "%s_%s",
+ tests[i].prog_type, "not_on_stack");
+ prog = bpf_object__find_program_by_name(skel->obj, prog_name);
+ if (!ASSERT_OK_PTR(prog, "find rnd_hi32 not_on_stack 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 0000000000000..ea29197646b0b
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/kasan.c
@@ -0,0 +1,462 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+#include <stdbool.h>
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include "bpf_misc.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 bpf_testmod_oob {
+ __u8 data;
+ union {
+ __u8 redzone_1;
+ __u16 redzone_2;
+ __u32 redzone_4;
+ __u64 redzone_8;
+ };
+};
+
+extern struct bpf_testmod_oob *bpf_testmod_oob_alloc(void) __ksym;
+extern void bpf_testmod_oob_free(struct bpf_testmod_oob *oob) __ksym;
+
+int access_size;
+
+struct kasan_test_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_test_val);
+} test_map SEC(".maps");
+
+SEC("tcx/ingress")
+int st_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_test_val val;
+
+ bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_test_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_test_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int st_not_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_test_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_test_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_test_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int stx_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_test_val val;
+
+ bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_test_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_test_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int stx_not_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_test_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_test_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_test_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int ldx_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_test_val val;
+
+ bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_test_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_test_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int ldx_not_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_test_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_test_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_test_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int ldx_patched_not_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_test_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_test_val));
+ __sink(val->data_4);
+ bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_test_val));
+
+ return 0;
+}
+
+SEC("tcx/ingress")
+int ldx_patched_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_test_val val;
+
+ bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_test_val));
+ __sink(val.data_4);
+ bpf_kfunc_kasan_unpoison(&val, sizeof(struct kasan_test_val));
+
+ return 0;
+}
+
+SEC("tcx/ingress")
+int simple_atomic_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_test_val val;
+
+ bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_test_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_test_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int simple_atomic_not_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_test_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_test_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_test_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int simple_atomic_fetch_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_test_val val;
+
+ bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_test_val));
+ __sync_fetch_and_or(&val.data_8, 8);
+ bpf_kfunc_kasan_unpoison(&val, sizeof(struct kasan_test_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int simple_atomic_fetch_not_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_test_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_test_val));
+ __sync_fetch_and_or(&val->data_8, 8);
+ bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_test_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_test_val val;
+
+ bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_test_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_test_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int load_acquire_not_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_test_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_test_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_test_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int store_release_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_test_val val;
+
+ bpf_kfunc_kasan_poison(&val, sizeof(struct kasan_test_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_test_val));
+ return 0;
+}
+
+SEC("tcx/ingress")
+int store_release_not_on_stack(struct __sk_buff *skb)
+{
+ struct kasan_test_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_test_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_test_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_test_val stack_val = {};
+ struct kasan_test_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;
+}
+
+SEC("tcx/ingress")
+int ldx_oob(struct __sk_buff *skb)
+{
+ struct bpf_testmod_oob *val;
+ struct kasan_test_val volatile tmp;
+
+ val = bpf_testmod_oob_alloc();
+ if (!val)
+ return 0;
+
+ switch (access_size) {
+ case 1:
+ tmp.data_1 = (__u8)val->redzone_1;
+ break;
+ case 2:
+ tmp.data_2 = (__u16)val->redzone_2;
+ break;
+ case 4:
+ tmp.data_4 = (__u32)val->redzone_4;
+ break;
+ case 8:
+ tmp.data_8 = (__u64)val->redzone_8;
+ break;
+ }
+ bpf_testmod_oob_free(val);
+ return tmp.data_1;
+}
+
+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 0000000000000..a2756bbfd5292
--- /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_test_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_test_val);
+} test_map SEC(".maps");
+
+SEC("tcx/ingress")
+int st_blinded(struct __sk_buff *skb)
+{
+ struct kasan_test_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_test_val));
+ val->data_1 = 0xAA;
+ bpf_kfunc_kasan_unpoison(val, sizeof(struct kasan_test_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 eb0f9b5e18d85..b59f176228bbb 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -75,6 +75,16 @@ union bpf_testmod_union_arg_2 {
struct bpf_testmod_struct_arg_2 arg;
};
+struct bpf_testmod_oob {
+ __u8 data;
+ union {
+ __u8 redzone_1;
+ __u16 redzone_2;
+ __u32 redzone_4;
+ __u64 redzone_8;
+ };
+};
+
__bpf_hook_start();
noinline int
@@ -298,6 +308,47 @@ __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_oob *bpf_testmod_oob_alloc(void)
+{
+ struct bpf_testmod_oob *p;
+
+ /*
+ * Only allocate size of data (and so, voluntarily use kmalloc
+ * instead of kmalloc_obj), not the rest of the structure, so
+ * that programs under test trying to access the rest of the
+ * structure trigger OoB accesses
+ */
+ p = kmalloc(sizeof(p->data), GFP_ATOMIC);
+ if (!p)
+ return NULL;
+ return p;
+}
+
+__bpf_kfunc void bpf_testmod_oob_free(struct bpf_testmod_oob *oob)
+{
+ kfree(oob);
+}
+
__bpf_kfunc struct bpf_testmod_ctx *
bpf_testmod_ctx_create(int *err)
{
@@ -772,6 +823,10 @@ 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_ID_FLAGS(func, bpf_testmod_oob_alloc, KF_ACQUIRE | KF_RET_NULL)
+BTF_ID_FLAGS(func, bpf_testmod_oob_free, KF_RELEASE)
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 c970e7793dfcb..737f668b35e23 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 2edc6fb7fc521..e00642afe86f4 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 f997d7ec8fd08..2b0a48e729dee 100644
--- a/tools/testing/selftests/bpf/unpriv_helpers.c
+++ b/tools/testing/selftests/bpf/unpriv_helpers.c
@@ -72,8 +72,9 @@ static int config_contains(const char *pat)
static bool cmdline_contains(const char *pat)
{
+ int fd, cnt, ret = false;
char cmdline[4096], *c;
- int fd, ret = false;
+ size_t pat_len;
fd = open("/proc/cmdline", O_RDONLY);
if (fd < 0) {
@@ -81,14 +82,16 @@ static bool cmdline_contains(const char *pat)
return false;
}
- if (read(fd, cmdline, sizeof(cmdline) - 1) < 0) {
+ cnt = read(fd, cmdline, sizeof(cmdline) - 1);
+ if (cnt < 0) {
perror("read /proc/cmdline");
goto out;
}
- cmdline[sizeof(cmdline) - 1] = '\0';
+ cmdline[cnt] = '\0';
+ pat_len = strlen(pat);
for (c = strtok(cmdline, " \n"); c; c = strtok(NULL, " \n")) {
- if (strncmp(c, pat, strlen(c)))
+ if (strlen(c) != pat_len || strcmp(c, pat))
continue;
ret = true;
break;
@@ -142,3 +145,13 @@ bool get_unpriv_disabled(void)
}
return mitigations_off;
}
+
+bool get_kasan_jit_enabled(void)
+{
+ return config_contains("CONFIG_BPF_JIT_KASAN=y") == 1;
+}
+
+bool get_kasan_multi_shot_enabled(void)
+{
+ return cmdline_contains("kasan_multi_shot");
+}
diff --git a/tools/testing/selftests/bpf/unpriv_helpers.h b/tools/testing/selftests/bpf/unpriv_helpers.h
index 151f673296652..a7ceb51577cdc 100644
--- a/tools/testing/selftests/bpf/unpriv_helpers.h
+++ b/tools/testing/selftests/bpf/unpriv_helpers.h
@@ -5,3 +5,5 @@
#define UNPRIV_SYSCTL "kernel/unprivileged_bpf_disabled"
bool get_unpriv_disabled(void);
+bool get_kasan_jit_enabled(void);
+bool get_kasan_multi_shot_enabled(void);
|
| 3/2 | 2026/08/04 18:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 24065 output: 83 thoughts: 765 Thoughts: **KASAN Support for BPF JIT on x86_64: A Deep Dive** Okay, so I'm looking at this patch series. It seems like the core of the work is focused on adding KASAN support to the BPF JIT compiler on x86_64. I see changes across a few key areas: `arch/x86/Kconfig`, modifications to the BPF JIT compiler (`arch/x86/net/bpf_jit_comp.c`), changes to verifier internals (`include/linux/bpf_verifier.h`, `kernel/bpf/verifier.c`, `kernel/bpf/fixups.c`), and some adjustments to the selftests. Digging into the meat of the changes, the most interesting bit is the addition of `emit_kasan_check` in `arch/x86/net/bpf_jit_comp.c`. This is where the magic happens – it's emitting the necessary calls to `__asan_load1`, `__asan_store1`, and similar functions to trigger KASAN checks during JIT compilation. The other relevant areas are the verifier side which is tagging the memory access, and `adjust_insn_aux_data` in `kernel/bpf/fixups.c`. This makes it functional and something I'd want to fuzz to ensure its stability. The key configuration is `CONFIG_BPF_JIT_KASAN`, but, hold on, it seems it depends on `HAVE_EBPF_JIT_KASAN`, `KASAN_GENERIC`, `KASAN_VMALLOC`, and `BPF_JIT`. Based on looking through the Kconfig files, the dependency `HAVE_EBPF_JIT_KASAN` is met by selecting `X86_64`. From a configuration standpoint, `BPF_JIT_KASAN` isn't visible, so it's automatically enabled if the dependencies are met. So, the main configuration flags I need to focus on enabling are `KASAN_GENERIC`, `KASAN_VMALLOC`, and `BPF_JIT`. Now, I need to remember `KASAN_GENERIC` is a choice under `KASAN`, so that has some configuration implications. Now, I want to understand exactly which functions are modified by this code, which will influence my analysis. I see that there is modification of `do_jit` in `arch/x86/net/bpf_jit_comp.c`, but it is static, so I'll need to look for any exposed non-static functions in that source file. |
| 4/2 | 2026/08/04 18:03 | tool | grepper |
0mArgs:{
"Expression": "^([a-zA-Z_][a-zA-Z0-9_]*\\s+)+\\*?[a-zA-Z_][a-zA-Z0-9_]*\\([^;]*$",
"PathPrefix": "arch/x86/net/bpf_jit_comp.c"
}Results: {
"Output": "arch/x86/net/bpf_jit_comp.c=35=static bool all_callee_regs_used[4] = {true, true, true, true};\narch/x86/net/bpf_jit_comp.c-36-\narch/x86/net/bpf_jit_comp.c:37:static u8 *emit_code(u8 *ptr, u32 bytes, unsigned int len)\narch/x86/net/bpf_jit_comp.c-38-{\n--\narch/x86/net/bpf_jit_comp.c-76-\narch/x86/net/bpf_jit_comp.c:77:static bool is_imm8(int value)\narch/x86/net/bpf_jit_comp.c-78-{\n--\narch/x86/net/bpf_jit_comp.c-126- */\narch/x86/net/bpf_jit_comp.c:127:static bool is_imm8_jmp_offset(int value)\narch/x86/net/bpf_jit_comp.c-128-{\n--\narch/x86/net/bpf_jit_comp.c-131-\narch/x86/net/bpf_jit_comp.c:132:static bool is_simm32(s64 value)\narch/x86/net/bpf_jit_comp.c-133-{\n--\narch/x86/net/bpf_jit_comp.c-136-\narch/x86/net/bpf_jit_comp.c:137:static bool is_uimm32(u64 value)\narch/x86/net/bpf_jit_comp.c-138-{\n--\narch/x86/net/bpf_jit_comp.c-148-\narch/x86/net/bpf_jit_comp.c:149:static int bpf_size_to_x86_bytes(int bpf_size)\narch/x86/net/bpf_jit_comp.c-150-{\n--\narch/x86/net/bpf_jit_comp.c=211=static const int reg2pt_regs[] = {\n--\narch/x86/net/bpf_jit_comp.c-228- */\narch/x86/net/bpf_jit_comp.c:229:static bool is_ereg(u32 reg)\narch/x86/net/bpf_jit_comp.c-230-{\n--\narch/x86/net/bpf_jit_comp.c-245- */\narch/x86/net/bpf_jit_comp.c:246:static bool is_ereg_8l(u32 reg)\narch/x86/net/bpf_jit_comp.c-247-{\n--\narch/x86/net/bpf_jit_comp.c-253-\narch/x86/net/bpf_jit_comp.c:254:static bool is_axreg(u32 reg)\narch/x86/net/bpf_jit_comp.c-255-{\n--\narch/x86/net/bpf_jit_comp.c-259-/* Add modifiers if 'reg' maps to x86-64 registers R8..R15 */\narch/x86/net/bpf_jit_comp.c:260:static u8 add_1mod(u8 byte, u32 reg)\narch/x86/net/bpf_jit_comp.c-261-{\n--\narch/x86/net/bpf_jit_comp.c-266-\narch/x86/net/bpf_jit_comp.c:267:static u8 add_2mod(u8 byte, u32 r1, u32 r2)\narch/x86/net/bpf_jit_comp.c-268-{\n--\narch/x86/net/bpf_jit_comp.c-275-\narch/x86/net/bpf_jit_comp.c:276:static u8 add_3mod(u8 byte, u32 r1, u32 r2, u32 index)\narch/x86/net/bpf_jit_comp.c-277-{\n--\narch/x86/net/bpf_jit_comp.c-287-/* Encode 'dst_reg' register into x86-64 opcode 'byte' */\narch/x86/net/bpf_jit_comp.c:288:static u8 add_1reg(u8 byte, u32 dst_reg)\narch/x86/net/bpf_jit_comp.c-289-{\n--\narch/x86/net/bpf_jit_comp.c-293-/* Encode 'dst_reg' and 'src_reg' registers into x86-64 opcode 'byte' */\narch/x86/net/bpf_jit_comp.c:294:static u8 add_2reg(u8 byte, u32 dst_reg, u32 src_reg)\narch/x86/net/bpf_jit_comp.c-295-{\n--\narch/x86/net/bpf_jit_comp.c=300=static u8 simple_alu_opcodes[] = {\n--\narch/x86/net/bpf_jit_comp.c-310-\narch/x86/net/bpf_jit_comp.c:311:static void jit_fill_hole(void *area, unsigned int size)\narch/x86/net/bpf_jit_comp.c-312-{\n--\narch/x86/net/bpf_jit_comp.c-316-\narch/x86/net/bpf_jit_comp.c:317:int bpf_arch_text_invalidate(void *dst, size_t len)\narch/x86/net/bpf_jit_comp.c-318-{\n--\narch/x86/net/bpf_jit_comp.c=322=struct jit_context {\n--\narch/x86/net/bpf_jit_comp.c-342-\narch/x86/net/bpf_jit_comp.c:343:static void push_r9(u8 **pprog)\narch/x86/net/bpf_jit_comp.c-344-{\n--\narch/x86/net/bpf_jit_comp.c-350-\narch/x86/net/bpf_jit_comp.c:351:static void pop_r9(u8 **pprog)\narch/x86/net/bpf_jit_comp.c-352-{\n--\narch/x86/net/bpf_jit_comp.c-358-\narch/x86/net/bpf_jit_comp.c:359:static void push_r12(u8 **pprog)\narch/x86/net/bpf_jit_comp.c-360-{\n--\narch/x86/net/bpf_jit_comp.c-366-\narch/x86/net/bpf_jit_comp.c:367:static void push_callee_regs(u8 **pprog, bool *callee_regs_used)\narch/x86/net/bpf_jit_comp.c-368-{\n--\narch/x86/net/bpf_jit_comp.c-381-\narch/x86/net/bpf_jit_comp.c:382:static void pop_r12(u8 **pprog)\narch/x86/net/bpf_jit_comp.c-383-{\n--\narch/x86/net/bpf_jit_comp.c-389-\narch/x86/net/bpf_jit_comp.c:390:static void pop_callee_regs(u8 **pprog, bool *callee_regs_used)\narch/x86/net/bpf_jit_comp.c-391-{\n--\narch/x86/net/bpf_jit_comp.c-405-/* add rsp, depth */\narch/x86/net/bpf_jit_comp.c:406:static void emit_add_rsp(u8 **pprog, u16 depth)\narch/x86/net/bpf_jit_comp.c-407-{\n--\narch/x86/net/bpf_jit_comp.c-419-/* sub rsp, depth */\narch/x86/net/bpf_jit_comp.c:420:static void emit_sub_rsp(u8 **pprog, u16 depth)\narch/x86/net/bpf_jit_comp.c-421-{\n--\narch/x86/net/bpf_jit_comp.c-432-\narch/x86/net/bpf_jit_comp.c:433:static void emit_nops(u8 **pprog, int len)\narch/x86/net/bpf_jit_comp.c-434-{\n--\narch/x86/net/bpf_jit_comp.c=456=static int emit_call(u8 **prog, void *func, void *ip);\narch/x86/net/bpf_jit_comp.c-457-\narch/x86/net/bpf_jit_comp.c:458:static void emit_fineibt(u8 **pprog, u8 *ip, u32 hash, int arity)\narch/x86/net/bpf_jit_comp.c-459-{\n--\narch/x86/net/bpf_jit_comp.c-474-\narch/x86/net/bpf_jit_comp.c:475:static void emit_kcfi(u8 **pprog, u32 hash)\narch/x86/net/bpf_jit_comp.c-476-{\n--\narch/x86/net/bpf_jit_comp.c-488-\narch/x86/net/bpf_jit_comp.c:489:static void emit_cfi(u8 **pprog, u8 *ip, u32 hash, int arity)\narch/x86/net/bpf_jit_comp.c-490-{\n--\narch/x86/net/bpf_jit_comp.c-509-\narch/x86/net/bpf_jit_comp.c:510:static void emit_prologue_tail_call(u8 **pprog, bool is_subprog)\narch/x86/net/bpf_jit_comp.c-511-{\n--\narch/x86/net/bpf_jit_comp.c-545- */\narch/x86/net/bpf_jit_comp.c:546:static void emit_prologue(u8 **pprog, u8 *ip, u32 stack_depth, bool ebpf_from_cbpf,\narch/x86/net/bpf_jit_comp.c-547-\t\t\t bool tail_call_reachable, bool is_subprog,\n--\narch/x86/net/bpf_jit_comp.c-598-\narch/x86/net/bpf_jit_comp.c:599:static int emit_patch(u8 **pprog, void *func, void *ip, u8 opcode)\narch/x86/net/bpf_jit_comp.c-600-{\n--\narch/x86/net/bpf_jit_comp.c-613-\narch/x86/net/bpf_jit_comp.c:614:static int emit_call(u8 **pprog, void *func, void *ip)\narch/x86/net/bpf_jit_comp.c-615-{\n--\narch/x86/net/bpf_jit_comp.c-618-\narch/x86/net/bpf_jit_comp.c:619:static int emit_rsb_call(u8 **pprog, void *func, void *ip)\narch/x86/net/bpf_jit_comp.c-620-{\n--\narch/x86/net/bpf_jit_comp.c-625-\narch/x86/net/bpf_jit_comp.c:626:static int emit_jump(u8 **pprog, void *func, void *ip)\narch/x86/net/bpf_jit_comp.c-627-{\n--\narch/x86/net/bpf_jit_comp.c-630-\narch/x86/net/bpf_jit_comp.c:631:static int __bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,\narch/x86/net/bpf_jit_comp.c-632-\t\t\t\tenum bpf_text_poke_type new_t,\n--\narch/x86/net/bpf_jit_comp.c-674-\narch/x86/net/bpf_jit_comp.c:675:int bpf_arch_text_poke(void *ip, enum bpf_text_poke_type old_t,\narch/x86/net/bpf_jit_comp.c-676-\t\t enum bpf_text_poke_type new_t, void *old_addr,\n--\narch/x86/net/bpf_jit_comp.c-695-\narch/x86/net/bpf_jit_comp.c:696:static void __emit_indirect_jump(u8 **pprog, int reg, bool ereg)\narch/x86/net/bpf_jit_comp.c-697-{\n--\narch/x86/net/bpf_jit_comp.c-707-\narch/x86/net/bpf_jit_comp.c:708:static void emit_indirect_jump(u8 **pprog, int bpf_reg, u8 *ip)\narch/x86/net/bpf_jit_comp.c-709-{\n--\narch/x86/net/bpf_jit_comp.c-734-\narch/x86/net/bpf_jit_comp.c:735:static void emit_return(u8 **pprog, u8 *ip)\narch/x86/net/bpf_jit_comp.c-736-{\n--\narch/x86/net/bpf_jit_comp.c-765- */\narch/x86/net/bpf_jit_comp.c:766:static void emit_bpf_tail_call_indirect(struct bpf_prog *bpf_prog,\narch/x86/net/bpf_jit_comp.c-767-\t\t\t\t\tu8 **pprog, bool *callee_regs_used,\n--\narch/x86/net/bpf_jit_comp.c-853-\narch/x86/net/bpf_jit_comp.c:854:static void emit_bpf_tail_call_direct(struct bpf_prog *bpf_prog,\narch/x86/net/bpf_jit_comp.c-855-\t\t\t\t struct bpf_jit_poke_descriptor *poke,\n--\narch/x86/net/bpf_jit_comp.c-910-\narch/x86/net/bpf_jit_comp.c:911:static void bpf_tail_call_direct_fixup(struct bpf_prog *prog)\narch/x86/net/bpf_jit_comp.c-912-{\n--\narch/x86/net/bpf_jit_comp.c-948-\narch/x86/net/bpf_jit_comp.c:949:static void emit_mov_imm32(u8 **pprog, bool sign_propagate,\narch/x86/net/bpf_jit_comp.c-950-\t\t\t u32 dst_reg, const u32 imm32)\n--\narch/x86/net/bpf_jit_comp.c-988-\narch/x86/net/bpf_jit_comp.c:989:static void emit_mov_imm64(u8 **pprog, u32 dst_reg,\narch/x86/net/bpf_jit_comp.c-990-\t\t\t const u32 imm32_hi, const u32 imm32_lo)\n--\narch/x86/net/bpf_jit_comp.c-1014-\narch/x86/net/bpf_jit_comp.c:1015:static void emit_mov_reg(u8 **pprog, bool is64, u32 dst_reg, u32 src_reg)\narch/x86/net/bpf_jit_comp.c-1016-{\n--\narch/x86/net/bpf_jit_comp.c-1031-\narch/x86/net/bpf_jit_comp.c:1032:static void emit_movsx_reg(u8 **pprog, int num_bits, bool is64, u32 dst_reg,\narch/x86/net/bpf_jit_comp.c-1033-\t\t\t u32 src_reg)\n--\narch/x86/net/bpf_jit_comp.c-1064-/* Emit the suffix (ModR/M etc) for addressing *(ptr_reg + off) and val_reg */\narch/x86/net/bpf_jit_comp.c:1065:static void emit_insn_suffix(u8 **pprog, u32 ptr_reg, u32 val_reg, int off)\narch/x86/net/bpf_jit_comp.c-1066-{\n--\narch/x86/net/bpf_jit_comp.c-1083-\narch/x86/net/bpf_jit_comp.c:1084:static void emit_insn_suffix_SIB(u8 **pprog, u32 ptr_reg, u32 val_reg, u32 index_reg, int off)\narch/x86/net/bpf_jit_comp.c-1085-{\n--\narch/x86/net/bpf_jit_comp.c-1098- */\narch/x86/net/bpf_jit_comp.c:1099:static void maybe_emit_mod(u8 **pprog, u32 dst_reg, u32 src_reg, bool is64)\narch/x86/net/bpf_jit_comp.c-1100-{\n--\narch/x86/net/bpf_jit_comp.c-1112- */\narch/x86/net/bpf_jit_comp.c:1113:static void maybe_emit_1mod(u8 **pprog, u32 reg, bool is64)\narch/x86/net/bpf_jit_comp.c-1114-{\n--\narch/x86/net/bpf_jit_comp.c-1123-\narch/x86/net/bpf_jit_comp.c:1124:static int emit_kasan_check(struct bpf_verifier_env *env, u8 **pprog,\narch/x86/net/bpf_jit_comp.c-1125-\t\t\t u32 addr_reg, struct bpf_insn *insn, u8 *ip,\n--\narch/x86/net/bpf_jit_comp.c-1210-/* LDX: dst_reg = *(u8*)(src_reg + off) */\narch/x86/net/bpf_jit_comp.c:1211:static void emit_ldx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)\narch/x86/net/bpf_jit_comp.c-1212-{\n--\narch/x86/net/bpf_jit_comp.c-1240-/* LDSX: dst_reg = *(s8*)(src_reg + off) */\narch/x86/net/bpf_jit_comp.c:1241:static void emit_ldsx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)\narch/x86/net/bpf_jit_comp.c-1242-{\n--\narch/x86/net/bpf_jit_comp.c-1262-\narch/x86/net/bpf_jit_comp.c:1263:static void emit_ldx_index(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, u32 index_reg, int off)\narch/x86/net/bpf_jit_comp.c-1264-{\n--\narch/x86/net/bpf_jit_comp.c-1288-\narch/x86/net/bpf_jit_comp.c:1289:static void emit_ldsx_index(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, u32 index_reg, int off)\narch/x86/net/bpf_jit_comp.c-1290-{\n--\narch/x86/net/bpf_jit_comp.c-1310-\narch/x86/net/bpf_jit_comp.c:1311:static void emit_ldx_r12(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)\narch/x86/net/bpf_jit_comp.c-1312-{\n--\narch/x86/net/bpf_jit_comp.c-1315-\narch/x86/net/bpf_jit_comp.c:1316:static void emit_ldsx_r12(u8 **prog, u32 size, u32 dst_reg, u32 src_reg, int off)\narch/x86/net/bpf_jit_comp.c-1317-{\n--\narch/x86/net/bpf_jit_comp.c-1321-/* STX: *(u8*)(dst_reg + off) = src_reg */\narch/x86/net/bpf_jit_comp.c:1322:static void emit_stx(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)\narch/x86/net/bpf_jit_comp.c-1323-{\n--\narch/x86/net/bpf_jit_comp.c-1355-/* STX: *(u8*)(dst_reg + index_reg + off) = src_reg */\narch/x86/net/bpf_jit_comp.c:1356:static void emit_stx_index(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, u32 index_reg, int off)\narch/x86/net/bpf_jit_comp.c-1357-{\n--\narch/x86/net/bpf_jit_comp.c-1381-\narch/x86/net/bpf_jit_comp.c:1382:static void emit_stx_r12(u8 **pprog, u32 size, u32 dst_reg, u32 src_reg, int off)\narch/x86/net/bpf_jit_comp.c-1383-{\n--\narch/x86/net/bpf_jit_comp.c-1387-/* ST: *(u8*)(dst_reg + index_reg + off) = imm32 */\narch/x86/net/bpf_jit_comp.c:1388:static void emit_st_index(u8 **pprog, u32 size, u32 dst_reg, u32 index_reg, int off, int imm)\narch/x86/net/bpf_jit_comp.c-1389-{\n--\narch/x86/net/bpf_jit_comp.c-1414-\narch/x86/net/bpf_jit_comp.c:1415:static void emit_st(u8 **pprog, struct bpf_insn *insn, int dst_reg,\narch/x86/net/bpf_jit_comp.c-1416-\t\t s32 outgoing_arg_base, u16 outgoing_rsp)\n--\narch/x86/net/bpf_jit_comp.c-1472-\narch/x86/net/bpf_jit_comp.c:1473:static void emit_st_r12(u8 **pprog, u32 size, u32 dst_reg, int off, int imm)\narch/x86/net/bpf_jit_comp.c-1474-{\n--\narch/x86/net/bpf_jit_comp.c-1477-\narch/x86/net/bpf_jit_comp.c:1478:static void emit_store_stack_imm64(u8 **pprog, int reg, int stack_off, u64 imm64)\narch/x86/net/bpf_jit_comp.c-1479-{\n--\narch/x86/net/bpf_jit_comp.c-1487-\narch/x86/net/bpf_jit_comp.c:1488:static int emit_atomic_rmw(u8 **pprog, u32 atomic_op,\narch/x86/net/bpf_jit_comp.c-1489-\t\t\t u32 dst_reg, u32 src_reg, s16 off, u8 bpf_size)\n--\narch/x86/net/bpf_jit_comp.c-1529-\narch/x86/net/bpf_jit_comp.c:1530:static int emit_atomic_rmw_index(u8 **pprog, u32 atomic_op, u32 size,\narch/x86/net/bpf_jit_comp.c-1531-\t\t\t\t u32 dst_reg, u32 src_reg, u32 index_reg,\n--\narch/x86/net/bpf_jit_comp.c-1580-\narch/x86/net/bpf_jit_comp.c:1581:static int emit_atomic_ld_st(struct bpf_verifier_env *env, u8 **pprog,\narch/x86/net/bpf_jit_comp.c-1582-\t\t\t struct bpf_insn *insn, u8 *ip, u32 dst_reg,\n--\narch/x86/net/bpf_jit_comp.c-1619-\narch/x86/net/bpf_jit_comp.c:1620:static int emit_atomic_ld_st_index(u8 **pprog, u32 atomic_op, u32 size,\narch/x86/net/bpf_jit_comp.c-1621-\t\t\t\t u32 dst_reg, u32 src_reg, u32 index_reg,\n--\narch/x86/net/bpf_jit_comp.c-1680-\narch/x86/net/bpf_jit_comp.c:1681:bool ex_handler_bpf(const struct exception_table_entry *x, struct pt_regs *regs)\narch/x86/net/bpf_jit_comp.c-1682-{\n--\narch/x86/net/bpf_jit_comp.c-1705-\narch/x86/net/bpf_jit_comp.c:1706:static void detect_reg_usage(struct bpf_insn *insn, int insn_cnt,\narch/x86/net/bpf_jit_comp.c-1707-\t\t\t bool *regs_used)\n--\narch/x86/net/bpf_jit_comp.c-1733- */\narch/x86/net/bpf_jit_comp.c:1734:static void emit_3vex(u8 **pprog, bool r, bool x, bool b, u8 m,\narch/x86/net/bpf_jit_comp.c-1735-\t\t bool w, u8 src_reg2, bool l, u8 pp)\n--\narch/x86/net/bpf_jit_comp.c-1770-/* emit BMI2 shift instruction */\narch/x86/net/bpf_jit_comp.c:1771:static void emit_shiftx(u8 **pprog, u32 dst_reg, u8 src_reg, bool is64, u8 op)\narch/x86/net/bpf_jit_comp.c-1772-{\n--\narch/x86/net/bpf_jit_comp.c-1781-\narch/x86/net/bpf_jit_comp.c:1782:static void emit_priv_frame_ptr(u8 **pprog, void __percpu *priv_frame_ptr)\narch/x86/net/bpf_jit_comp.c-1783-{\n--\narch/x86/net/bpf_jit_comp.c-1811-\narch/x86/net/bpf_jit_comp.c:1812:static int emit_spectre_bhb_barrier(u8 **pprog, u8 *ip,\narch/x86/net/bpf_jit_comp.c-1813-\t\t\t\t struct bpf_prog *bpf_prog)\n--\narch/x86/net/bpf_jit_comp.c-1853-\narch/x86/net/bpf_jit_comp.c:1854:static int do_jit(struct bpf_verifier_env *env, struct bpf_prog *bpf_prog, int *addrs, u8 *image,\narch/x86/net/bpf_jit_comp.c-1855-\t\t u8 *rw_image, int oldproglen, struct jit_context *ctx, bool jmp_padding)\n--\narch/x86/net/bpf_jit_comp.c-3105-\narch/x86/net/bpf_jit_comp.c:3106:static void clean_stack_garbage(const struct btf_func_model *m,\narch/x86/net/bpf_jit_comp.c-3107-\t\t\t\tu8 **pprog, int nr_stack_slots,\n--\narch/x86/net/bpf_jit_comp.c-3154-/* get the count of the regs that are used to pass arguments */\narch/x86/net/bpf_jit_comp.c:3155:static int get_nr_used_regs(const struct btf_func_model *m)\narch/x86/net/bpf_jit_comp.c-3156-{\n--\narch/x86/net/bpf_jit_comp.c-3170-\narch/x86/net/bpf_jit_comp.c:3171:static void save_args(const struct btf_func_model *m, u8 **prog,\narch/x86/net/bpf_jit_comp.c-3172-\t\t int stack_size, bool for_call_origin, u32 flags)\n--\narch/x86/net/bpf_jit_comp.c-3248-\narch/x86/net/bpf_jit_comp.c:3249:static void restore_regs(const struct btf_func_model *m, u8 **prog,\narch/x86/net/bpf_jit_comp.c-3250-\t\t\t int stack_size)\n--\narch/x86/net/bpf_jit_comp.c-3280-\narch/x86/net/bpf_jit_comp.c:3281:static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog,\narch/x86/net/bpf_jit_comp.c-3282-\t\t\t struct bpf_tramp_node *node, int stack_size,\n--\narch/x86/net/bpf_jit_comp.c-3369-\narch/x86/net/bpf_jit_comp.c:3370:static void emit_align(u8 **pprog, u32 align)\narch/x86/net/bpf_jit_comp.c-3371-{\n--\narch/x86/net/bpf_jit_comp.c-3380-\narch/x86/net/bpf_jit_comp.c:3381:static int emit_cond_near_jump(u8 **pprog, void *func, void *ip, u8 jmp_cond)\narch/x86/net/bpf_jit_comp.c-3382-{\n--\narch/x86/net/bpf_jit_comp.c-3395-\narch/x86/net/bpf_jit_comp.c:3396:static int invoke_bpf(const struct btf_func_model *m, u8 **pprog,\narch/x86/net/bpf_jit_comp.c-3397-\t\t struct bpf_tramp_nodes *tl, int stack_size,\n--\narch/x86/net/bpf_jit_comp.c-3418-\narch/x86/net/bpf_jit_comp.c:3419:static int invoke_bpf_mod_ret(const struct btf_func_model *m, u8 **pprog,\narch/x86/net/bpf_jit_comp.c-3420-\t\t\t struct bpf_tramp_nodes *tl, int stack_size,\n--\narch/x86/net/bpf_jit_comp.c-3520- */\narch/x86/net/bpf_jit_comp.c:3521:static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_image,\narch/x86/net/bpf_jit_comp.c-3522-\t\t\t\t\t void *rw_image_end, void *image,\n--\narch/x86/net/bpf_jit_comp.c-3825-\narch/x86/net/bpf_jit_comp.c:3826:void *arch_alloc_bpf_trampoline(unsigned int size)\narch/x86/net/bpf_jit_comp.c-3827-{\n--\narch/x86/net/bpf_jit_comp.c-3830-\narch/x86/net/bpf_jit_comp.c:3831:void arch_free_bpf_trampoline(void *image, unsigned int size)\narch/x86/net/bpf_jit_comp.c-3832-{\n--\narch/x86/net/bpf_jit_comp.c-3835-\narch/x86/net/bpf_jit_comp.c:3836:int arch_protect_bpf_trampoline(void *image, unsigned int size)\narch/x86/net/bpf_jit_comp.c-3837-{\n--\narch/x86/net/bpf_jit_comp.c-3840-\narch/x86/net/bpf_jit_comp.c:3841:int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *image_end,\narch/x86/net/bpf_jit_comp.c-3842-\t\t\t\tconst struct btf_func_model *m, u32 flags,\n--\narch/x86/net/bpf_jit_comp.c-3869-\narch/x86/net/bpf_jit_comp.c:3870:int arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,\narch/x86/net/bpf_jit_comp.c-3871-\t\t\t struct bpf_tramp_nodes *tnodes, void *func_addr)\n--\narch/x86/net/bpf_jit_comp.c-3892-\narch/x86/net/bpf_jit_comp.c:3893:static int emit_bpf_dispatcher(u8 **pprog, int a, int b, s64 *progs, u8 *image, u8 *buf)\narch/x86/net/bpf_jit_comp.c-3894-{\n--\narch/x86/net/bpf_jit_comp.c-3960-\narch/x86/net/bpf_jit_comp.c:3961:static int cmp_ips(const void *a, const void *b)\narch/x86/net/bpf_jit_comp.c-3962-{\n--\narch/x86/net/bpf_jit_comp.c-3972-\narch/x86/net/bpf_jit_comp.c:3973:int arch_prepare_bpf_dispatcher(void *image, void *buf, s64 *funcs, int num_funcs)\narch/x86/net/bpf_jit_comp.c-3974-{\n--\narch/x86/net/bpf_jit_comp.c-3980-\narch/x86/net/bpf_jit_comp.c:3981:static void priv_stack_init_guard(void __percpu *priv_stack_ptr, int alloc_size)\narch/x86/net/bpf_jit_comp.c-3982-{\n--\narch/x86/net/bpf_jit_comp.c-3992-\narch/x86/net/bpf_jit_comp.c:3993:static void priv_stack_check_guard(void __percpu *priv_stack_ptr, int alloc_size,\narch/x86/net/bpf_jit_comp.c-3994-\t\t\t\t struct bpf_prog *prog)\n--\narch/x86/net/bpf_jit_comp.c=4010=struct x64_jit_data {\n--\narch/x86/net/bpf_jit_comp.c-4021-\narch/x86/net/bpf_jit_comp.c:4022:struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog)\narch/x86/net/bpf_jit_comp.c-4023-{\n--\narch/x86/net/bpf_jit_comp.c-4213-\narch/x86/net/bpf_jit_comp.c:4214:bool bpf_jit_supports_kfunc_call(void)\narch/x86/net/bpf_jit_comp.c-4215-{\n--\narch/x86/net/bpf_jit_comp.c-4218-\narch/x86/net/bpf_jit_comp.c:4219:bool bpf_jit_supports_stack_args(void)\narch/x86/net/bpf_jit_comp.c-4220-{\n--\narch/x86/net/bpf_jit_comp.c-4223-\narch/x86/net/bpf_jit_comp.c:4224:void *bpf_arch_text_copy(void *dst, void *src, size_t len)\narch/x86/net/bpf_jit_comp.c-4225-{\n--\narch/x86/net/bpf_jit_comp.c-4231-/* Indicate the JIT backend supports mixing bpf2bpf and tailcalls. */\narch/x86/net/bpf_jit_comp.c:4232:bool bpf_jit_supports_subprog_tailcalls(void)\narch/x86/net/bpf_jit_comp.c-4233-{\n--\narch/x86/net/bpf_jit_comp.c-4236-\narch/x86/net/bpf_jit_comp.c:4237:bool bpf_jit_supports_percpu_insn(void)\narch/x86/net/bpf_jit_comp.c-4238-{\n--\narch/x86/net/bpf_jit_comp.c-4241-\narch/x86/net/bpf_jit_comp.c:4242:void bpf_jit_free(struct bpf_prog *prog)\narch/x86/net/bpf_jit_comp.c-4243-{\n--\narch/x86/net/bpf_jit_comp.c-4276-\narch/x86/net/bpf_jit_comp.c:4277:bool bpf_jit_supports_exceptions(void)\narch/x86/net/bpf_jit_comp.c-4278-{\n--\narch/x86/net/bpf_jit_comp.c-4285-\narch/x86/net/bpf_jit_comp.c:4286:bool bpf_jit_supports_private_stack(void)\narch/x86/net/bpf_jit_comp.c-4287-{\n--\narch/x86/net/bpf_jit_comp.c-4290-\narch/x86/net/bpf_jit_comp.c:4291:void arch_bpf_stack_walk(bool (*consume_fn)(void *cookie, u64 ip, u64 sp, u64 bp), void *cookie)\narch/x86/net/bpf_jit_comp.c-4292-{\n--\narch/x86/net/bpf_jit_comp.c-4306-\narch/x86/net/bpf_jit_comp.c:4307:void bpf_arch_poke_desc_update(struct bpf_jit_poke_descriptor *poke,\narch/x86/net/bpf_jit_comp.c-4308-\t\t\t struct bpf_prog *new, struct bpf_prog *old)\n--\narch/x86/net/bpf_jit_comp.c-4354-\narch/x86/net/bpf_jit_comp.c:4355:bool bpf_jit_supports_arena(void)\narch/x86/net/bpf_jit_comp.c-4356-{\n--\narch/x86/net/bpf_jit_comp.c-4359-\narch/x86/net/bpf_jit_comp.c:4360:bool bpf_jit_supports_insn(struct bpf_insn *insn, bool in_arena)\narch/x86/net/bpf_jit_comp.c-4361-{\n--\narch/x86/net/bpf_jit_comp.c-4374-\narch/x86/net/bpf_jit_comp.c:4375:bool bpf_jit_supports_ptr_xchg(void)\narch/x86/net/bpf_jit_comp.c-4376-{\n--\narch/x86/net/bpf_jit_comp.c-4380-/* x86-64 JIT emits its own code to filter user addresses so return 0 here */\narch/x86/net/bpf_jit_comp.c:4381:u64 bpf_arch_uaddress_limit(void)\narch/x86/net/bpf_jit_comp.c-4382-{\n--\narch/x86/net/bpf_jit_comp.c-4385-\narch/x86/net/bpf_jit_comp.c:4386:bool bpf_jit_supports_timed_may_goto(void)\narch/x86/net/bpf_jit_comp.c-4387-{\n--\narch/x86/net/bpf_jit_comp.c-4390-\narch/x86/net/bpf_jit_comp.c:4391:bool bpf_jit_supports_fsession(void)\narch/x86/net/bpf_jit_comp.c-4392-{\n"
} |
| 5/2 | 2026/08/04 18:03 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 37037 output: 76 thoughts: 106 Thoughts: **Analyzing the BPF JIT Patch** Okay, so this patch looks like it's digging into the BPF JIT compilation process. I see it's touching both `bpf_int_jit_compile` and `do_jit`, which, of course, is directly called from `bpf_int_jit_compile`. My immediate focus will be on those, potentially including `bpf_patch_insn_data`. Wait a minute, though... I recognize that name. `bpf_patch_insn_data` is being modified in `kernel/bpf/fixups.c`. That's important. Given that it's being patched, I need to understand that `fixups.c` file and how it works as part of this. Let's delve into that file now. |
| 6/2 | 2026/08/04 18:04 | tool | grepper |
0mArgs:{
"Expression": "^([a-zA-Z_][a-zA-Z0-9_]*\\s+)+\\*?[a-zA-Z_][a-zA-Z0-9_]*\\([^;]*$",
"PathPrefix": "kernel/bpf/fixups.c"
}Results: {
"Output": "kernel/bpf/fixups.c-15-\nkernel/bpf/fixups.c:16:static bool is_cmpxchg_insn(const struct bpf_insn *insn)\nkernel/bpf/fixups.c-17-{\n--\nkernel/bpf/fixups.c-23-/* Return the regno defined by the insn, or -1. */\nkernel/bpf/fixups.c:24:static int insn_def_regno(const struct bpf_insn *insn)\nkernel/bpf/fixups.c-25-{\n--\nkernel/bpf/fixups.c-47-/* Return TRUE if INSN has defined any 32-bit value explicitly. */\nkernel/bpf/fixups.c:48:static bool insn_has_def32(struct bpf_insn *insn)\nkernel/bpf/fixups.c-49-{\n--\nkernel/bpf/fixups.c-57-\nkernel/bpf/fixups.c:58:static int kfunc_desc_cmp_by_imm_off(const void *a, const void *b)\nkernel/bpf/fixups.c-59-{\n--\nkernel/bpf/fixups.c=71=bpf_jit_find_kfunc_model(const struct bpf_prog *prog,\n--\nkernel/bpf/fixups.c-87-\nkernel/bpf/fixups.c:88:static int set_kfunc_desc_imm(struct bpf_verifier_env *env, struct bpf_kfunc_desc *desc)\nkernel/bpf/fixups.c-89-{\n--\nkernel/bpf/fixups.c-106-\nkernel/bpf/fixups.c:107:static int sort_kfunc_descs_by_imm_off(struct bpf_verifier_env *env)\nkernel/bpf/fixups.c-108-{\n--\nkernel/bpf/fixups.c-126-\nkernel/bpf/fixups.c:127:static int add_kfunc_in_insns(struct bpf_verifier_env *env,\nkernel/bpf/fixups.c-128-\t\t\t struct bpf_insn *insn, int cnt)\n--\nkernel/bpf/fixups.c-142-#ifndef CONFIG_BPF_JIT_ALWAYS_ON\nkernel/bpf/fixups.c:143:static int get_callee_stack_depth(struct bpf_verifier_env *env,\nkernel/bpf/fixups.c-144-\t\t\t\t const struct bpf_insn *insn, int idx)\n--\nkernel/bpf/fixups.c-154-\nkernel/bpf/fixups.c:155:static bool is_mem_insn(struct bpf_insn *insn)\nkernel/bpf/fixups.c-156-{\n--\nkernel/bpf/fixups.c-170- */\nkernel/bpf/fixups.c:171:static void adjust_insn_aux_data(struct bpf_verifier_env *env,\nkernel/bpf/fixups.c-172-\t\t\t\t struct bpf_prog *new_prog, u32 off, u32 cnt,\n--\nkernel/bpf/fixups.c-236-\nkernel/bpf/fixups.c:237:static void adjust_subprog_starts(struct bpf_verifier_env *env, u32 off, u32 len)\nkernel/bpf/fixups.c-238-{\n--\nkernel/bpf/fixups.c-250-\nkernel/bpf/fixups.c:251:static void adjust_insn_arrays(struct bpf_verifier_env *env, u32 off, u32 len)\nkernel/bpf/fixups.c-252-{\n--\nkernel/bpf/fixups.c-261-\nkernel/bpf/fixups.c:262:static void adjust_insn_arrays_after_remove(struct bpf_verifier_env *env, u32 off, u32 len)\nkernel/bpf/fixups.c-263-{\n--\nkernel/bpf/fixups.c-269-\nkernel/bpf/fixups.c:270:static void adjust_poke_descs(struct bpf_prog *prog, u32 off, u32 len)\nkernel/bpf/fixups.c-271-{\n--\nkernel/bpf/fixups.c-283-\nkernel/bpf/fixups.c:284:struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,\nkernel/bpf/fixups.c-285-\t\t\t\t const struct bpf_insn *patch, u32 len)\n--\nkernel/bpf/fixups.c-321- */\nkernel/bpf/fixups.c:322:static int adjust_jmp_off(struct bpf_prog *prog, u32 tgt_idx, u32 delta)\nkernel/bpf/fixups.c-323-{\n--\nkernel/bpf/fixups.c-355-\nkernel/bpf/fixups.c:356:static int adjust_subprog_starts_after_remove(struct bpf_verifier_env *env,\nkernel/bpf/fixups.c-357-\t\t\t\t\t u32 off, u32 cnt)\n--\nkernel/bpf/fixups.c-411-\nkernel/bpf/fixups.c:412:static int bpf_adj_linfo_after_remove(struct bpf_verifier_env *env, u32 off,\nkernel/bpf/fixups.c-413-\t\t\t\t u32 cnt)\n--\nkernel/bpf/fixups.c-478- */\nkernel/bpf/fixups.c:479:void bpf_clear_insn_aux_data(struct bpf_verifier_env *env, int start, int len)\nkernel/bpf/fixups.c-480-{\n--\nkernel/bpf/fixups.c-496-\nkernel/bpf/fixups.c:497:static int verifier_remove_insns(struct bpf_verifier_env *env, u32 off, u32 cnt)\nkernel/bpf/fixups.c-498-{\n--\nkernel/bpf/fixups.c=530=static const struct bpf_insn MAY_GOTO_0 = BPF_RAW_INSN(BPF_JMP | BPF_JCOND, 0, 0, 0, 0);\nkernel/bpf/fixups.c-531-\nkernel/bpf/fixups.c:532:bool bpf_insn_is_cond_jump(u8 code)\nkernel/bpf/fixups.c-533-{\n--\nkernel/bpf/fixups.c-545-\nkernel/bpf/fixups.c:546:void bpf_opt_hard_wire_dead_code_branches(struct bpf_verifier_env *env)\nkernel/bpf/fixups.c-547-{\n--\nkernel/bpf/fixups.c-571-\nkernel/bpf/fixups.c:572:int bpf_opt_remove_dead_code(struct bpf_verifier_env *env)\nkernel/bpf/fixups.c-573-{\n--\nkernel/bpf/fixups.c-595-\nkernel/bpf/fixups.c:596:int bpf_opt_remove_nops(struct bpf_verifier_env *env)\nkernel/bpf/fixups.c-597-{\n--\nkernel/bpf/fixups.c-620-\nkernel/bpf/fixups.c:621:int bpf_opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,\nkernel/bpf/fixups.c-622-\t\t\t\t\t const union bpf_attr *attr)\n--\nkernel/bpf/fixups.c-726- */\nkernel/bpf/fixups.c:727:int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)\nkernel/bpf/fixups.c-728-{\n--\nkernel/bpf/fixups.c-1026-\nkernel/bpf/fixups.c:1027:static u32 *bpf_dup_subprog_starts(struct bpf_verifier_env *env)\nkernel/bpf/fixups.c-1028-{\n--\nkernel/bpf/fixups.c-1038-\nkernel/bpf/fixups.c:1039:static void bpf_restore_subprog_starts(struct bpf_verifier_env *env, u32 *orig_starts)\nkernel/bpf/fixups.c-1040-{\n--\nkernel/bpf/fixups.c-1046-\nkernel/bpf/fixups.c:1047:struct bpf_insn_aux_data *bpf_dup_insn_aux_data(struct bpf_verifier_env *env)\nkernel/bpf/fixups.c-1048-{\n--\nkernel/bpf/fixups.c-1058-\nkernel/bpf/fixups.c:1059:void bpf_restore_insn_aux_data(struct bpf_verifier_env *env,\nkernel/bpf/fixups.c-1060-\t\t\t struct bpf_insn_aux_data *orig_insn_aux)\n--\nkernel/bpf/fixups.c-1066-\nkernel/bpf/fixups.c:1067:static int jit_subprogs(struct bpf_verifier_env *env)\nkernel/bpf/fixups.c-1068-{\n--\nkernel/bpf/fixups.c-1334-\nkernel/bpf/fixups.c:1335:int bpf_jit_subprogs(struct bpf_verifier_env *env)\nkernel/bpf/fixups.c-1336-{\n--\nkernel/bpf/fixups.c-1414-\nkernel/bpf/fixups.c:1415:int bpf_fixup_call_args(struct bpf_verifier_env *env)\nkernel/bpf/fixups.c-1416-{\n--\nkernel/bpf/fixups.c-1489-/* The function requires that first instruction in 'patch' is insnsi[prog-\u003elen - 1] */\nkernel/bpf/fixups.c:1490:static int add_hidden_subprog(struct bpf_verifier_env *env, struct bpf_insn *patch, int len)\nkernel/bpf/fixups.c-1491-{\n--\nkernel/bpf/fixups.c-1518- */\nkernel/bpf/fixups.c:1519:int bpf_do_misc_fixups(struct bpf_verifier_env *env)\nkernel/bpf/fixups.c-1520-{\n--\nkernel/bpf/fixups.c-2470-\nkernel/bpf/fixups.c:2471:static struct bpf_prog *inline_bpf_loop(struct bpf_verifier_env *env,\nkernel/bpf/fixups.c-2472-\t\t\t\t\tint position,\n--\nkernel/bpf/fixups.c-2546-\nkernel/bpf/fixups.c:2547:static bool is_bpf_loop_call(struct bpf_insn *insn)\nkernel/bpf/fixups.c-2548-{\n--\nkernel/bpf/fixups.c-2562- */\nkernel/bpf/fixups.c:2563:int bpf_optimize_bpf_loop(struct bpf_verifier_env *env)\nkernel/bpf/fixups.c-2564-{\n--\nkernel/bpf/fixups.c-2610- */\nkernel/bpf/fixups.c:2611:int bpf_remove_fastcall_spills_fills(struct bpf_verifier_env *env)\nkernel/bpf/fixups.c-2612-{\n"
} |
| 7/2 | 2026/08/04 18:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 40527 output: 75 thoughts: 82 Thoughts: **Analyzing the BPF Patch** Okay, so this patch is clearly focused on memory access within the BPF (Berkeley Packet Filter) subsystem. My initial assessment is that it targets some kind of memory-related fix or enhancement. I see changes in `kernel/bpf/fixups.c`, specifically around `adjust_insn_aux_data` – that's often related to how the verifier handles instruction metadata – and a new function, `is_mem_insn`. The addition of `is_mem_insn` is immediately interesting; it implies that the patch needs a straightforward way to identify memory instructions, which could be for filtering or optimization. Now, the more substantial modifications seem to be within `kernel/bpf/verifier.c`. I see changes to `check_mem_access`, which is the core function for, well, *checking* memory access during BPF bytecode verification. Given my understanding of BPF security and sandboxing, I suspect these modifications address a potential vulnerability or some new type of memory access pattern. The added `mark_non_stack_access` function further reinforces this. This probably relates to distinguishing between allowed stack accesses and accesses outside the permitted stack memory, probably to enforce stricter security rules. Given the importance of `kernel/bpf/verifier.c` in BPF's security guarantees, I should focus on that file. The verifier is where the rubber meets the road, so to speak. Let me dive into the specific code changes within that file and see what's actually being modified in the memory access checks. I'll need to understand exactly what types of memory access are being modified and how the verifier is now differentiating them. |
| 8/2 | 2026/08/04 18:04 | tool | grepper |
0mArgs:{
"Expression": "^([a-zA-Z_][a-zA-Z0-9_]*\\s+)+\\*?[a-zA-Z_][a-zA-Z0-9_]*\\([^;]*$",
"PathPrefix": "kernel/bpf/verifier.c"
}Results: {
"Output": "\nFull output is too long, showing 500 out of 2385 lines.\nUse more precise expression if possible.\n\nkernel/bpf/verifier.c=211=static bool is_tracing_prog_type(enum bpf_prog_type type);\nkernel/bpf/verifier.c:212:static int ref_set_non_owning(struct bpf_verifier_env *env,\nkernel/bpf/verifier.c-213-\t\t\t struct bpf_reg_state *reg);\n--\nkernel/bpf/verifier.c=218=static void scalar_min_max_add(struct bpf_reg_state *dst_reg, struct bpf_reg_state *src_reg);\nkernel/bpf/verifier.c-219-\nkernel/bpf/verifier.c:220:static void bpf_map_ptr_store(struct bpf_insn_aux_data *aux,\nkernel/bpf/verifier.c-221-\t\t\t struct bpf_map *map,\n--\nkernel/bpf/verifier.c-229-\nkernel/bpf/verifier.c:230:static void bpf_map_key_store(struct bpf_insn_aux_data *aux, u64 state)\nkernel/bpf/verifier.c-231-{\n--\nkernel/bpf/verifier.c-237-\nkernel/bpf/verifier.c:238:static void update_ref_obj(struct ref_obj_desc *ref_obj, struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-239-{\n--\nkernel/bpf/verifier.c-244-\nkernel/bpf/verifier.c:245:static int validate_ref_obj(struct bpf_verifier_env *env, struct ref_obj_desc *ref_obj)\nkernel/bpf/verifier.c-246-{\n--\nkernel/bpf/verifier.c=266=typedef struct argno {\n--\nkernel/bpf/verifier.c-269-\nkernel/bpf/verifier.c:270:static argno_t argno_from_reg(u32 regno)\nkernel/bpf/verifier.c-271-{\n--\nkernel/bpf/verifier.c-274-\nkernel/bpf/verifier.c:275:static argno_t argno_from_arg(u32 arg)\nkernel/bpf/verifier.c-276-{\n--\nkernel/bpf/verifier.c-279-\nkernel/bpf/verifier.c:280:static int reg_from_argno(argno_t a)\nkernel/bpf/verifier.c-281-{\n--\nkernel/bpf/verifier.c-288-\nkernel/bpf/verifier.c:289:static int arg_from_argno(argno_t a)\nkernel/bpf/verifier.c-290-{\n--\nkernel/bpf/verifier.c-295-\nkernel/bpf/verifier.c:296:static int arg_idx_from_argno(argno_t a)\nkernel/bpf/verifier.c-297-{\n--\nkernel/bpf/verifier.c-300-\nkernel/bpf/verifier.c:301:static const char *btf_type_name(const struct btf *btf, u32 id)\nkernel/bpf/verifier.c-302-{\n--\nkernel/bpf/verifier.c=310=__printf(2, 3) static void verbose(void *private_data, const char *fmt, ...)\n--\nkernel/bpf/verifier.c-322-\nkernel/bpf/verifier.c:323:static void verbose_invalid_scalar(struct bpf_verifier_env *env,\nkernel/bpf/verifier.c-324-\t\t\t\t struct bpf_reg_state *reg,\n--\nkernel/bpf/verifier.c-343-\nkernel/bpf/verifier.c:344:static bool reg_not_null(struct bpf_verifier_env *env, const struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-345-{\n--\nkernel/bpf/verifier.c-362-\nkernel/bpf/verifier.c:363:static struct btf_record *reg_btf_record(const struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-364-{\n--\nkernel/bpf/verifier.c-377-\nkernel/bpf/verifier.c:378:bool bpf_subprog_is_global(const struct bpf_verifier_env *env, int subprog)\nkernel/bpf/verifier.c-379-{\n--\nkernel/bpf/verifier.c-384-\nkernel/bpf/verifier.c:385:static bool subprog_returns_void(struct bpf_verifier_env *env, int subprog)\nkernel/bpf/verifier.c-386-{\n--\nkernel/bpf/verifier.c-407-\nkernel/bpf/verifier.c:408:static const char *subprog_name(const struct bpf_verifier_env *env, int subprog)\nkernel/bpf/verifier.c-409-{\n--\nkernel/bpf/verifier.c-418-\nkernel/bpf/verifier.c:419:void bpf_mark_subprog_exc_cb(struct bpf_verifier_env *env, int subprog)\nkernel/bpf/verifier.c-420-{\n--\nkernel/bpf/verifier.c-427-\nkernel/bpf/verifier.c:428:static bool subprog_is_exc_cb(struct bpf_verifier_env *env, int subprog)\nkernel/bpf/verifier.c-429-{\n--\nkernel/bpf/verifier.c-432-\nkernel/bpf/verifier.c:433:static bool reg_may_point_to_spin_lock(const struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-434-{\n--\nkernel/bpf/verifier.c-437-\nkernel/bpf/verifier.c:438:static bool type_is_rdonly_mem(u32 type)\nkernel/bpf/verifier.c-439-{\n--\nkernel/bpf/verifier.c-442-\nkernel/bpf/verifier.c:443:static bool is_acquire_function(enum bpf_func_id func_id,\nkernel/bpf/verifier.c-444-\t\t\t\tconst struct bpf_map *map)\n--\nkernel/bpf/verifier.c-462-\nkernel/bpf/verifier.c:463:static bool is_ptr_cast_function(enum bpf_func_id func_id)\nkernel/bpf/verifier.c-464-{\n--\nkernel/bpf/verifier.c=480=static bool is_task_work_add_kfunc(u32 func_id);\nkernel/bpf/verifier.c-481-\nkernel/bpf/verifier.c:482:static bool is_sync_callback_calling_function(enum bpf_func_id func_id)\nkernel/bpf/verifier.c-483-{\n--\nkernel/bpf/verifier.c-489-\nkernel/bpf/verifier.c:490:static bool is_async_callback_calling_function(enum bpf_func_id func_id)\nkernel/bpf/verifier.c-491-{\n--\nkernel/bpf/verifier.c-494-\nkernel/bpf/verifier.c:495:static bool is_callback_calling_function(enum bpf_func_id func_id)\nkernel/bpf/verifier.c-496-{\n--\nkernel/bpf/verifier.c-500-\nkernel/bpf/verifier.c:501:bool bpf_is_sync_callback_calling_insn(struct bpf_insn *insn)\nkernel/bpf/verifier.c-502-{\n--\nkernel/bpf/verifier.c-506-\nkernel/bpf/verifier.c:507:bool bpf_is_async_callback_calling_insn(struct bpf_insn *insn)\nkernel/bpf/verifier.c-508-{\n--\nkernel/bpf/verifier.c-512-\nkernel/bpf/verifier.c:513:static bool is_async_cb_sleepable(struct bpf_verifier_env *env, struct bpf_insn *insn)\nkernel/bpf/verifier.c-514-{\n--\nkernel/bpf/verifier.c-527-\nkernel/bpf/verifier.c:528:bool bpf_is_may_goto_insn(struct bpf_insn *insn)\nkernel/bpf/verifier.c-529-{\n--\nkernel/bpf/verifier.c-532-\nkernel/bpf/verifier.c:533:static bool is_spi_bounds_valid(struct bpf_func_state *state, int spi, int nr_slots)\nkernel/bpf/verifier.c-534-{\n--\nkernel/bpf/verifier.c-546-\nkernel/bpf/verifier.c:547:static int stack_slot_obj_get_spi(struct bpf_verifier_env *env, struct bpf_reg_state *reg,\nkernel/bpf/verifier.c-548-\t\t\t const char *obj_kind, int nr_slots)\n--\nkernel/bpf/verifier.c-573-\nkernel/bpf/verifier.c:574:static int dynptr_get_spi(struct bpf_verifier_env *env, struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-575-{\n--\nkernel/bpf/verifier.c-578-\nkernel/bpf/verifier.c:579:static int iter_get_spi(struct bpf_verifier_env *env, struct bpf_reg_state *reg, int nr_slots)\nkernel/bpf/verifier.c-580-{\n--\nkernel/bpf/verifier.c-583-\nkernel/bpf/verifier.c:584:static int irq_flag_get_spi(struct bpf_verifier_env *env, struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-585-{\n--\nkernel/bpf/verifier.c-588-\nkernel/bpf/verifier.c:589:static enum bpf_dynptr_type arg_to_dynptr_type(enum bpf_arg_type arg_type)\nkernel/bpf/verifier.c-590-{\n--\nkernel/bpf/verifier.c-608-\nkernel/bpf/verifier.c:609:static enum bpf_type_flag get_dynptr_type_flag(enum bpf_dynptr_type type)\nkernel/bpf/verifier.c-610-{\n--\nkernel/bpf/verifier.c-628-\nkernel/bpf/verifier.c:629:static bool dynptr_type_referenced(enum bpf_dynptr_type type)\nkernel/bpf/verifier.c-630-{\n--\nkernel/bpf/verifier.c-633-\nkernel/bpf/verifier.c:634:static void __mark_dynptr_reg(struct bpf_reg_state *reg,\nkernel/bpf/verifier.c-635-\t\t\t enum bpf_dynptr_type type,\n--\nkernel/bpf/verifier.c-638-\nkernel/bpf/verifier.c:639:static void mark_dynptr_stack_regs(struct bpf_verifier_env *env,\nkernel/bpf/verifier.c-640-\t\t\t\t struct bpf_reg_state *sreg1,\n--\nkernel/bpf/verifier.c-649-\nkernel/bpf/verifier.c:650:static void mark_dynptr_cb_reg(struct bpf_verifier_env *env,\nkernel/bpf/verifier.c-651-\t\t\t struct bpf_reg_state *reg,\n--\nkernel/bpf/verifier.c-656-\nkernel/bpf/verifier.c:657:static int destroy_if_dynptr_stack_slot(struct bpf_verifier_env *env,\nkernel/bpf/verifier.c-658-\t\t\t\t struct bpf_func_state *state, int spi);\nkernel/bpf/verifier.c-659-\nkernel/bpf/verifier.c:660:static int mark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_state *reg,\nkernel/bpf/verifier.c-661-\t\t\t\t enum bpf_arg_type arg_type, int insn_idx,\n--\nkernel/bpf/verifier.c-728-\nkernel/bpf/verifier.c:729:static void invalidate_dynptr(struct bpf_verifier_env *env, struct bpf_stack_state *stack)\nkernel/bpf/verifier.c-730-{\n--\nkernel/bpf/verifier.c-741-\nkernel/bpf/verifier.c:742:static int unmark_stack_slots_dynptr(struct bpf_verifier_env *env, struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-743-{\n--\nkernel/bpf/verifier.c-761-\nkernel/bpf/verifier.c:762:static void __mark_reg_unknown(const struct bpf_verifier_env *env,\nkernel/bpf/verifier.c-763-\t\t\t struct bpf_reg_state *reg);\nkernel/bpf/verifier.c-764-\nkernel/bpf/verifier.c:765:static void mark_reg_invalid(const struct bpf_verifier_env *env, struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-766-{\n--\nkernel/bpf/verifier.c-772-\nkernel/bpf/verifier.c:773:static int dynptr_ref_cnt(struct bpf_verifier_env *env, int v_parent_id)\nkernel/bpf/verifier.c-774-{\n--\nkernel/bpf/verifier.c-791-\nkernel/bpf/verifier.c:792:static int destroy_if_dynptr_stack_slot(struct bpf_verifier_env *env,\nkernel/bpf/verifier.c-793-\t\t\t\t struct bpf_func_state *state, int spi)\n--\nkernel/bpf/verifier.c-829-\nkernel/bpf/verifier.c:830:static bool is_dynptr_reg_valid_uninit(struct bpf_verifier_env *env, struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-831-{\n--\nkernel/bpf/verifier.c-857-\nkernel/bpf/verifier.c:858:static bool is_dynptr_reg_valid_init(struct bpf_verifier_env *env, struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-859-{\n--\nkernel/bpf/verifier.c-886-\nkernel/bpf/verifier.c:887:static bool is_dynptr_type_expected(struct bpf_verifier_env *env, struct bpf_reg_state *reg,\nkernel/bpf/verifier.c-888-\t\t\t\t enum bpf_arg_type arg_type)\n--\nkernel/bpf/verifier.c=913=static bool is_kfunc_rcu_protected(struct bpf_call_arg_meta *meta);\nkernel/bpf/verifier.c-914-\nkernel/bpf/verifier.c:915:static int mark_stack_slots_iter(struct bpf_verifier_env *env,\nkernel/bpf/verifier.c-916-\t\t\t\t struct bpf_call_arg_meta *meta,\n--\nkernel/bpf/verifier.c-957-\nkernel/bpf/verifier.c:958:static int unmark_stack_slots_iter(struct bpf_verifier_env *env,\nkernel/bpf/verifier.c-959-\t\t\t\t struct bpf_reg_state *reg, int nr_slots)\n--\nkernel/bpf/verifier.c-985-\nkernel/bpf/verifier.c:986:static bool is_iter_reg_valid_uninit(struct bpf_verifier_env *env,\nkernel/bpf/verifier.c-987-\t\t\t\t struct bpf_reg_state *reg, int nr_slots)\n--\nkernel/bpf/verifier.c-1012-\nkernel/bpf/verifier.c:1013:static int is_iter_reg_valid_init(struct bpf_verifier_env *env, struct bpf_reg_state *reg,\nkernel/bpf/verifier.c-1014-\t\t\t\t struct btf *btf, u32 btf_id, int nr_slots)\n--\nkernel/bpf/verifier.c=1046=static int release_irq_state(struct bpf_verifier_state *state, int id);\nkernel/bpf/verifier.c-1047-\nkernel/bpf/verifier.c:1048:static int mark_stack_slot_irq_flag(struct bpf_verifier_env *env,\nkernel/bpf/verifier.c-1049-\t\t\t\t struct bpf_call_arg_meta *meta,\n--\nkernel/bpf/verifier.c-1080-\nkernel/bpf/verifier.c:1081:static int unmark_stack_slot_irq_flag(struct bpf_verifier_env *env, struct bpf_reg_state *reg,\nkernel/bpf/verifier.c-1082-\t\t\t\t int kfunc_class)\n--\nkernel/bpf/verifier.c-1130-\nkernel/bpf/verifier.c:1131:static bool is_irq_flag_reg_valid_uninit(struct bpf_verifier_env *env, struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1132-{\n--\nkernel/bpf/verifier.c-1154-\nkernel/bpf/verifier.c:1155:static int is_irq_flag_reg_valid_init(struct bpf_verifier_env *env, struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1156-{\n--\nkernel/bpf/verifier.c-1183- */\nkernel/bpf/verifier.c:1184:static bool is_stack_slot_special(const struct bpf_stack_state *stack)\nkernel/bpf/verifier.c-1185-{\n--\nkernel/bpf/verifier.c-1219- */\nkernel/bpf/verifier.c:1220:static void mark_stack_slot_misc(struct bpf_verifier_env *env, u8 *stype)\nkernel/bpf/verifier.c-1221-{\n--\nkernel/bpf/verifier.c-1228-\nkernel/bpf/verifier.c:1229:static void scrub_spilled_slot(u8 *stype)\nkernel/bpf/verifier.c-1230-{\n--\nkernel/bpf/verifier.c-1241- */\nkernel/bpf/verifier.c:1242:static void *copy_array(void *dst, const void *src, size_t n, size_t size, gfp_t flags)\nkernel/bpf/verifier.c-1243-{\n--\nkernel/bpf/verifier.c-1270- */\nkernel/bpf/verifier.c:1271:static void *realloc_array(void *arr, size_t old_n, size_t new_n, size_t size)\nkernel/bpf/verifier.c-1272-{\n--\nkernel/bpf/verifier.c-1293-\nkernel/bpf/verifier.c:1294:static int copy_reference_state(struct bpf_verifier_state *dst, const struct bpf_verifier_state *src)\nkernel/bpf/verifier.c-1295-{\n--\nkernel/bpf/verifier.c-1310-\nkernel/bpf/verifier.c:1311:static int copy_stack_state(struct bpf_func_state *dst, const struct bpf_func_state *src)\nkernel/bpf/verifier.c-1312-{\n--\nkernel/bpf/verifier.c-1335-\nkernel/bpf/verifier.c:1336:static int resize_reference_state(struct bpf_verifier_state *state, size_t n)\nkernel/bpf/verifier.c-1337-{\n--\nkernel/bpf/verifier.c-1349- */\nkernel/bpf/verifier.c:1350:static int grow_stack_state(struct bpf_verifier_env *env, struct bpf_func_state *state, int size)\nkernel/bpf/verifier.c-1351-{\n--\nkernel/bpf/verifier.c-1373-\nkernel/bpf/verifier.c:1374:static int grow_stack_arg_slots(struct bpf_verifier_env *env,\nkernel/bpf/verifier.c-1375-\t\t\t\tstruct bpf_func_state *state, int cnt)\n--\nkernel/bpf/verifier.c-1395- */\nkernel/bpf/verifier.c:1396:static struct bpf_reference_state *acquire_reference_state(struct bpf_verifier_env *env, int insn_idx)\nkernel/bpf/verifier.c-1397-{\n--\nkernel/bpf/verifier.c-1409-\nkernel/bpf/verifier.c:1410:static int acquire_reference(struct bpf_verifier_env *env, int insn_idx, int parent_id)\nkernel/bpf/verifier.c-1411-{\n--\nkernel/bpf/verifier.c-1422-\nkernel/bpf/verifier.c:1423:static int acquire_lock_state(struct bpf_verifier_env *env, int insn_idx, enum ref_state_type type,\nkernel/bpf/verifier.c-1424-\t\t\t int id, void *ptr)\n--\nkernel/bpf/verifier.c-1441-\nkernel/bpf/verifier.c:1442:static int acquire_irq_state(struct bpf_verifier_env *env, int insn_idx)\nkernel/bpf/verifier.c-1443-{\n--\nkernel/bpf/verifier.c-1456-\nkernel/bpf/verifier.c:1457:static void release_reference_state(struct bpf_verifier_state *state, int idx)\nkernel/bpf/verifier.c-1458-{\n--\nkernel/bpf/verifier.c-1475-\nkernel/bpf/verifier.c:1476:static bool find_reference_state(struct bpf_verifier_state *state, int id)\nkernel/bpf/verifier.c-1477-{\n--\nkernel/bpf/verifier.c-1489-\nkernel/bpf/verifier.c:1490:static bool reg_is_referenced(struct bpf_verifier_env *env, const struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1491-{\n--\nkernel/bpf/verifier.c-1494-\nkernel/bpf/verifier.c:1495:static int release_lock_state(struct bpf_verifier_state *state, int type, int id, void *ptr)\nkernel/bpf/verifier.c-1496-{\n--\nkernel/bpf/verifier.c-1518-\nkernel/bpf/verifier.c:1519:static int release_irq_state(struct bpf_verifier_state *state, int id)\nkernel/bpf/verifier.c-1520-{\n--\nkernel/bpf/verifier.c-1540-\nkernel/bpf/verifier.c:1541:static struct bpf_reference_state *find_lock_state(struct bpf_verifier_state *state, enum ref_state_type type,\nkernel/bpf/verifier.c-1542-\t\t\t\t\t\t int id, void *ptr)\n--\nkernel/bpf/verifier.c-1557-\nkernel/bpf/verifier.c:1558:static void free_func_state(struct bpf_func_state *state)\nkernel/bpf/verifier.c-1559-{\n--\nkernel/bpf/verifier.c-1566-\nkernel/bpf/verifier.c:1567:void bpf_clear_jmp_history(struct bpf_verifier_state *state)\nkernel/bpf/verifier.c-1568-{\n--\nkernel/bpf/verifier.c-1573-\nkernel/bpf/verifier.c:1574:void bpf_free_verifier_state(struct bpf_verifier_state *state,\nkernel/bpf/verifier.c-1575-\t\t\t bool free_self)\n--\nkernel/bpf/verifier.c-1591- */\nkernel/bpf/verifier.c:1592:static int copy_func_state(struct bpf_func_state *dst,\nkernel/bpf/verifier.c-1593-\t\t\t const struct bpf_func_state *src)\n--\nkernel/bpf/verifier.c-1598-\nkernel/bpf/verifier.c:1599:int bpf_copy_verifier_state(struct bpf_verifier_state *dst_state,\nkernel/bpf/verifier.c-1600-\t\t\t const struct bpf_verifier_state *src)\n--\nkernel/bpf/verifier.c-1647-\nkernel/bpf/verifier.c:1648:static u32 state_htab_size(struct bpf_verifier_env *env)\nkernel/bpf/verifier.c-1649-{\n--\nkernel/bpf/verifier.c-1652-\nkernel/bpf/verifier.c:1653:struct list_head *bpf_explored_state(struct bpf_verifier_env *env, int idx)\nkernel/bpf/verifier.c-1654-{\n--\nkernel/bpf/verifier.c-1660-\nkernel/bpf/verifier.c:1661:static bool same_callsites(struct bpf_verifier_state *a, struct bpf_verifier_state *b)\nkernel/bpf/verifier.c-1662-{\n--\nkernel/bpf/verifier.c-1675-\nkernel/bpf/verifier.c:1676:void bpf_free_backedges(struct bpf_scc_visit *visit)\nkernel/bpf/verifier.c-1677-{\n--\nkernel/bpf/verifier.c-1687-\nkernel/bpf/verifier.c:1688:static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx,\nkernel/bpf/verifier.c-1689-\t\t int *insn_idx, bool pop_log)\n--\nkernel/bpf/verifier.c-1716-\nkernel/bpf/verifier.c:1717:static bool error_recoverable_with_nospec(int err)\nkernel/bpf/verifier.c-1718-{\n--\nkernel/bpf/verifier.c-1728-\nkernel/bpf/verifier.c:1729:static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env,\nkernel/bpf/verifier.c-1730-\t\t\t\t\t int insn_idx, int prev_insn_idx,\n--\nkernel/bpf/verifier.c-1770-\nkernel/bpf/verifier.c:1771:static const char *reg_arg_name(struct bpf_verifier_env *env, argno_t argno)\nkernel/bpf/verifier.c-1772-{\n--\nkernel/bpf/verifier.c=1787=static const int caller_saved[CALLER_SAVED_REGS] = {\n--\nkernel/bpf/verifier.c-1791-/* This helper doesn't clear reg-\u003eid */\nkernel/bpf/verifier.c:1792:static void ___mark_reg_known(struct bpf_reg_state *reg, u64 imm)\nkernel/bpf/verifier.c-1793-{\n--\nkernel/bpf/verifier.c-1801- */\nkernel/bpf/verifier.c:1802:static void __mark_reg_known(struct bpf_reg_state *reg, u64 imm)\nkernel/bpf/verifier.c-1803-{\n--\nkernel/bpf/verifier.c-1811-\nkernel/bpf/verifier.c:1812:static void __mark_reg32_known(struct bpf_reg_state *reg, u64 imm)\nkernel/bpf/verifier.c-1813-{\n--\nkernel/bpf/verifier.c-1820- */\nkernel/bpf/verifier.c:1821:static void __mark_reg_known_zero(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1822-{\n--\nkernel/bpf/verifier.c-1825-\nkernel/bpf/verifier.c:1826:static void __mark_reg_const_zero(const struct bpf_verifier_env *env, struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1827-{\n--\nkernel/bpf/verifier.c-1835-\nkernel/bpf/verifier.c:1836:static void mark_reg_known_zero(struct bpf_verifier_env *env,\nkernel/bpf/verifier.c-1837-\t\t\t\tstruct bpf_reg_state *regs, u32 regno)\n--\nkernel/bpf/verifier.c-1841-\nkernel/bpf/verifier.c:1842:static void __mark_dynptr_reg(struct bpf_reg_state *reg, enum bpf_dynptr_type type,\nkernel/bpf/verifier.c-1843-\t\t\t bool first_slot, int id, int parent_id)\n--\nkernel/bpf/verifier.c-1861- */\nkernel/bpf/verifier.c:1862:static void refine_map_lookup_value(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1863-{\n--\nkernel/bpf/verifier.c-1883-\nkernel/bpf/verifier.c:1884:static void mark_ptr_not_null_reg(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1885-{\n--\nkernel/bpf/verifier.c-1888-\nkernel/bpf/verifier.c:1889:static void mark_reg_graph_node(struct bpf_reg_state *regs, u32 regno,\nkernel/bpf/verifier.c-1890-\t\t\t\tstruct btf_field_graph_root *ds_head)\n--\nkernel/bpf/verifier.c-1897-\nkernel/bpf/verifier.c:1898:static bool reg_is_pkt_pointer(const struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1899-{\n--\nkernel/bpf/verifier.c-1902-\nkernel/bpf/verifier.c:1903:static bool reg_is_pkt_pointer_any(const struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1904-{\n--\nkernel/bpf/verifier.c-1908-\nkernel/bpf/verifier.c:1909:static bool reg_is_dynptr_slice_pkt(const struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1910-{\n--\nkernel/bpf/verifier.c-1916-/* Unmodified PTR_TO_PACKET[_META,_END] register from ctx access. */\nkernel/bpf/verifier.c:1917:static bool reg_is_init_pkt_pointer(const struct bpf_reg_state *reg,\nkernel/bpf/verifier.c-1918-\t\t\t\t enum bpf_reg_type which)\n--\nkernel/bpf/verifier.c-1928-\nkernel/bpf/verifier.c:1929:static void __mark_reg32_unbounded(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1930-{\n--\nkernel/bpf/verifier.c-1933-\nkernel/bpf/verifier.c:1934:static void __mark_reg64_unbounded(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1935-{\n--\nkernel/bpf/verifier.c-1939-/* Reset the min/max bounds of a register */\nkernel/bpf/verifier.c:1940:static void __mark_reg_unbounded(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1941-{\n--\nkernel/bpf/verifier.c-1945-\nkernel/bpf/verifier.c:1946:static void reset_reg64_and_tnum(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1947-{\n--\nkernel/bpf/verifier.c-1951-\nkernel/bpf/verifier.c:1952:static void reset_reg32_and_tnum(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1953-{\n--\nkernel/bpf/verifier.c-1957-\nkernel/bpf/verifier.c:1958:static struct cnum32 cnum32_from_tnum(struct tnum tnum)\nkernel/bpf/verifier.c-1959-{\n--\nkernel/bpf/verifier.c-1969-\nkernel/bpf/verifier.c:1970:static struct cnum64 cnum64_from_tnum(struct tnum tnum)\nkernel/bpf/verifier.c-1971-{\n--\nkernel/bpf/verifier.c-1980-\nkernel/bpf/verifier.c:1981:static void __update_reg32_bounds(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1982-{\n--\nkernel/bpf/verifier.c-1985-\nkernel/bpf/verifier.c:1986:static void __update_reg64_bounds(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-1987-{\n--\nkernel/bpf/verifier.c-2020-\nkernel/bpf/verifier.c:2021:static void __update_reg_bounds(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-2022-{\n--\nkernel/bpf/verifier.c-2026-\nkernel/bpf/verifier.c:2027:static void deduce_bounds_32_from_64(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-2028-{\n--\nkernel/bpf/verifier.c-2031-\nkernel/bpf/verifier.c:2032:static void deduce_bounds_64_from_32(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-2033-{\n--\nkernel/bpf/verifier.c-2036-\nkernel/bpf/verifier.c:2037:static void __reg_deduce_bounds(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-2038-{\n--\nkernel/bpf/verifier.c-2043-/* Attempts to improve var_off based on unsigned min/max information */\nkernel/bpf/verifier.c:2044:static void __reg_bound_offset(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-2045-{\n--\nkernel/bpf/verifier.c=2056=static bool range_bounds_violation(struct bpf_reg_state *reg);\nkernel/bpf/verifier.c-2057-\nkernel/bpf/verifier.c:2058:static void reg_bounds_sync(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-2059-{\n--\nkernel/bpf/verifier.c-2076-\nkernel/bpf/verifier.c:2077:static bool const_tnum_range_mismatch(struct bpf_reg_state *reg)\nkernel/bpf/verifier.c-2078-{\n\n"
} |
| 9/2 | 2026/08/04 18:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 50469 output: 35 thoughts: 50 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Analyzing the New BPF Verifier Addition** Alright, so I'm noticing this new function, `mark_non_stack_access`, has popped up in `kernel/bpf/verifier.c`. My immediate instinct is to understand its purpose and how it fits into the overall BPF verification process. Given the naming convention, "non-stack access" implies a significant shift in how memory accesses are being handled. I'll need to dig into its implementation to see exactly what kind of checks it's adding. Let's start by searching the source file for references to that function, to get an understanding of its integration points, the contexts where it's being invoked, and how it might be interacting with the existing stack tracking and bounds checking logic within the verifier. I'm especially interested in whether this introduces new constraints, relaxes old ones, or addresses a specific class of vulnerabilities related to memory safety. Is this related to improved pointer arithmetic, off-stack data regions, or maybe some type of memory protection mechanism? |
| 10/2 | 2026/08/04 18:04 | tool | grepper |
0mArgs:{
"Expression": "mark_non_stack_access",
"PathPrefix": "kernel/bpf/verifier.c"
}Results: {
"Output": "kernel/bpf/verifier.c=3211=static void mark_indirect_target(struct bpf_verifier_env *env, int idx)\n--\nkernel/bpf/verifier.c-3215-\nkernel/bpf/verifier.c:3216:static void mark_non_stack_access(struct bpf_verifier_env *env, int idx)\nkernel/bpf/verifier.c-3217-{\n--\nkernel/bpf/verifier.c=6172=static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, struct bpf_reg_state *reg, argno_t argno,\n--\nkernel/bpf/verifier.c-6442-\tif (!err \u0026\u0026 reg-\u003etype != PTR_TO_STACK)\nkernel/bpf/verifier.c:6443:\t\tmark_non_stack_access(env, insn_idx);\nkernel/bpf/verifier.c-6444-\n"
} |
| 11/2 | 2026/08/04 18:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 51040 output: 38 thoughts: 112 Thoughts: **Analyzing the BPF Patch** Okay, so I'm looking at this patch and immediately I see it touches several key areas within the BPF subsystem. Specifically, it's modifying `check_mem_access` in `kernel/bpf/verifier.c`. That's interesting, as this deals with memory access verification – likely related to ensuring BPF programs don't try to read or write memory outside their allocated context. It's also making changes to `do_jit` in `arch/x86/net/bpf_jit_comp.c`, so it probably has some bearing on the JIT compiler, probably dealing with optimizations or special handling for x86 architectures. And it's also making changes in `kernel/bpf/fixups.c`. It's touching `adjust_insn_aux_data`, which suggests the patch is potentially dealing with auxiliary data associated with BPF instructions and maybe how it's handled during program modifications. Also `bpf_patch_insn_data` is being changed. Given the name, I'd say this is where I'll start digging; this is going to be my first area of deep scrutiny. |
| 12/2 | 2026/08/04 18:04 | tool | grepper |
0mArgs:{
"Expression": "bpf_patch_insn_data",
"PathPrefix": "kernel/bpf/fixups.c"
}Results: {
"Output": "kernel/bpf/fixups.c=270=static void adjust_poke_descs(struct bpf_prog *prog, u32 off, u32 len)\n--\nkernel/bpf/fixups.c-283-\nkernel/bpf/fixups.c:284:struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,\nkernel/bpf/fixups.c-285-\t\t\t\t const struct bpf_insn *patch, u32 len)\n--\nkernel/bpf/fixups.c=621=int bpf_opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,\n--\nkernel/bpf/fixups.c-709-apply_patch_buffer:\nkernel/bpf/fixups.c:710:\t\tnew_prog = bpf_patch_insn_data(env, adj_idx, patch, patch_len);\nkernel/bpf/fixups.c-711-\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c=727=int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)\n--\nkernel/bpf/fixups.c-754-\t\t\tinsn_buf[cnt++] = env-\u003eprog-\u003einsnsi[0];\nkernel/bpf/fixups.c:755:\t\t\tnew_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt);\nkernel/bpf/fixups.c-756-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-777-\t\t} else if (cnt) {\nkernel/bpf/fixups.c:778:\t\t\tnew_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt);\nkernel/bpf/fixups.c-779-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-809-\t\t\tcnt = patch - insn_buf;\nkernel/bpf/fixups.c:810:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-811-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-882-\t\t\tcnt = patch - insn_buf;\nkernel/bpf/fixups.c:883:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-884-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1012-patch_insn_buf:\nkernel/bpf/fixups.c:1013:\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1014-\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c=1490=static int add_hidden_subprog(struct bpf_verifier_env *env, struct bpf_insn *patch, int len)\n--\nkernel/bpf/fixups.c-1502-\t * ones for the hidden subprog. Hence all of the adjustment operations\nkernel/bpf/fixups.c:1503:\t * in bpf_patch_insn_data are no-ops.\nkernel/bpf/fixups.c-1504-\t */\nkernel/bpf/fixups.c:1505:\tprog = bpf_patch_insn_data(env, env-\u003eprog-\u003elen - 1, patch, len);\nkernel/bpf/fixups.c-1506-\tif (!prog)\n--\nkernel/bpf/fixups.c=1519=int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n--\nkernel/bpf/fixups.c-1589-\nkernel/bpf/fixups.c:1590:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1591-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1680-\nkernel/bpf/fixups.c:1681:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1682-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1710-\t\t\tcnt = patch - insn_buf;\nkernel/bpf/fixups.c:1711:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1712-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1730-\nkernel/bpf/fixups.c:1731:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1732-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1783-\nkernel/bpf/fixups.c:1784:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1785-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1828-\nkernel/bpf/fixups.c:1829:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1830-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1849-\nkernel/bpf/fixups.c:1850:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1851-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1870-\nkernel/bpf/fixups.c:1871:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1872-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1957-\t\t\tcnt = 3;\nkernel/bpf/fixups.c:1958:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1959-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1990-\nkernel/bpf/fixups.c:1991:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1992-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2009-\nkernel/bpf/fixups.c:2010:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-2011-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2052-\nkernel/bpf/fixups.c:2053:\t\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta,\nkernel/bpf/fixups.c-2054-\t\t\t\t\t\t\t insn_buf, cnt);\n--\nkernel/bpf/fixups.c-2135-\nkernel/bpf/fixups.c:2136:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2137-\t\t\t\t\t\t cnt);\n--\nkernel/bpf/fixups.c-2165-#endif\nkernel/bpf/fixups.c:2166:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-2167-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2184-\nkernel/bpf/fixups.c:2185:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-2186-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2218-\nkernel/bpf/fixups.c:2219:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-2220-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2250-\nkernel/bpf/fixups.c:2251:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-2252-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2276-\nkernel/bpf/fixups.c:2277:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-2278-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2292-\nkernel/bpf/fixups.c:2293:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, 1);\nkernel/bpf/fixups.c-2294-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2347-\nkernel/bpf/fixups.c:2348:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-2349-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2365-\nkernel/bpf/fixups.c:2366:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-2367-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2433-\nkernel/bpf/fixups.c:2434:\t\tnew_prog = bpf_patch_insn_data(env, subprog_start, insn_buf, cnt);\nkernel/bpf/fixups.c-2435-\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2441-\t\t * to insn after BPF_ST that inits may_goto count.\nkernel/bpf/fixups.c:2442:\t\t * Adjustment will succeed because bpf_patch_insn_data() didn't fail.\nkernel/bpf/fixups.c-2443-\t\t */\n--\nkernel/bpf/fixups.c=2471=static struct bpf_prog *inline_bpf_loop(struct bpf_verifier_env *env,\n--\nkernel/bpf/fixups.c-2532-\t*total_cnt = cnt;\nkernel/bpf/fixups.c:2533:\tnew_prog = bpf_patch_insn_data(env, position, insn_buf, cnt);\nkernel/bpf/fixups.c-2534-\tif (!new_prog)\n"
} |
| 13/2 | 2026/08/04 18:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 54570 output: 38 thoughts: 239 Thoughts: **Analyzing the Kernel's BPF Instruction Patching** Okay, so this patch is touching `bpf_patch_insn_data` within `kernel/bpf/fixups.c`. That function gets called *a lot*, which immediately raises a flag. We need to zero in on the critical symbols here. The key players are going to be `bpf_int_jit_compile` from `arch/x86/net/bpf_jit_comp.c`, `bpf_patch_insn_data` itself, of course, and also `check_mem_access` in `kernel/bpf/verifier.c`. Right, `bpf_int_jit_compile` is the primary entry point for JIT compilation, so it's central to the whole process. Now, I need to understand the scope of `bpf_patch_insn_data`. Is it static or not? Let me check its declaration... Ah, it's *not* static, so it's likely defined in `include/linux/bpf_verifier.h`, or just exported. Confirmed - it's *not* static. Good, that helps. Let's dig deeper into the source, starting with `kernel/bpf/fixups.c`. |
| 14/2 | 2026/08/04 18:04 | tool | grepper |
0mArgs:{
"Expression": "bpf_patch_insn_data",
"PathPrefix": "kernel/bpf/fixups.c"
}Results: {
"Output": "kernel/bpf/fixups.c=270=static void adjust_poke_descs(struct bpf_prog *prog, u32 off, u32 len)\n--\nkernel/bpf/fixups.c-283-\nkernel/bpf/fixups.c:284:struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 off,\nkernel/bpf/fixups.c-285-\t\t\t\t const struct bpf_insn *patch, u32 len)\n--\nkernel/bpf/fixups.c=621=int bpf_opt_subreg_zext_lo32_rnd_hi32(struct bpf_verifier_env *env,\n--\nkernel/bpf/fixups.c-709-apply_patch_buffer:\nkernel/bpf/fixups.c:710:\t\tnew_prog = bpf_patch_insn_data(env, adj_idx, patch, patch_len);\nkernel/bpf/fixups.c-711-\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c=727=int bpf_convert_ctx_accesses(struct bpf_verifier_env *env)\n--\nkernel/bpf/fixups.c-754-\t\t\tinsn_buf[cnt++] = env-\u003eprog-\u003einsnsi[0];\nkernel/bpf/fixups.c:755:\t\t\tnew_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt);\nkernel/bpf/fixups.c-756-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-777-\t\t} else if (cnt) {\nkernel/bpf/fixups.c:778:\t\t\tnew_prog = bpf_patch_insn_data(env, 0, insn_buf, cnt);\nkernel/bpf/fixups.c-779-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-809-\t\t\tcnt = patch - insn_buf;\nkernel/bpf/fixups.c:810:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-811-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-882-\t\t\tcnt = patch - insn_buf;\nkernel/bpf/fixups.c:883:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-884-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1012-patch_insn_buf:\nkernel/bpf/fixups.c:1013:\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1014-\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c=1490=static int add_hidden_subprog(struct bpf_verifier_env *env, struct bpf_insn *patch, int len)\n--\nkernel/bpf/fixups.c-1502-\t * ones for the hidden subprog. Hence all of the adjustment operations\nkernel/bpf/fixups.c:1503:\t * in bpf_patch_insn_data are no-ops.\nkernel/bpf/fixups.c-1504-\t */\nkernel/bpf/fixups.c:1505:\tprog = bpf_patch_insn_data(env, env-\u003eprog-\u003elen - 1, patch, len);\nkernel/bpf/fixups.c-1506-\tif (!prog)\n--\nkernel/bpf/fixups.c=1519=int bpf_do_misc_fixups(struct bpf_verifier_env *env)\n--\nkernel/bpf/fixups.c-1589-\nkernel/bpf/fixups.c:1590:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1591-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1680-\nkernel/bpf/fixups.c:1681:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1682-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1710-\t\t\tcnt = patch - insn_buf;\nkernel/bpf/fixups.c:1711:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1712-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1730-\nkernel/bpf/fixups.c:1731:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1732-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1783-\nkernel/bpf/fixups.c:1784:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1785-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1828-\nkernel/bpf/fixups.c:1829:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1830-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1849-\nkernel/bpf/fixups.c:1850:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1851-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1870-\nkernel/bpf/fixups.c:1871:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1872-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1957-\t\t\tcnt = 3;\nkernel/bpf/fixups.c:1958:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1959-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-1990-\nkernel/bpf/fixups.c:1991:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-1992-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2009-\nkernel/bpf/fixups.c:2010:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-2011-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2052-\nkernel/bpf/fixups.c:2053:\t\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta,\nkernel/bpf/fixups.c-2054-\t\t\t\t\t\t\t insn_buf, cnt);\n--\nkernel/bpf/fixups.c-2135-\nkernel/bpf/fixups.c:2136:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf,\nkernel/bpf/fixups.c-2137-\t\t\t\t\t\t cnt);\n--\nkernel/bpf/fixups.c-2165-#endif\nkernel/bpf/fixups.c:2166:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-2167-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2184-\nkernel/bpf/fixups.c:2185:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-2186-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2218-\nkernel/bpf/fixups.c:2219:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-2220-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2250-\nkernel/bpf/fixups.c:2251:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-2252-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2276-\nkernel/bpf/fixups.c:2277:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-2278-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2292-\nkernel/bpf/fixups.c:2293:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, 1);\nkernel/bpf/fixups.c-2294-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2347-\nkernel/bpf/fixups.c:2348:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-2349-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2365-\nkernel/bpf/fixups.c:2366:\t\t\tnew_prog = bpf_patch_insn_data(env, i + delta, insn_buf, cnt);\nkernel/bpf/fixups.c-2367-\t\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2433-\nkernel/bpf/fixups.c:2434:\t\tnew_prog = bpf_patch_insn_data(env, subprog_start, insn_buf, cnt);\nkernel/bpf/fixups.c-2435-\t\tif (!new_prog)\n--\nkernel/bpf/fixups.c-2441-\t\t * to insn after BPF_ST that inits may_goto count.\nkernel/bpf/fixups.c:2442:\t\t * Adjustment will succeed because bpf_patch_insn_data() didn't fail.\nkernel/bpf/fixups.c-2443-\t\t */\n--\nkernel/bpf/fixups.c=2471=static struct bpf_prog *inline_bpf_loop(struct bpf_verifier_env *env,\n--\nkernel/bpf/fixups.c-2532-\t*total_cnt = cnt;\nkernel/bpf/fixups.c:2533:\tnew_prog = bpf_patch_insn_data(env, position, insn_buf, cnt);\nkernel/bpf/fixups.c-2534-\tif (!new_prog)\n"
} |
| 15/2 | 2026/08/04 18:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 58252 output: 33 thoughts: 162 Thoughts: **Analyzing BPF JIT Compilation Components** Okay, let's break this down. My primary targets here are `bpf_int_jit_compile`, `bpf_patch_insn_data`, and `check_mem_access`. I need to understand how these pieces fit together. First, I'll check on the visibility of `bpf_patch_insn_data`. It looks like it's defined globally in `kernel/bpf/fixups.c`, so that's good to know. Now, `check_mem_access` – ah, it's static within `kernel/bpf/verifier.c`. Because it's static, I won't be able to directly use it. I'll need to look for functions that call it. That means I need to use `bpf_check` or `do_check`, since those are likely the wrappers. My next step will be to dive into `kernel/bpf/verifier.c` and figure out how `check_mem_access` is being used and called by the non-static functions. |
| 16/2 | 2026/08/04 18:04 | tool | grepper |
0mArgs:{
"Expression": "check_mem_access",
"PathPrefix": "kernel/bpf/verifier.c"
}Results: {
"Output": "kernel/bpf/verifier.c=57=static bool bpf_global_percpu_ma_set;\n--\nkernel/bpf/verifier.c-104- * can be: PTR_TO_MAP_VALUE, PTR_TO_CTX, PTR_TO_STACK, PTR_TO_SOCKET. These are\nkernel/bpf/verifier.c:105: * four pointer types recognized by check_mem_access() function.\nkernel/bpf/verifier.c-106- *\n--\nkernel/bpf/verifier.c=830=static bool is_dynptr_reg_valid_uninit(struct bpf_verifier_env *env, struct bpf_reg_state *reg)\n--\nkernel/bpf/verifier.c-840-\t * error because this just means the stack state hasn't been updated yet.\nkernel/bpf/verifier.c:841:\t * We will do check_mem_access to check and update stack bounds later.\nkernel/bpf/verifier.c-842-\t */\n--\nkernel/bpf/verifier.c=986=static bool is_iter_reg_valid_uninit(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-992-\t/* For -ERANGE (i.e. spi not falling into allocated stack slots), we\nkernel/bpf/verifier.c:993:\t * will do check_mem_access to check and update stack bounds later, so\nkernel/bpf/verifier.c-994-\t * return true for that case.\n--\nkernel/bpf/verifier.c=1131=static bool is_irq_flag_reg_valid_uninit(struct bpf_verifier_env *env, struct bpf_reg_state *reg)\n--\nkernel/bpf/verifier.c-1137-\t/* For -ERANGE (i.e. spi not falling into allocated stack slots), we\nkernel/bpf/verifier.c:1138:\t * will do check_mem_access to check and update stack bounds later, so\nkernel/bpf/verifier.c-1139-\t * return true for that case.\n--\nkernel/bpf/verifier.c=3487=static void scrub_special_slot(struct bpf_func_state *state, int spi)\n--\nkernel/bpf/verifier.c-3499-/* check_stack_{read,write}_fixed_off functions track spill/fill of registers,\nkernel/bpf/verifier.c:3500: * stack boundary and alignment are checked in check_mem_access()\nkernel/bpf/verifier.c-3501- */\n--\nkernel/bpf/verifier.c=4219=static int check_map_access_type(struct bpf_verifier_env *env, struct bpf_reg_state *reg,\n--\nkernel/bpf/verifier.c-4240-/* check read/write into memory region (e.g., map value, ringbuf sample, etc) */\nkernel/bpf/verifier.c:4241:static int __check_mem_access(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,\nkernel/bpf/verifier.c-4242-\t\t\t int off, int size, u32 mem_size,\n--\nkernel/bpf/verifier.c=4279=static int check_mem_region_access(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,\n--\nkernel/bpf/verifier.c-4302-\t}\nkernel/bpf/verifier.c:4303:\terr = __check_mem_access(env, reg, argno, reg_smin(reg) + off, size,\nkernel/bpf/verifier.c-4304-\t\t\t\t mem_size, zero_size_allowed);\n--\nkernel/bpf/verifier.c-4319-\t}\nkernel/bpf/verifier.c:4320:\terr = __check_mem_access(env, reg, argno, reg_umax(reg) + off, size,\nkernel/bpf/verifier.c-4321-\t\t\t\t mem_size, zero_size_allowed);\n--\nkernel/bpf/verifier.c=4730=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-4743-\nkernel/bpf/verifier.c:4744:\t/* __check_mem_access has made sure \"off + size - 1\" is within u16.\nkernel/bpf/verifier.c-4745-\t * reg_umax(reg) can't be bigger than MAX_PACKET_OFF which is 0xffff,\nkernel/bpf/verifier.c-4746-\t * otherwise find_good_pkt_pointers would have refused to set range info\nkernel/bpf/verifier.c:4747:\t * that __check_mem_access would have rejected this pkt access.\nkernel/bpf/verifier.c-4748-\t * Therefore, \"off + reg_umax(reg) + size - 1\" won't overflow u32.\n--\nkernel/bpf/verifier.c=6149=static void add_scalar_to_reg(struct bpf_reg_state *dst_reg, s64 val)\n--\nkernel/bpf/verifier.c-6171- */\nkernel/bpf/verifier.c:6172: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-6173-\t\t\t int off, int bpf_size, enum bpf_access_type t,\n--\nkernel/bpf/verifier.c=6451=static int check_load_mem(struct bpf_verifier_env *env, struct bpf_insn *insn,\n--\nkernel/bpf/verifier.c-6483-\t */\nkernel/bpf/verifier.c:6484:\terr = check_mem_access(env, env-\u003einsn_idx, regs + insn-\u003esrc_reg, argno_from_reg(insn-\u003esrc_reg), insn-\u003eoff,\nkernel/bpf/verifier.c-6485-\t\t\t BPF_SIZE(insn-\u003ecode), BPF_READ, insn-\u003edst_reg,\n--\nkernel/bpf/verifier.c=6494=static int check_store_reg(struct bpf_verifier_env *env, struct bpf_insn *insn,\n--\nkernel/bpf/verifier.c-6523-\t/* Check if (dst_reg + off) is writeable. */\nkernel/bpf/verifier.c:6524:\terr = check_mem_access(env, env-\u003einsn_idx, regs + insn-\u003edst_reg, argno_from_reg(insn-\u003edst_reg), insn-\u003eoff,\nkernel/bpf/verifier.c-6525-\t\t\t BPF_SIZE(insn-\u003ecode), BPF_WRITE, insn-\u003esrc_reg,\n--\nkernel/bpf/verifier.c=6532=static int check_atomic_rmw(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-6601-\t */\nkernel/bpf/verifier.c:6602:\terr = check_mem_access(env, env-\u003einsn_idx, dst_reg, argno_from_reg(insn-\u003edst_reg), insn-\u003eoff,\nkernel/bpf/verifier.c-6603-\t\t\t BPF_SIZE(insn-\u003ecode), BPF_READ, -1, true, false);\nkernel/bpf/verifier.c-6604-\tif (!err \u0026\u0026 load_reg \u003e= 0)\nkernel/bpf/verifier.c:6605:\t\terr = check_mem_access(env, env-\u003einsn_idx, dst_reg, argno_from_reg(insn-\u003edst_reg),\nkernel/bpf/verifier.c-6606-\t\t\t\t insn-\u003eoff, BPF_SIZE(insn-\u003ecode),\n--\nkernel/bpf/verifier.c-6616-\t/* Check whether we can write into the same memory. */\nkernel/bpf/verifier.c:6617:\terr = check_mem_access(env, env-\u003einsn_idx, dst_reg, argno_from_reg(insn-\u003edst_reg), insn-\u003eoff,\nkernel/bpf/verifier.c-6618-\t\t\t BPF_SIZE(insn-\u003ecode), BPF_WRITE, -1, true, false);\n--\nkernel/bpf/verifier.c=7354=static int process_dynptr_func(struct bpf_verifier_env *env, struct bpf_reg_state *reg,\n--\nkernel/bpf/verifier.c-7390-\t\tfor (i = 0; i \u003c BPF_DYNPTR_SIZE; i += 8) {\nkernel/bpf/verifier.c:7391:\t\t\terr = check_mem_access(env, insn_idx, reg, argno,\nkernel/bpf/verifier.c-7392-\t\t\t\t\t i, BPF_DW, BPF_WRITE, -1, false, false);\n--\nkernel/bpf/verifier.c=7474=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-7511-\t\tfor (i = 0; i \u003c nr_slots * 8; i += BPF_REG_SIZE) {\nkernel/bpf/verifier.c:7512:\t\t\terr = check_mem_access(env, insn_idx, reg, argno,\nkernel/bpf/verifier.c-7513-\t\t\t\t\t i, BPF_DW, BPF_WRITE, -1, false, false);\n--\nkernel/bpf/verifier.c=10304=static int check_helper_call(struct bpf_verifier_env *env, struct bpf_insn *insn,\n--\nkernel/bpf/verifier.c-10390-\tfor (i = 0; i \u003c meta.arg_raw_mem.size; i++) {\nkernel/bpf/verifier.c:10391:\t\terr = check_mem_access(env, insn_idx, regs + meta.arg_raw_mem.regno,\nkernel/bpf/verifier.c-10392-\t\t\t\t argno_from_reg(meta.arg_raw_mem.regno), i, BPF_B,\n--\nkernel/bpf/verifier.c-10641-\t\t\t\t * it will confuse the check of PTR_TO_BTF_ID in\nkernel/bpf/verifier.c:10642:\t\t\t\t * check_mem_access().\nkernel/bpf/verifier.c-10643-\t\t\t\t */\n--\nkernel/bpf/verifier.c=11622=static int process_irq_flag(struct bpf_verifier_env *env, struct bpf_reg_state *reg, argno_t argno,\n--\nkernel/bpf/verifier.c-11649-\nkernel/bpf/verifier.c:11650:\t\terr = check_mem_access(env, env-\u003einsn_idx, reg, argno, 0, BPF_DW,\nkernel/bpf/verifier.c-11651-\t\t\t\t BPF_WRITE, -1, false, false);\n--\nkernel/bpf/verifier.c=17375=static int do_check_insn(struct bpf_verifier_env *env, bool *do_print_state)\n--\nkernel/bpf/verifier.c-17412-\nkernel/bpf/verifier.c:17413:\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-17414-\t\t\t\t insn-\u003eoff, BPF_SIZE(insn-\u003ecode),\n"
} |
| 17/2 | 2026/08/04 18:04 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 61771 output: 32 thoughts: 1163 Thoughts: **My Analysis of the KASAN eBPF JIT Instrumentation Patch** Okay, let's break this down. My initial assessment is that this patch introduces KASAN instrumentation into the eBPF JIT compiler for the x86_64 architecture. The core of this seems to be modifications to the JIT compiler itself to inject KASAN checks for memory accesses, along with updates to the BPF verifier and fixup mechanisms to correctly track non-stack memory accesses. This is a pretty significant functional change because it directly impacts core kernel logic related to the BPF JIT and verifier, and the attack surface is exposed to userspace through BPF program loading and execution. That *screams* fuzzing candidate to me. It's crucial to verify the JIT emits correct KASAN checks and doesn't introduce any regressions or crashes, or worse, bypasses. Now, let's pinpoint the key symbols I need to focus on during verification. I started by looking for call chains and entry points. I identified a few promising candidates. `check_mem_access` is called from several places: `check_load_mem`, `check_store_reg`, `check_atomic_rmw`, `process_dynptr_func`, `process_iter_arg`, `check_helper_call`, `process_irq_flag`, and `do_check_insn`. Since `check_mem_access` is static, that's not ideal as a primary focus. Therefore, I need to look at its callers, or other entry points to the code. After a quick scan through the code, I think the focus symbols should be: * `bpf_int_jit_compile` (from `arch/x86/net/bpf_jit_comp.c`) - This is where the JIT compilation itself happens and where the instrumentation is added. * `bpf_patch_insn_data` (from `kernel/bpf/fixups.c`) - This is the fixup logic which is modified to handle the new KASAN checks. * `do_check_insn` or, as a more general entrypoint, `bpf_check` (from `kernel/bpf/verifier.c`) - `bpf_check` is the main entry point to the verifier, and `do_check_insn` is one of its callers, which is a key part of the flow. Next, I need to ensure the correct kernel configurations are enabled. From my review of the code and the `Kconfig` files, I believe the following are essential: * `BPF_JIT` - This is fundamental for the JIT compiler to be active. * `KASAN_GENERIC` - This enables the generic KASAN infrastructure. `KASAN_GENERIC` depends on `HAVE_ARCH_KASAN` and `CC_HAS_KASAN_GENERIC`, but those seem to be implied by `KASAN_GENERIC` itself in most cases. * `KASAN_VMALLOC` - This is probably needed for VM allocation in BPF. * `BPF_SYSCALL` - Probably required for BPF functionality in general. Since `HAVE_EBPF_JIT_KASAN` is selected if `X86_64` is set, I don't need to explicitly enable it. In summary, this patch is a functional change involving core kernel components (eBPF JIT and verifier), with a direct userspace interface, and therefore requires thorough fuzzing. I need to focus on `bpf_int_jit_compile`, `bpf_patch_insn_data`, and `bpf_check` while verifying the configurations `BPF_JIT`, `KASAN_GENERIC`, `KASAN_VMALLOC`, and `BPF_SYSCALL` are enabled. |
| 18/2 | 2026/08/04 18:05 | tool | grepper |
0mArgs:{
"Expression": "bpf_check",
"PathPrefix": "kernel/bpf/verifier.c"
}Results: {
"Output": "kernel/bpf/verifier.c=57=static bool bpf_global_percpu_ma_set;\nkernel/bpf/verifier.c-58-\nkernel/bpf/verifier.c:59:/* bpf_check() is a static code analyzer that walks eBPF program\nkernel/bpf/verifier.c-60- * instruction by instruction and updates register/stack state.\n--\nkernel/bpf/verifier.c=12087=static int process_kf_arg_ptr_to_rbtree_node(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-12098- * LSM hooks and iters (both sleepable and non-sleepable) are safe.\nkernel/bpf/verifier.c:12099: * Any sleepable progs are also safe since bpf_check_attach_target() enforce\nkernel/bpf/verifier.c-12100- * them can only be attached to some specific hook points.\n--\nkernel/bpf/verifier.c=19118=static bool attach_uses_trampoline_retval(enum bpf_attach_type type)\n--\nkernel/bpf/verifier.c-19131-\nkernel/bpf/verifier.c:19132:int bpf_check_attach_target(struct bpf_verifier_log *log,\nkernel/bpf/verifier.c-19133-\t\t\t const struct bpf_prog *prog,\n--\nkernel/bpf/verifier.c=19545=static int check_attach_btf_id(struct bpf_verifier_env *env)\n--\nkernel/bpf/verifier.c-19575-\nkernel/bpf/verifier.c:19576:\tret = bpf_check_attach_target(\u0026env-\u003elog, prog, tgt_prog, btf_id, \u0026tgt_info);\nkernel/bpf/verifier.c-19577-\tif (ret)\n--\nkernel/bpf/verifier.c-19644-\nkernel/bpf/verifier.c:19645:int bpf_check_attach_btf_id_multi(struct btf *btf, struct bpf_prog *prog, u32 btf_id,\nkernel/bpf/verifier.c-19646-\t\t\t\t struct bpf_attach_target_info *tgt_info)\n--\nkernel/bpf/verifier.c=20033=static enum bpf_sig_keyring bpf_classify_keyring(s32 keyring_id)\n--\nkernel/bpf/verifier.c-20047-/*\nkernel/bpf/verifier.c:20048: * Verify the PKCS#7 signature of a loaded program. Called from bpf_check()\nkernel/bpf/verifier.c-20049- * once the program's metadata maps have been resolved into used_maps, so\n--\nkernel/bpf/verifier.c=20056=static int bpf_prog_verify_signature(struct bpf_verifier_env *env,\n--\nkernel/bpf/verifier.c-20172-\nkernel/bpf/verifier.c:20173:int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,\nkernel/bpf/verifier.c-20174-\t struct bpf_log_attr *attr_log)\n--\nkernel/bpf/verifier.c-20187-\t/* 'struct bpf_verifier_env' can be global, but since it's not small,\nkernel/bpf/verifier.c:20188:\t * allocate/free it every time bpf_check() is called\nkernel/bpf/verifier.c-20189-\t */\n--\nkernel/bpf/verifier.c-20280-\nkernel/bpf/verifier.c:20281:\tret = bpf_check_btf_info_early(env, attr, uattr);\nkernel/bpf/verifier.c-20282-\tif (ret \u003c 0)\n--\nkernel/bpf/verifier.c-20292-\nkernel/bpf/verifier.c:20293:\tret = bpf_check_btf_info(env, attr, uattr);\nkernel/bpf/verifier.c-20294-\tif (ret \u003c 0)\n--\nkernel/bpf/verifier.c-20306-\nkernel/bpf/verifier.c:20307:\tret = bpf_check_cfg(env);\nkernel/bpf/verifier.c-20308-\tif (ret \u003c 0)\n"
} |
| 19/2 | 2026/08/04 18:05 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 65003 output: 204 thoughts: 308 Thoughts: **Analyzing Recent BPF JIT Compiler Changes** Okay, so I need to get a handle on the recent modifications to the BPF JIT compiler. The obvious starting point is `bpf_check` – that's the primary entry point, so any changes there are likely to have ripple effects. To zero in on the core of the updates, I'm going to focus on key symbols. Specifically, `bpf_int_jit_compile`, `bpf_patch_insn_data`, and, of course, `bpf_check` itself. Let's begin by reviewing the changes to `arch/x86/net/bpf_jit_comp.c`. It looks like they've introduced `emit_kasan_check` – that suggests a new integration with KASAN, which is definitely worth a closer look for memory safety implications. Also, `emit_st` is new. There's also some modification to `emit_atomic_ld_st` and `do_jit`. I think `bpf_int_jit_compile` is a good place to start digging deeper. Next, I need to see what's happening in `kernel/bpf/fixups.c`. Here, it looks like `adjust_insn_aux_data` and `bpf_patch_insn_data` have been modified. This seems related to instruction patching or data adjustment during the JIT process. Finally, let's examine the `kernel/bpf/verifier.c` changes. I see `mark_non_stack_access` is new, which is a significant addition. It's likely related to security and sandboxing; the verifier is a critical component. There's also a modification to `check_mem_access`, which is something to pay close attention to, since this is directly impacting memory access security. So, to summarize, `bpf_check`, `bpf_patch_insn_data`, and `bpf_int_jit_compile` are definitely my primary focus points. I'll need to drill down into the KASAN integration and the memory access validation. Regarding configuration options, I'll need to remember to keep an eye on `BPF_JIT`, `KASAN_GENERIC`, and `KASAN_VMALLOC` as I analyze this further, since their states will impact the behavior. Okay, I think I have a clear direction now. Time to use the `set-results` tool and start digging into the details. |
| 20/2 | 2026/08/04 18:05 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"BPF_JIT",
"KASAN_GENERIC",
"KASAN_VMALLOC"
],
"FocusSymbols": [
"bpf_int_jit_compile",
"bpf_patch_insn_data",
"bpf_check"
],
"Reasoning": "The patch adds KASAN instrumentation to the eBPF JIT compiler on x86_64. It modifies the JIT compiler to emit KASAN checks for memory accesses, and updates the BPF verifier and fixup logic to track whether instructions access non-stack memory. This is a functional change to core kernel logic (BPF JIT and verifier) that can be reached from user space by loading and executing BPF programs. Fuzzing is highly recommended to ensure the JIT emits correct KASAN checks and doesn't introduce regressions or crashes.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"BPF_JIT",
"KASAN_GENERIC",
"KASAN_VMALLOC"
],
"FocusSymbols": [
"bpf_int_jit_compile",
"bpf_patch_insn_data",
"bpf_check"
],
"Reasoning": "The patch adds KASAN instrumentation to the eBPF JIT compiler on x86_64. It modifies the JIT compiler to emit KASAN checks for memory accesses, and updates the BPF verifier and fixup logic to track whether instructions access non-stack memory. This is a functional change to core kernel logic (BPF JIT and verifier) that can be reached from user space by loading and executing BPF programs. Fuzzing is highly recommended to ensure the JIT emits correct KASAN checks and doesn't introduce regressions or crashes.",
"WorthFuzzing": true
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|