Allow to verify jit behaviour by writing tests as below: SEC("tp") __jit_x86("endbr64") __jit_x86("movabs $0x.*,%r9") __jit_x86("add %gs:0x.*,%r9") __jit_x86("mov $0x1,%edi") __jit_x86("mov %rdi,-0x8(%r9)") __jit_x86("mov -0x8(%r9),%rdi") __jit_x86("xor %eax,%eax") __jit_x86("lock xchg %rax,-0x8(%r9)") __jit_x86("lock xadd %rax,-0x8(%r9)") __naked void stack_access_insns(void) { asm volatile (... ::: __clobber_all); } Use regular expressions by default, use basic regular expressions class in order to avoid escaping symbols like $(), often used in AT&T disassembly syntax. Signed-off-by: Eduard Zingerman <eddyz87@xxxxxxxxx> --- tools/testing/selftests/bpf/progs/bpf_misc.h | 2 + tools/testing/selftests/bpf/test_loader.c | 156 +++++++++++++------ 2 files changed, 112 insertions(+), 46 deletions(-) diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h index a225cd87897c..06e353a0a5b1 100644 --- a/tools/testing/selftests/bpf/progs/bpf_misc.h +++ b/tools/testing/selftests/bpf/progs/bpf_misc.h @@ -90,6 +90,8 @@ #define __arch_x86_64 __arch("X86_64") #define __arch_arm64 __arch("ARM64") #define __arch_riscv64 __arch("RISCV64") +#define __jit_x86(basic_regex) __attribute__((btf_decl_tag("comment:test_jit_x86=" basic_regex))) +#define __jit_x86_unpriv(basic_regex) __attribute__((btf_decl_tag("comment:test_jit_x86_unpriv=" basic_regex))) /* Convenience macro for use with 'asm volatile' blocks */ #define __naked __attribute__((naked)) diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c index 1b1290e090e7..7d8a0cf9904a 100644 --- a/tools/testing/selftests/bpf/test_loader.c +++ b/tools/testing/selftests/bpf/test_loader.c @@ -10,6 +10,7 @@ #include "disasm_helpers.h" #include "unpriv_helpers.h" #include "cap_helpers.h" +#include "jit_disasm_helpers.h" #define str_has_pfx(str, pfx) \ (strncmp(str, pfx, __builtin_constant_p(pfx) ? sizeof(pfx) - 1 : strlen(pfx)) == 0) @@ -35,6 +36,8 @@ #define TEST_TAG_AUXILIARY_UNPRIV "comment:test_auxiliary_unpriv" #define TEST_BTF_PATH "comment:test_btf_path=" #define TEST_TAG_ARCH "comment:test_arch=" +#define TEST_TAG_JIT_X86_PFX "comment:test_jit_x86=" +#define TEST_TAG_JIT_X86_PFX_UNPRIV "comment:test_jit_x86_unpriv=" /* Warning: duplicated in bpf_misc.h */ #define POINTER_VALUE 0xcafe4all @@ -53,10 +56,18 @@ enum mode { UNPRIV = 2 }; +enum arch { + ARCH_X86_64 = 1, + ARCH_ARM64 = 2, + ARCH_RISCV64 = 3, + ARCH_MAX +}; + struct expect_msg { const char *substr; /* substring match */ const char *regex_str; /* regex-based match */ regex_t regex; + int regex_flags; }; struct expected_msgs { @@ -69,6 +80,7 @@ struct test_subspec { bool expect_failure; struct expected_msgs expect_msgs; struct expected_msgs expect_xlated; + struct expected_msgs jited[ARCH_MAX]; int retval; bool execute; }; @@ -120,11 +132,17 @@ static void free_msgs(struct expected_msgs *msgs) static void free_test_spec(struct test_spec *spec) { + int i; + /* Deallocate expect_msgs arrays. */ free_msgs(&spec->priv.expect_msgs); free_msgs(&spec->unpriv.expect_msgs); free_msgs(&spec->priv.expect_xlated); free_msgs(&spec->unpriv.expect_xlated); + for (i = 0; i < ARCH_MAX; ++i) { + free_msgs(&spec->priv.jited[i]); + free_msgs(&spec->unpriv.jited[i]); + } free(spec->priv.name); free(spec->unpriv.name); @@ -132,7 +150,8 @@ static void free_test_spec(struct test_spec *spec) spec->unpriv.name = NULL; } -static int push_msg(const char *substr, const char *regex_str, struct expected_msgs *msgs) +static int __push_msg(const char *substr, const char *regex_str, int regex_flags, + struct expected_msgs *msgs) { void *tmp; int regcomp_res; @@ -151,10 +170,12 @@ static int push_msg(const char *substr, const char *regex_str, struct expected_m if (substr) { msg->substr = substr; msg->regex_str = NULL; + msg->regex_flags = 0; } else { msg->regex_str = regex_str; msg->substr = NULL; - regcomp_res = regcomp(&msg->regex, regex_str, REG_EXTENDED|REG_NEWLINE); + msg->regex_flags = regex_flags; + regcomp_res = regcomp(&msg->regex, regex_str, regex_flags|REG_NEWLINE); if (regcomp_res != 0) { regerror(regcomp_res, &msg->regex, error_msg, sizeof(error_msg)); PRINT_FAIL("Regexp compilation error in '%s': '%s'\n", @@ -167,6 +188,35 @@ static int push_msg(const char *substr, const char *regex_str, struct expected_m return 0; } +static int clone_msgs(struct expected_msgs *from, struct expected_msgs *to) +{ + struct expect_msg *msg; + int i, err; + + for (i = 0; i < from->cnt; i++) { + msg = &from->patterns[i]; + err = __push_msg(msg->substr, msg->regex_str, msg->regex_flags, to); + if (err) + return err; + } + return 0; +} + +static int push_msg(const char *substr, struct expected_msgs *msgs) +{ + return __push_msg(substr, NULL, 0, msgs); +} + +static int push_extended_regex(const char *regex_str, struct expected_msgs *msgs) +{ + return __push_msg(NULL, regex_str, REG_EXTENDED, msgs); +} + +static int push_basic_regex(const char *regex_str, struct expected_msgs *msgs) +{ + return __push_msg(NULL, regex_str, 0, msgs); +} + static int parse_int(const char *str, int *val, const char *name) { char *end; @@ -215,12 +265,6 @@ static void update_flags(int *flags, int flag, bool clear) *flags |= flag; } -enum arch { - ARCH_X86_64 = 0x1, - ARCH_ARM64 = 0x2, - ARCH_RISCV64 = 0x4, -}; - /* Uses btf_decl_tag attributes to describe the expected test * behavior, see bpf_misc.h for detailed description of each attribute * and attribute combinations. @@ -292,37 +336,49 @@ static int parse_test_spec(struct test_loader *tester, spec->mode_mask |= UNPRIV; } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX)) { msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1; - err = push_msg(msg, NULL, &spec->priv.expect_msgs); + err = push_msg(msg, &spec->priv.expect_msgs); if (err) goto cleanup; spec->mode_mask |= PRIV; } else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX_UNPRIV)) { msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX_UNPRIV) - 1; - err = push_msg(msg, NULL, &spec->unpriv.expect_msgs); + err = push_msg(msg, &spec->unpriv.expect_msgs); if (err) goto cleanup; spec->mode_mask |= UNPRIV; } else if (str_has_pfx(s, TEST_TAG_EXPECT_REGEX_PFX)) { msg = s + sizeof(TEST_TAG_EXPECT_REGEX_PFX) - 1; - err = push_msg(NULL, msg, &spec->priv.expect_msgs); + err = push_extended_regex(msg, &spec->priv.expect_msgs); if (err) goto cleanup; spec->mode_mask |= PRIV; } else if (str_has_pfx(s, TEST_TAG_EXPECT_REGEX_PFX_UNPRIV)) { msg = s + sizeof(TEST_TAG_EXPECT_REGEX_PFX_UNPRIV) - 1; - err = push_msg(NULL, msg, &spec->unpriv.expect_msgs); + err = push_extended_regex(msg, &spec->unpriv.expect_msgs); + if (err) + goto cleanup; + spec->mode_mask |= UNPRIV; + } else if (str_has_pfx(s, TEST_TAG_JIT_X86_PFX)) { + msg = s + sizeof(TEST_TAG_JIT_X86_PFX) - 1; + err = push_basic_regex(msg, &spec->priv.jited[ARCH_X86_64]); + if (err) + goto cleanup; + spec->mode_mask |= PRIV; + } else if (str_has_pfx(s, TEST_TAG_JIT_X86_PFX_UNPRIV)) { + msg = s + sizeof(TEST_TAG_JIT_X86_PFX_UNPRIV) - 1; + err = push_basic_regex(msg, &spec->unpriv.jited[ARCH_X86_64]); if (err) goto cleanup; spec->mode_mask |= UNPRIV; } else if (str_has_pfx(s, TEST_TAG_EXPECT_XLATED_PFX)) { msg = s + sizeof(TEST_TAG_EXPECT_XLATED_PFX) - 1; - err = push_msg(msg, NULL, &spec->priv.expect_xlated); + err = push_msg(msg, &spec->priv.expect_xlated); if (err) goto cleanup; spec->mode_mask |= PRIV; } else if (str_has_pfx(s, TEST_TAG_EXPECT_XLATED_PFX_UNPRIV)) { msg = s + sizeof(TEST_TAG_EXPECT_XLATED_PFX_UNPRIV) - 1; - err = push_msg(msg, NULL, &spec->unpriv.expect_xlated); + err = push_msg(msg, &spec->unpriv.expect_xlated); if (err) goto cleanup; spec->mode_mask |= UNPRIV; @@ -376,11 +432,11 @@ static int parse_test_spec(struct test_loader *tester, } else if (str_has_pfx(s, TEST_TAG_ARCH)) { val = s + sizeof(TEST_TAG_ARCH) - 1; if (strcmp(val, "X86_64") == 0) { - arch_mask |= ARCH_X86_64; + arch_mask |= 1u << ARCH_X86_64; } else if (strcmp(val, "ARM64") == 0) { - arch_mask |= ARCH_ARM64; + arch_mask |= 1u << ARCH_ARM64; } else if (strcmp(val, "RISCV64") == 0) { - arch_mask |= ARCH_RISCV64; + arch_mask |= 1u << ARCH_RISCV64; } else { PRINT_FAIL("bad arch spec: '%s'", val); err = -EINVAL; @@ -434,26 +490,13 @@ static int parse_test_spec(struct test_loader *tester, spec->unpriv.execute = spec->priv.execute; } - if (spec->unpriv.expect_msgs.cnt == 0) { - for (i = 0; i < spec->priv.expect_msgs.cnt; i++) { - struct expect_msg *msg = &spec->priv.expect_msgs.patterns[i]; - - err = push_msg(msg->substr, msg->regex_str, - &spec->unpriv.expect_msgs); - if (err) - goto cleanup; - } - } - if (spec->unpriv.expect_xlated.cnt == 0) { - for (i = 0; i < spec->priv.expect_xlated.cnt; i++) { - struct expect_msg *msg = &spec->priv.expect_xlated.patterns[i]; - - err = push_msg(msg->substr, msg->regex_str, - &spec->unpriv.expect_xlated); - if (err) - goto cleanup; - } - } + if (spec->unpriv.expect_msgs.cnt == 0) + clone_msgs(&spec->priv.expect_msgs, &spec->unpriv.expect_msgs); + if (spec->unpriv.expect_xlated.cnt == 0) + clone_msgs(&spec->priv.expect_xlated, &spec->unpriv.expect_xlated); + for (i = 0; i < ARCH_MAX; ++i) + if (spec->unpriv.jited[i].cnt == 0) + clone_msgs(&spec->priv.jited[i], &spec->unpriv.jited[i]); } spec->valid = true; @@ -508,6 +551,13 @@ static void emit_xlated(const char *xlated, bool force) fprintf(stdout, "XLATED:\n=============\n%s=============\n", xlated); } +static void emit_jited(const char *jited, bool force) +{ + if (!force && env.verbosity == VERBOSE_NONE) + return; + fprintf(stdout, "JITED:\n=============\n%s=============\n", jited); +} + static void validate_msgs(char *log_buf, struct expected_msgs *msgs, void (*emit_fn)(const char *buf, bool force)) { @@ -702,18 +752,16 @@ static int get_xlated_program_text(int prog_fd, char *text, size_t text_sz) return err; } -static bool run_on_current_arch(int arch_mask) +static int get_current_arch(void) { - if (arch_mask == 0) - return true; #if defined(__x86_64__) - return arch_mask & ARCH_X86_64; + return ARCH_X86_64; #elif defined(__aarch64__) - return arch_mask & ARCH_ARM64; + return ARCH_ARM64; #elif defined(__riscv) && __riscv_xlen == 64 - return arch_mask & ARCH_RISCV64; + return ARCH_RISCV64; #endif - return false; + return -1; } /* this function is forced noinline and has short generic name to look better @@ -732,15 +780,16 @@ void run_subtest(struct test_loader *tester, struct bpf_program *tprog = NULL, *tprog_iter; struct test_spec *spec_iter; struct cap_state caps = {}; + int retval, err, i, arch; struct bpf_object *tobj; struct bpf_map *map; - int retval, err, i; bool should_load; if (!test__start_subtest(subspec->name)) return; - if (!run_on_current_arch(spec->arch_mask)) { + arch = get_current_arch(); + if (spec->arch_mask && (arch < 0 || (spec->arch_mask & (1u << arch)) == 0)) { test__skip(); return; } @@ -817,6 +866,21 @@ void run_subtest(struct test_loader *tester, validate_msgs(tester->log_buf, &subspec->expect_xlated, emit_xlated); } + if (arch > 0 && subspec->jited[arch].cnt) { + err = get_jited_program_text(bpf_program__fd(tprog), + tester->log_buf, tester->log_buf_sz); + if (err == -ENOTSUP) { + printf("%s:SKIP: jited programs disassembly is not supported,\n", __func__); + printf("%s:SKIP: tests are built w/o LLVM development libs\n", __func__); + test__skip(); + goto tobj_cleanup; + } + if (!ASSERT_EQ(err, 0, "get_jited_program_text")) + goto tobj_cleanup; + emit_jited(tester->log_buf, false /*force*/); + validate_msgs(tester->log_buf, &subspec->jited[arch], emit_jited); + } + if (should_do_test_run(spec, subspec)) { /* For some reason test_verifier executes programs * with all capabilities restored. Do the same here. -- 2.45.2