From: Dave Marchevsky <davemarchevsky@fb.com>
To: <bpf@vger.kernel.org>
Cc: Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>,
Kernel Team <kernel-team@fb.com>,
Dave Marchevsky <davemarchevsky@fb.com>
Subject: [PATCH bpf-next 2/2] selftests/bpf: Add verifier test exercising jit PROBE_MEM logic
Date: Tue, 13 Dec 2022 10:27:26 -0800 [thread overview]
Message-ID: <20221213182726.325137-2-davemarchevsky@fb.com> (raw)
In-Reply-To: <20221213182726.325137-1-davemarchevsky@fb.com>
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@fb.com>
---
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
next prev parent reply other threads:[~2022-12-13 18:27 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-12-13 18:27 [PATCH bpf-next 1/2] bpf, x86: Improve PROBE_MEM runtime load check Dave Marchevsky
2022-12-13 18:27 ` Dave Marchevsky [this message]
2022-12-16 3:34 ` [PATCH bpf-next 2/2] selftests/bpf: Add verifier test exercising jit PROBE_MEM logic Yonghong Song
2022-12-16 4:39 ` Dave Marchevsky
2022-12-15 23:38 ` [PATCH bpf-next 1/2] bpf, x86: Improve PROBE_MEM runtime load check Yonghong Song
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20221213182726.325137-2-davemarchevsky@fb.com \
--to=davemarchevsky@fb.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=kernel-team@fb.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.