[PATCH bpf v2 2/2] selftests/bpf: Check per-CPU address resolution per register
Vineet Gupta <[email protected]>
| Newsgroups | org.kernel.vger.stable,org.kernel.vger.bpf |
|---|---|
| Message-ID | <[email protected]> |
An ld_imm64 of a per-CPU map value is followed by a mov_percpu_addr that reuses the same register, so which register the address lands in decides how the JIT encodes the add. Getting the REX prefix wrong there is invisible to a functional test on x86 unless the address happens to land in an extended register, which is why this went unnoticed. Add one __naked program per extended register, R5, R7, R8 and R9, each loading a .percpu variable into that register, and match the JITed add against the register it must resolve into. R1 is covered too, so that a fix which sets REX.R unconditionally does not pass either. Signed-off-by: Vineet Gupta <[email protected]> --- .../selftests/bpf/prog_tests/verifier.c | 2 + .../bpf/progs/verifier_percpu_addr.c | 114 ++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 tools/testing/selftests/bpf/progs/verifier_percpu_addr.c diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c index 8113fea7ba86..64ac49ad67e6 100644 --- a/tools/testing/selftests/bpf/prog_tests/verifier.c +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c @@ -79,6 +79,7 @@ #include "verifier_netfilter_retcode.skel.h" #include "verifier_bpf_fastcall.skel.h" #include "verifier_or_jmp32_k.skel.h" +#include "verifier_percpu_addr.skel.h" #include "verifier_precision.skel.h" #include "verifier_prevent_map_lookup.skel.h" #include "verifier_private_stack.skel.h" @@ -240,6 +241,7 @@ void test_verifier_netfilter_ctx(void) { RUN(verifier_netfilter_ctx); } void test_verifier_netfilter_retcode(void) { RUN(verifier_netfilter_retcode); } void test_verifier_bpf_fastcall(void) { RUN(verifier_bpf_fastcall); } void test_verifier_or_jmp32_k(void) { RUN(verifier_or_jmp32_k); } +void test_verifier_percpu_addr(void) { RUN(verifier_percpu_addr); } void test_verifier_precision(void) { RUN(verifier_precision); } void test_verifier_prevent_map_lookup(void) { RUN(verifier_prevent_map_lookup); } void test_verifier_private_stack(void) { RUN(verifier_private_stack); } diff --git a/tools/testing/selftests/bpf/progs/verifier_percpu_addr.c b/tools/testing/selftests/bpf/progs/verifier_percpu_addr.c new file mode 100644 index 000000000000..962faea8ef90 --- /dev/null +++ b/tools/testing/selftests/bpf/progs/verifier_percpu_addr.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <vmlinux.h> +#include <bpf/bpf_helpers.h> +#include "bpf_misc.h" + +int percpu_data SEC(".percpu"); + +#if defined(__TARGET_ARCH_x86) + +/* + * An ld_imm64 of a per-CPU map value is followed by a mov_percpu_addr that + * reuses the same register, so the register the address lands in decides how + * the JIT encodes the add. On x86 R5, R7, R8 and R9 are the extended + * registers, whose high bit needs REX.R because the destination sits in + * ModRM.reg. Check one program per extended register, since getting the + * prefix wrong resolves the address into whichever register shares the low + * three bits instead. + */ + +SEC("raw_tp") +__description("per-CPU address into r5") +__success +__arch_x86_64 +__jited(" addq %gs:{{.*}}, %r8") +__naked void percpu_addr_into_r5(void) +{ + asm volatile (" \ + r5 = %[percpu_data] ll; \ + r0 = *(u32 *)(r5 + 0); \ + exit; \ +" : + : __imm_addr(percpu_data) + : __clobber_all); +} + +SEC("raw_tp") +__description("per-CPU address into r7") +__success +__arch_x86_64 +__jited(" addq %gs:{{.*}}, %r13") +__naked void percpu_addr_into_r7(void) +{ + asm volatile (" \ + r7 = %[percpu_data] ll; \ + r0 = *(u32 *)(r7 + 0); \ + exit; \ +" : + : __imm_addr(percpu_data) + : __clobber_all); +} + +SEC("raw_tp") +__description("per-CPU address into r8") +__success +__arch_x86_64 +__jited(" addq %gs:{{.*}}, %r14") +__naked void percpu_addr_into_r8(void) +{ + asm volatile (" \ + r8 = %[percpu_data] ll; \ + r0 = *(u32 *)(r8 + 0); \ + exit; \ +" : + : __imm_addr(percpu_data) + : __clobber_all); +} + +SEC("raw_tp") +__description("per-CPU address into r9") +__success +__arch_x86_64 +__jited(" addq %gs:{{.*}}, %r15") +__naked void percpu_addr_into_r9(void) +{ + asm volatile (" \ + r9 = %[percpu_data] ll; \ + r0 = *(u32 *)(r9 + 0); \ + exit; \ +" : + : __imm_addr(percpu_data) + : __clobber_all); +} + +/* A register that needs no REX.R, to catch a fix that overcorrects. */ +SEC("raw_tp") +__description("per-CPU address into r1") +__success +__arch_x86_64 +__jited(" addq %gs:{{.*}}, %rdi") +__naked void percpu_addr_into_r1(void) +{ + asm volatile (" \ + r1 = %[percpu_data] ll; \ + r0 = *(u32 *)(r1 + 0); \ + exit; \ +" : + : __imm_addr(percpu_data) + : __clobber_all); +} + +#else + +SEC("raw_tp") +__description("percpu addr dummy") +__success +int dummy_test(void) +{ + return 0; +} + +#endif + +char _license[] SEC("license") = "GPL"; -- 2.53.0-Meta