This patch adds a test exercising logic that was fixed / improved in the previous patch in the series, as well as general sanity checking for jit's PROBE_MEM logic which should've been unaffected by the previous patch. The added verifier test does the following: * Acquire a referenced kptr to struct prog_test_ref_kfunc using existing net/bpf/test_run.c kfunc * Helper returns ptr to a specific prog_test_ref_kfunc whose first two fields - both ints - have been prepopulated w/ vals 42 and 108, respectively * kptr_xchg the acquired ptr into an arraymap * Do a direct map_value load of the just-added ptr * Goal of all this setup is to get an unreferenced kptr pointing to struct with ints of known value, which is the result of this step * Using unreferenced kptr obtained in previous step, do loads of prog_test_ref_kfunc.a (offset 0) and .b (offset 4) * Then incr the kptr by 8 and load prog_test_ref_kfunc.a again (this time at offset -8) * Add all the loaded ints together and return Before the PROBE_MEM fixes in previous patch, the loads at offset 0 and 4 would succeed, while the load at offset -8 would incorrectly fail runtime check emitted by the JIT and 0 out dst reg as a result. This confirmed by retval of 150 for this test before previous patch - since second .a read is 0'd out - and a retval of 192 with the fixed logic. The test exercises the two optimizations to fixed logic added in last patch as well: * BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0) exercises "insn->off is 0, no need to add / sub from src_reg" optimization * BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, -8) exercises "src_reg == dst_reg, no need to restore src_reg after load" optimization Signed-off-by: Dave Marchevsky <davemarchevsky@xxxxxx> --- tools/testing/selftests/bpf/test_verifier.c | 75 ++++++++++++++---- tools/testing/selftests/bpf/verifier/jit.c | 84 +++++++++++++++++++++ 2 files changed, 146 insertions(+), 13 deletions(-) diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 8c808551dfd7..14f8d0231e3c 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -55,7 +55,7 @@ #define MAX_UNEXPECTED_INSNS 32 #define MAX_TEST_INSNS 1000000 #define MAX_FIXUPS 8 -#define MAX_NR_MAPS 23 +#define MAX_NR_MAPS 24 #define MAX_TEST_RUNS 8 #define POINTER_VALUE 0xcafe4all #define TEST_DATA_LEN 64 @@ -131,6 +131,7 @@ struct bpf_test { int fixup_map_ringbuf[MAX_FIXUPS]; int fixup_map_timer[MAX_FIXUPS]; int fixup_map_kptr[MAX_FIXUPS]; + int fixup_map_probe_mem_read[MAX_FIXUPS]; struct kfunc_btf_id_pair fixup_kfunc_btf_id[MAX_FIXUPS]; /* Expected verifier log output for result REJECT or VERBOSE_ACCEPT. * Can be a tab-separated sequence of expected strings. An empty string @@ -698,15 +699,26 @@ static int create_cgroup_storage(bool percpu) * struct timer { * struct bpf_timer t; * }; + * struct prog_test_member1 { + * int a; + * }; + * struct prog_test_member { + * struct prog_test_member1 m; + * int c; + * }; * struct btf_ptr { * struct prog_test_ref_kfunc __kptr *ptr; * struct prog_test_ref_kfunc __kptr_ref *ptr; * struct prog_test_member __kptr_ref *ptr; - * } + * }; + * struct probe_mem_holder { + * struct prog_test_member kptr_ref *ptr; + * }; */ static const char btf_str_sec[] = "\0bpf_spin_lock\0val\0cnt\0l\0bpf_timer\0timer\0t" "\0btf_ptr\0prog_test_ref_kfunc\0ptr\0kptr\0kptr_ref" - "\0prog_test_member"; + "\0prog_test_member\0prog_test_member1\0a\0c\0m" + "\0probe_mem_holder"; static __u32 btf_raw_types[] = { /* int */ BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4), /* [1] */ @@ -724,20 +736,29 @@ static __u32 btf_raw_types[] = { BTF_MEMBER_ENC(41, 4, 0), /* struct bpf_timer t; */ /* struct prog_test_ref_kfunc */ /* [6] */ BTF_STRUCT_ENC(51, 0, 0), - BTF_STRUCT_ENC(89, 0, 0), /* [7] */ + /* struct prog_test_member1 */ /* [7] */ + BTF_STRUCT_ENC(106, 1, 4), + BTF_MEMBER_ENC(124, 1, 0), /* int a; */ + /* struct prog_test_member */ /* [8] */ + BTF_STRUCT_ENC(89, 2, 8), + BTF_MEMBER_ENC(128, 7, 0), /* struct prog_test_member1 m; */ + BTF_MEMBER_ENC(126, 1, 32), /* int c; */ /* type tag "kptr" */ - BTF_TYPE_TAG_ENC(75, 6), /* [8] */ + BTF_TYPE_TAG_ENC(75, 6), /* [9] */ /* type tag "kptr_ref" */ - BTF_TYPE_TAG_ENC(80, 6), /* [9] */ - BTF_TYPE_TAG_ENC(80, 7), /* [10] */ - BTF_PTR_ENC(8), /* [11] */ + BTF_TYPE_TAG_ENC(80, 6), /* [10] */ + BTF_TYPE_TAG_ENC(80, 8), /* [11] */ BTF_PTR_ENC(9), /* [12] */ BTF_PTR_ENC(10), /* [13] */ - /* struct btf_ptr */ /* [14] */ + BTF_PTR_ENC(11), /* [14] */ + /* struct btf_ptr */ /* [15] */ BTF_STRUCT_ENC(43, 3, 24), - BTF_MEMBER_ENC(71, 11, 0), /* struct prog_test_ref_kfunc __kptr *ptr; */ - BTF_MEMBER_ENC(71, 12, 64), /* struct prog_test_ref_kfunc __kptr_ref *ptr; */ - BTF_MEMBER_ENC(71, 13, 128), /* struct prog_test_member __kptr_ref *ptr; */ + BTF_MEMBER_ENC(71, 12, 0), /* struct prog_test_ref_kfunc __kptr *ptr; */ + BTF_MEMBER_ENC(71, 13, 64), /* struct prog_test_ref_kfunc __kptr_ref *ptr; */ + BTF_MEMBER_ENC(71, 14, 128), /* struct prog_test_member __kptr_ref *ptr; */ + /* struct probe_mem_holder */ /* [16] */ + BTF_STRUCT_ENC(130, 1, 8), + BTF_MEMBER_ENC(71, 13, 0), /* struct prog_test_ref_kfunc kptr_ref *ptr; */ }; static char bpf_vlog[UINT_MAX >> 8]; @@ -863,7 +884,7 @@ static int create_map_kptr(void) { LIBBPF_OPTS(bpf_map_create_opts, opts, .btf_key_type_id = 1, - .btf_value_type_id = 14, + .btf_value_type_id = 15, ); int fd, btf_fd; @@ -878,6 +899,26 @@ static int create_map_kptr(void) return fd; } +static int create_map_probe_mem_read(void) +{ + LIBBPF_OPTS(bpf_map_create_opts, opts, + .btf_key_type_id = 1, + .btf_value_type_id = 16, + ); + int fd, btf_fd; + + btf_fd = load_btf(); + if (btf_fd < 0) + return -1; + + opts.btf_fd = btf_fd; + fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "test_map", 4, 8, 1, &opts); + if (fd < 0) + printf("Failed to create map for probe_mem reads\n"); + + return fd; +} + static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type, struct bpf_insn *prog, int *map_fds) { @@ -904,6 +945,7 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type, int *fixup_map_ringbuf = test->fixup_map_ringbuf; int *fixup_map_timer = test->fixup_map_timer; int *fixup_map_kptr = test->fixup_map_kptr; + int *fixup_map_probe_mem_read = test->fixup_map_probe_mem_read; struct kfunc_btf_id_pair *fixup_kfunc_btf_id = test->fixup_kfunc_btf_id; if (test->fill_helper) { @@ -1104,6 +1146,13 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type, fixup_map_kptr++; } while (*fixup_map_kptr); } + if (*fixup_map_probe_mem_read) { + map_fds[23] = create_map_probe_mem_read(); + do { + prog[*fixup_map_probe_mem_read].imm = map_fds[23]; + fixup_map_probe_mem_read++; + } while (*fixup_map_probe_mem_read); + } /* Patch in kfunc BTF IDs */ if (fixup_kfunc_btf_id->kfunc) { diff --git a/tools/testing/selftests/bpf/verifier/jit.c b/tools/testing/selftests/bpf/verifier/jit.c index 8bf37e5207f1..92f19bcaf3ad 100644 --- a/tools/testing/selftests/bpf/verifier/jit.c +++ b/tools/testing/selftests/bpf/verifier/jit.c @@ -216,3 +216,87 @@ .result = ACCEPT, .retval = 3, }, +{ + "jit: PROBE_MEM_READ ldx success cases", + .insns = { + /* int *reg_9 = fp - 12; + * *reg_9 = 0 + */ + BPF_MOV64_REG(BPF_REG_9, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_9, -12), + BPF_ST_MEM(BPF_W, BPF_REG_9, 0, 0), + BPF_LD_MAP_FD(BPF_REG_6, 0), + + /* long *dummy_arg = fp - 8 + * ret = bpf_kfunc_call_test_acquire(dummy_arg) + * if (!ret) exit + */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_ST_MEM(BPF_DW, BPF_REG_1, 0, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 5), + BPF_EXIT_INSN(), + + /* label err_exit + * err_exit: + * bpf_kfunc_call_test_release(reg_7); + * return 1; + */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_7), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, BPF_PSEUDO_KFUNC_CALL, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + + /* reg_7 = acquired prog_test_ref_kfunc */ + BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_9), + + /* ret = bpf_map_lookup_elem(&data, &zero); + * if (!ret) goto err_exit; + * else reg_0 = ptr_map_val containing struct prog_test_ref_kfunc + */ + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, -10), + + /* ret = bpf_kptr_xchg(ptr_map_val, acquired prog_test_ref_kfunc) + * if(ret) goto err_exit; + */ + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_7), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_kptr_xchg), + BPF_MOV64_REG(BPF_REG_7, BPF_REG_0), + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, -15), + + /* Do a direct LD of the struct prog_test_ref_kfunc we just xchg'd into + * map + */ + BPF_LD_MAP_VALUE(BPF_REG_0, 0, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), + BPF_EXIT_INSN(), + + /* Try accessing its fields - this should trigger PROBE_MEM jit + * behavior + * + * r0 = prog_test_ref_kfunc.a * 2 + prog_test_ref_kfunc.b + */ + BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), + BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_0, 4), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_0, -8), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_2), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .fixup_map_probe_mem_read = { 3, 26 }, + .fixup_kfunc_btf_id = { + { "bpf_kfunc_call_test_acquire", 8 }, + { "bpf_kfunc_call_test_release", 12}, + }, + .result = ACCEPT, + .retval = 192, +}, -- 2.30.2