Kasan for JIT: CI test run v5#12674
Open
Tropicao wants to merge 11 commits into
Open
Conversation
a1a89b3 to
76842fa
Compare
a86433a to
ef46468
Compare
ef46468 to
7d1432f
Compare
7d1432f to
f42b070
Compare
When the verifier patches an ebpf program with bpf_patch_insn_data, it then calls adjust_insn_aux_data to make sure that insn_aux_data takes into account the newly inserted patch. Some of the data offset is pretty straightforward to deduce, it is for example the case for indirect_target, as any patch affecting indirect calls will systematically move the original instruction to the end of the new patch. In order to introduce KASAN support for eBPF JIT, we need to mark any load/store instruction that accesses non-stack memory, but updating this new marking after a patch is not as straightforward as for indirect calls: the original BPF_ST/BPF_STX/BPF_LDX can be at the beginning, at the end or somewhere in the middle of the new patch: we then need some additional info to properly update this marking. Add a new parameter to bpf_patch_insn_data and adjust_insn_aux_data to convey the info about the new location in the patch of the original instruction. This info does not always make sense depending on the generated patch (eg, some bpf helpers being inlined by the verifier, and so turned into multiple new instructions without any remaining BPF_CALL), the parameter can then be set to -1. It will be used in next patches to properly handle insn_aux_data adjustment for the new KASAN instrumentation Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com> --- Changes in v5: - fix a few incorrect offset values Changes in v4: - define insn_off_in_patch inside convert_ctx_accesses main loop to avoid old value leakage Changes in v3: - new patch
In order to prepare to emit KASAN checks in JITed programs, JIT compilers need to be aware about whether some load/store instructions are targeting the bpf program stack, as those should not be monitored (we already have guard pages for that, and it is difficult anyway to correctly monitor any kind of data passed on stack). To support this need, make the BPF verifier mark the instructions depending on whether they could access or not memory other than stack. As different states in the verifier could lead to different memory types for the same access, just marking an instruction as accessing stack only is not enough (it could be some other memory type in another verifier state), so the algorithm rather sets by default any load/store instruction as stack only, and if _any_ state leads to any memory access type other than PTR_TO_STACK, it overrides this setting. It also takes care about shifting back the instruction marking in adjust_insn_aux_data if the verifier patches instructions. However, if the verifier generates new BPF_ST/BPF_STX/BPF_LDX while patching some instructions, those new ones are systematically marked as non-stack-accessing: this may over-instrument a few memory accessing instructions, but it allows making sure that we will not miss accidentally any. Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com> --- Changes in v5: - fix incorrect marking for single instruction patch Changes in v4: - include BPF_ATOMIC in is_mem_insn - correctly mark instructions in adjust_insn_aux_data if patch generates a single instruction (ie replace an instruction) Changes in v3: - drop getter - drop cBPF handling - update marking shifting logic to track more precisely orignal instructions - systematically mark newly generated instructions as non-stack accessing Changes in v2: - invert marking logic to cover possible different reg types when the verifier covers different states - add a best-effort processing for classical bpf programs, inspecting directly src and dst registers since we don't have verifier env - make sure to keep marking in sync with prog when it is patched by verifier
Add a new Kconfig option CONFIG_BPF_JIT_KASAN that automatically enables generic KASAN (Kernel Address SANitizer) memory access checks for JIT-compiled BPF programs as well, when both KASAN (and more specifically, generic KASAN with KASAN_VMALLOC) and JIT compiler are enabled. This new Kconfig is not a user selectable one: it is either automatically enabled if KASAN is enabled on a compatible platform. When enabled, the JIT compiler will emit shadow memory checks before memory loads and stores to detect use-after-free or out-of-bounds accesses at runtime. The option is gated behind HAVE_EBPF_JIT_KASAN, as it needs proper arch-specific implementation. Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com> --- Changes in v4: - reorganize dependencies (Andrey) - drop VMAP_STACK dependency Changes in v2: - add dependency on kasan for vmalloc and vmalloc'ed stack
Add the emit_kasan_check() function that emits KASAN shadow memory
checks before memory accesses in JIT-compiled BPF programs. The
implementation relies on the existing __asan_{load,store}X functions
from KASAN subsystem. The helper:
- ensures that the kasan instrumention is actually needed: if the
instruction being processed accesses the program stack, we skip the
instrumentation, as those accesses are already protected with page
guards
- saves registers. This includes caller-saved registers, but also
temporary registers, as those were possibly used by the
affected program. Theoretically, r10 and r11 should be saved as well,
but the number of called function and their scope being limited, they
are skipped for the sake of reducing the overhead
- computes the accessed address and stores it in %rdi
- calls the relevant function, depending on the instruction being a load
or a store, and the size of the access.
- restores registers
The special care needed when inserting this instrumentation comes at the
cost of a non negligeable increase in JITed code size. For example, a
bare
mov 0x0(%si),rbx # Load in rbx content at address stored in rsi
becomes
push %rax
push %rcx
push %rdx
push %rsi
push %rdi
push %r8
push %r9
mov %rsi,%rdi
call 0xffffffff81da0a60 <__asan_load8>
pop %r9
pop %r8
pop %rdi
pop %rsi
pop %rdx
pop %rcx
pop %rax
mov 0x0(%rsi),rbx
Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com>
---
Changes in v3:
- skip kasan instrumentation if there is no verifier env (cBPF)
- move helper up in the file
Changes in v2:
- move asan functions declaration directly into jit compiler, and guard
them with IS_ENABLED
- remove faulty stack alignment, no arg is passed to kasan funcs on the
stack anyway
- make sure to emit call depth accounting code
- do not save unneeded registers
- update helper signature to let caller configure some values (eg:
is_write)
In order to prepare for KASAN checks insertion before every memory-related load or store, group all BPF_ST instructions that indeed access memory in a single helper to allow instrumenting those in one call, rather than having to instrument all cases individually. Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com> --- Changes in v3: - new patch
Insert KASAN shadow memory checks before memory load and store operations in JIT-compiled BPF programs. This helps detect memory safety bugs such as use-after-free and out-of-bounds accesses at runtime. The main instructions being targeted are BPF_ST, BPF_STX and BPF_LDX, but not all of them are being instrumented: - if the load/store instruction is in fact accessing the program stack, emit_kasan_check silently skips the instrumentation, as we can already benefit from guard pages to monitor stack accesses. - if the load/store instruction is a BPF_PROBE_MEM or a BPF_PROBE_ATOMIC instruction, we do not instrument it, as the passed address can fault (hence the custom fault management with BPF_PROBE_XXX instructions), and so the corresponding kasan check could fault as well. Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com> --- Changes in v5: - change access type (read -> write) for atomic RMW check Changes in v4: - refactor BPF_FETCH handling Changes in v3: - fix LLVM23 build failure Changes in v2: - support BPF_ATOMICS - support BPF_ST - make sure to systematically pass correct instruction to kasan check
Mark x86 as supporting KASAN checks in JITed programs so that the corresponding JIT compiler inserts checks on the translated instructions. Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com>
Add two simple helpers to allow checking whether KASAN for eBPF tests should be executed: - one helper to check if BPF_JIT_KASAN is enabled in kernel configuration - one helper to check if the kernel is running with kasan_multi_shot (otherwise only the first test will be able to trigger a report) Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com> --- Changes in v5: - add kasan_multi_shot helper Changes in v2: - fix condition
Move set_bpf_jit_harden to testing helpers so that other selftests can change the hardening configuration without re-implementing a helper. Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com> --- Changes in v5: - check set_bpf_jit_harden return code in cleanup path Changes in v3: - new patch
Add a basic KASAN test runner that loads and test-run programs that can trigger memory management bugs. The test captures kernel logs and ensure that the expected KASAN splat is emitted by searching for the corresponding first lines in the report, hence validated that the needed instrumentation has been inserted by the JIT compiler before the relevant memory accesses. To allow each test to trigger the expected report, the kernel must run with the kasan_multi_shot configuration. The runner covers different cases and settings: in the nominal case, it validates kasan reports on basic instructions (on all supported accesses sizes) but also when report _should not_ be emitted (eg: for accesses on program stack). The runner also comes with a few specialized tests that are then not executed for all sizes/locations: - specific atomic ops - test for instructions involving different verifier states, with some states flagging memory as stack, and other states as non-stack memory - tests that validate the stack marking shifting when a patch is emitted by the verifier (zext/rnd_hi32, constant blindind) A few of those tests depends on cpuv4 (load_acquire and store_release). # ./test_progs -a kasan kernel-patches#165/1 kasan/st_1_not_on_stack:OK kernel-patches#165/2 kasan/st_1_on_stack:OK kernel-patches#165/3 kasan/st_2_not_on_stack:OK kernel-patches#165/4 kasan/st_2_on_stack:OK kernel-patches#165/5 kasan/st_4_not_on_stack:OK kernel-patches#165/6 kasan/st_4_on_stack:OK kernel-patches#165/7 kasan/st_8_not_on_stack:OK kernel-patches#165/8 kasan/st_8_on_stack:OK kernel-patches#165/9 kasan/stx_1_not_on_stack:OK kernel-patches#165/10 kasan/stx_1_on_stack:OK kernel-patches#165/11 kasan/stx_2_not_on_stack:OK kernel-patches#165/12 kasan/stx_2_on_stack:OK kernel-patches#165/13 kasan/stx_4_not_on_stack:OK kernel-patches#165/14 kasan/stx_4_on_stack:OK kernel-patches#165/15 kasan/stx_8_not_on_stack:OK kernel-patches#165/16 kasan/stx_8_on_stack:OK kernel-patches#165/17 kasan/ldx_1_not_on_stack:OK kernel-patches#165/18 kasan/ldx_1_on_stack:OK kernel-patches#165/19 kasan/ldx_2_not_on_stack:OK kernel-patches#165/20 kasan/ldx_2_on_stack:OK kernel-patches#165/21 kasan/ldx_4_not_on_stack:OK kernel-patches#165/22 kasan/ldx_4_on_stack:OK kernel-patches#165/23 kasan/ldx_8_not_on_stack:OK kernel-patches#165/24 kasan/ldx_8_on_stack:OK kernel-patches#165/25 kasan/simple_atomic_4_not_on_stack:OK kernel-patches#165/26 kasan/simple_atomic_4_on_stack:OK kernel-patches#165/27 kasan/simple_atomic_8_not_on_stack:OK kernel-patches#165/28 kasan/simple_atomic_8_on_stack:OK kernel-patches#165/29 kasan/load_acquire_1_not_on_stack:SKIP kernel-patches#165/30 kasan/load_acquire_1_on_stack:SKIP kernel-patches#165/31 kasan/load_acquire_2_not_on_stack:SKIP kernel-patches#165/32 kasan/load_acquire_2_on_stack:SKIP kernel-patches#165/33 kasan/load_acquire_4_not_on_stack:SKIP kernel-patches#165/34 kasan/load_acquire_4_on_stack:SKIP kernel-patches#165/35 kasan/load_acquire_8_not_on_stack:SKIP kernel-patches#165/36 kasan/load_acquire_8_on_stack:SKIP kernel-patches#165/37 kasan/store_release_1_not_on_stack:SKIP kernel-patches#165/38 kasan/store_release_1_on_stack:SKIP kernel-patches#165/39 kasan/store_release_2_not_on_stack:SKIP kernel-patches#165/40 kasan/store_release_2_on_stack:SKIP kernel-patches#165/41 kasan/store_release_4_not_on_stack:SKIP kernel-patches#165/42 kasan/store_release_4_on_stack:SKIP kernel-patches#165/43 kasan/store_release_8_not_on_stack:SKIP kernel-patches#165/44 kasan/store_release_8_on_stack:SKIP kernel-patches#165/45 kasan/ldx_patched:OK kernel-patches#165/46 kasan/ldx_patched_on_stack:OK kernel-patches#165/47 kasan/verifier_paths_stack_and_non_stack:OK kernel-patches#165/48 kasan/st_blinded:OK kernel-patches#165 kasan:OK (SKIP: 16/48) Summary: 1/32 PASSED, 16 SKIPPED, 0 FAILED Signed-off-by: Alexis Lothoré (eBPF Foundation) <alexis.lothore@bootlin.com> --- Changes in v5: - check snprintf return codes when checking kernel logs - skip tests in kasan_multi_shot is not enabled - update simple atomic test expected access type (read -> write) Changes in v4: - prevent interleaved kernel logs from breaking kasan report search - make new kfuncs in bpf_testmod depend on BPF_JIT_KASAN rather than KASAN_GENERIC - initialize buffer passed to bpf_prog_test_run_opts Changes in v3: - increase kernel log buffer size - fix comment style - check bpf_program__fd return code - document kasan_multi_shot - fix copy-paste mistakes on poisoning/unpoisoning sequences - add test for patch due to constant blinding Changes in v2: - simplify tests by just manually poisoning test areas with a dedicated kfunc - introduce one prog per covered instruction family - make sure that tests do not consume kernel logs (use /dev/kmgs rather than klogctl) - add tests for stack accesses: - marking correctly set when there are diverging verifier states leading to different memory types - marking kept in sync with prog when it is patched
386cc3c to
ded8f8a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.