From: Mykyta Yatsenko Add bpf_wq selftests to verify: * BPF program using non-constant offset of struct bpf_wq is rejected * BPF program using map with no BTF for storing struct bpf_wq is rejected Signed-off-by: Mykyta Yatsenko --- tools/testing/selftests/bpf/prog_tests/wq.c | 48 +++++++++++++++++++ .../testing/selftests/bpf/progs/wq_failures.c | 23 +++++++++ 2 files changed, 71 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/wq.c b/tools/testing/selftests/bpf/prog_tests/wq.c index 99e438fe12ac..13c124fff365 100644 --- a/tools/testing/selftests/bpf/prog_tests/wq.c +++ b/tools/testing/selftests/bpf/prog_tests/wq.c @@ -1,9 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2024 Benjamin Tissoires */ #include +#include #include "wq.skel.h" #include "wq_failures.skel.h" +static void test_failure_map_no_btf(void); + void serial_test_wq(void) { struct wq *wq_skel = NULL; @@ -11,6 +14,9 @@ void serial_test_wq(void) LIBBPF_OPTS(bpf_test_run_opts, topts); + if (test__start_subtest("test_failure_map_no_btf")) + test_failure_map_no_btf(); + RUN_TESTS(wq); /* re-run the success test to check if the timer was actually executed */ @@ -38,3 +44,45 @@ void serial_test_failures_wq(void) { RUN_TESTS(wq_failures); } + +static void test_failure_map_no_btf(void) +{ + char log[8192]; + struct btf *vmlinux_btf = libbpf_find_kernel_btf(); + int kfunc_id = btf__find_by_name_kind(vmlinux_btf, "bpf_wq_init", BTF_KIND_FUNC); + int map_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "map_no_btf", sizeof(__u32), sizeof(__u64), + 100, NULL); + struct bpf_insn prog[] = { + /* key = 42 on stack at [fp-4] */ + BPF_MOV64_IMM(BPF_REG_0, 42), /* r0 = 42 */ + BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), /* *(u32 *)(fp-4) = 42 */ + + /* r1 = &map (patched from map_fd), r2 = &key */ + BPF_LD_MAP_FD(BPF_REG_1, map_fd), /* r1 = map */ + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), /* r2 = fp */ + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), /* r2 = fp-4 (key addr) */ + + /* map_val = bpf_map_lookup_elem(map, &key) */ + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), /* r0 = map_val or NULL */ + + /* if (!map_val) goto out; */ + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), /* if (r0 == NULL) skip next 4 insns */ + + /* wq = (void *)(map_val + 0); -> use r0 as arg1 directly */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), /* r1 = wq (= val ptr) */ + + /* bpf_wq_init(wq, &map, 0) */ + BPF_LD_MAP_FD(BPF_REG_2, map_fd), /* r2 = map */ + BPF_MOV64_IMM(BPF_REG_3, 0), /* r3 = flags (0) */ + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, + kfunc_id), /* r0 = bpf_wq_init(wq, &map, 0) */ + BPF_EXIT_INSN(), /* return -3 */ + }; + LIBBPF_OPTS(bpf_prog_load_opts, opts, .log_size = sizeof(log), .log_buf = log, + .log_level = 2); + int r = bpf_prog_load(BPF_PROG_TYPE_TRACEPOINT, NULL, "GPL", prog, ARRAY_SIZE(prog), &opts); + + ASSERT_NEQ(r, 0, "prog load failed"); + ASSERT_HAS_SUBSTR(log, "map 'map_no_btf' has to have BTF in order to use bpf_wq", + "log complains no map BTF"); +} diff --git a/tools/testing/selftests/bpf/progs/wq_failures.c b/tools/testing/selftests/bpf/progs/wq_failures.c index 4240211a1900..d06f6d40594a 100644 --- a/tools/testing/selftests/bpf/progs/wq_failures.c +++ b/tools/testing/selftests/bpf/progs/wq_failures.c @@ -142,3 +142,26 @@ long test_wrong_wq_pointer_offset(void *ctx) return -22; } + +SEC("tc") +__log_level(2) +__failure +__msg(": (85) call bpf_wq_init#") +__msg("R1 doesn't have constant offset. bpf_wq has to be at the constant offset") +long test_bad_wq_off(void *ctx) +{ + struct elem *val; + struct bpf_wq *wq; + int key = 42; + u64 unknown; + + val = bpf_map_lookup_elem(&array, &key); + if (!val) + return -2; + + unknown = bpf_get_prandom_u32(); + wq = &val->w + unknown; + if (bpf_wq_init(wq, &array, 0) != 0) + return -3; + return 0; +} -- 2.51.0