mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-04 20:19:47 +08:00
selftests/bpf: __jited test tag to check disassembly after jit
Allow to verify jit behaviour by writing tests as below: SEC("tp") __arch_x86_64 __jited(" endbr64") __jited(" nopl (%rax,%rax)") __jited(" xorq %rax, %rax") ... __naked void some_test(void) { asm volatile (... ::: __clobber_all); } Allow regular expressions in patterns, same way as in __msg. By default assume that each __jited pattern has to be matched on the next consecutive line of the disassembly, e.g.: __jited(" endbr64") # matched on line N __jited(" nopl (%rax,%rax)") # matched on line N+1 If match occurs on a wrong line an error is reported. To override this behaviour use __jited("..."), e.g.: __jited(" endbr64") # matched on line N __jited("...") # not matched __jited(" nopl (%rax,%rax)") # matched on any line >= N Signed-off-by: Eduard Zingerman <eddyz87@gmail.com> Link: https://lore.kernel.org/r/20240820102357.3372779-7-eddyz87@gmail.com Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
b991fc5207
commit
7d743e4c75
@ -36,6 +36,39 @@
|
|||||||
* Regular expressions could be specified same way as in __msg.
|
* Regular expressions could be specified same way as in __msg.
|
||||||
* __xlated_unpriv Same as __xlated but for unprivileged mode.
|
* __xlated_unpriv Same as __xlated but for unprivileged mode.
|
||||||
*
|
*
|
||||||
|
* __jited Match a line in a disassembly of the jited BPF program.
|
||||||
|
* Has to be used after __arch_* macro.
|
||||||
|
* For example:
|
||||||
|
*
|
||||||
|
* __arch_x86_64
|
||||||
|
* __jited(" endbr64")
|
||||||
|
* __jited(" nopl (%rax,%rax)")
|
||||||
|
* __jited(" xorq %rax, %rax")
|
||||||
|
* ...
|
||||||
|
* __naked void some_test(void)
|
||||||
|
* {
|
||||||
|
* asm volatile (... ::: __clobber_all);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Regular expressions could be included in patterns same way
|
||||||
|
* as in __msg.
|
||||||
|
*
|
||||||
|
* By default assume that each pattern has to be matched on the
|
||||||
|
* next consecutive line of disassembly, e.g.:
|
||||||
|
*
|
||||||
|
* __jited(" endbr64") # matched on line N
|
||||||
|
* __jited(" nopl (%rax,%rax)") # matched on line N+1
|
||||||
|
*
|
||||||
|
* If match occurs on a wrong line an error is reported.
|
||||||
|
* To override this behaviour use literal "...", e.g.:
|
||||||
|
*
|
||||||
|
* __jited(" endbr64") # matched on line N
|
||||||
|
* __jited("...") # not matched
|
||||||
|
* __jited(" nopl (%rax,%rax)") # matched on any line >= N
|
||||||
|
*
|
||||||
|
* __jited_unpriv Same as __jited but for unprivileged mode.
|
||||||
|
*
|
||||||
|
*
|
||||||
* __success Expect program load success in privileged mode.
|
* __success Expect program load success in privileged mode.
|
||||||
* __success_unpriv Expect program load success in unprivileged mode.
|
* __success_unpriv Expect program load success in unprivileged mode.
|
||||||
*
|
*
|
||||||
@ -76,11 +109,13 @@
|
|||||||
*/
|
*/
|
||||||
#define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" XSTR(__COUNTER__) "=" msg)))
|
#define __msg(msg) __attribute__((btf_decl_tag("comment:test_expect_msg=" XSTR(__COUNTER__) "=" msg)))
|
||||||
#define __xlated(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated=" XSTR(__COUNTER__) "=" msg)))
|
#define __xlated(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated=" XSTR(__COUNTER__) "=" msg)))
|
||||||
|
#define __jited(msg) __attribute__((btf_decl_tag("comment:test_jited=" XSTR(__COUNTER__) "=" msg)))
|
||||||
#define __failure __attribute__((btf_decl_tag("comment:test_expect_failure")))
|
#define __failure __attribute__((btf_decl_tag("comment:test_expect_failure")))
|
||||||
#define __success __attribute__((btf_decl_tag("comment:test_expect_success")))
|
#define __success __attribute__((btf_decl_tag("comment:test_expect_success")))
|
||||||
#define __description(desc) __attribute__((btf_decl_tag("comment:test_description=" desc)))
|
#define __description(desc) __attribute__((btf_decl_tag("comment:test_description=" desc)))
|
||||||
#define __msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_msg_unpriv=" XSTR(__COUNTER__) "=" msg)))
|
#define __msg_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_msg_unpriv=" XSTR(__COUNTER__) "=" msg)))
|
||||||
#define __xlated_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated_unpriv=" XSTR(__COUNTER__) "=" msg)))
|
#define __xlated_unpriv(msg) __attribute__((btf_decl_tag("comment:test_expect_xlated_unpriv=" XSTR(__COUNTER__) "=" msg)))
|
||||||
|
#define __jited_unpriv(msg) __attribute__((btf_decl_tag("comment:test_jited=" XSTR(__COUNTER__) "=" msg)))
|
||||||
#define __failure_unpriv __attribute__((btf_decl_tag("comment:test_expect_failure_unpriv")))
|
#define __failure_unpriv __attribute__((btf_decl_tag("comment:test_expect_failure_unpriv")))
|
||||||
#define __success_unpriv __attribute__((btf_decl_tag("comment:test_expect_success_unpriv")))
|
#define __success_unpriv __attribute__((btf_decl_tag("comment:test_expect_success_unpriv")))
|
||||||
#define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl)))
|
#define __log_level(lvl) __attribute__((btf_decl_tag("comment:test_log_level="#lvl)))
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "disasm_helpers.h"
|
#include "disasm_helpers.h"
|
||||||
#include "unpriv_helpers.h"
|
#include "unpriv_helpers.h"
|
||||||
#include "cap_helpers.h"
|
#include "cap_helpers.h"
|
||||||
|
#include "jit_disasm_helpers.h"
|
||||||
|
|
||||||
#define str_has_pfx(str, pfx) \
|
#define str_has_pfx(str, pfx) \
|
||||||
(strncmp(str, pfx, __builtin_constant_p(pfx) ? sizeof(pfx) - 1 : strlen(pfx)) == 0)
|
(strncmp(str, pfx, __builtin_constant_p(pfx) ? sizeof(pfx) - 1 : strlen(pfx)) == 0)
|
||||||
@ -33,6 +34,8 @@
|
|||||||
#define TEST_TAG_AUXILIARY_UNPRIV "comment:test_auxiliary_unpriv"
|
#define TEST_TAG_AUXILIARY_UNPRIV "comment:test_auxiliary_unpriv"
|
||||||
#define TEST_BTF_PATH "comment:test_btf_path="
|
#define TEST_BTF_PATH "comment:test_btf_path="
|
||||||
#define TEST_TAG_ARCH "comment:test_arch="
|
#define TEST_TAG_ARCH "comment:test_arch="
|
||||||
|
#define TEST_TAG_JITED_PFX "comment:test_jited="
|
||||||
|
#define TEST_TAG_JITED_PFX_UNPRIV "comment:test_jited_unpriv="
|
||||||
|
|
||||||
/* Warning: duplicated in bpf_misc.h */
|
/* Warning: duplicated in bpf_misc.h */
|
||||||
#define POINTER_VALUE 0xcafe4all
|
#define POINTER_VALUE 0xcafe4all
|
||||||
@ -68,6 +71,7 @@ struct test_subspec {
|
|||||||
bool expect_failure;
|
bool expect_failure;
|
||||||
struct expected_msgs expect_msgs;
|
struct expected_msgs expect_msgs;
|
||||||
struct expected_msgs expect_xlated;
|
struct expected_msgs expect_xlated;
|
||||||
|
struct expected_msgs jited;
|
||||||
int retval;
|
int retval;
|
||||||
bool execute;
|
bool execute;
|
||||||
};
|
};
|
||||||
@ -124,6 +128,8 @@ static void free_test_spec(struct test_spec *spec)
|
|||||||
free_msgs(&spec->unpriv.expect_msgs);
|
free_msgs(&spec->unpriv.expect_msgs);
|
||||||
free_msgs(&spec->priv.expect_xlated);
|
free_msgs(&spec->priv.expect_xlated);
|
||||||
free_msgs(&spec->unpriv.expect_xlated);
|
free_msgs(&spec->unpriv.expect_xlated);
|
||||||
|
free_msgs(&spec->priv.jited);
|
||||||
|
free_msgs(&spec->unpriv.jited);
|
||||||
|
|
||||||
free(spec->priv.name);
|
free(spec->priv.name);
|
||||||
free(spec->unpriv.name);
|
free(spec->unpriv.name);
|
||||||
@ -237,6 +243,21 @@ static int push_msg(const char *substr, struct expected_msgs *msgs)
|
|||||||
return __push_msg(substr, false, msgs);
|
return __push_msg(substr, false, msgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int push_disasm_msg(const char *regex_str, bool *on_next_line, struct expected_msgs *msgs)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (strcmp(regex_str, "...") == 0) {
|
||||||
|
*on_next_line = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
err = __push_msg(regex_str, *on_next_line, msgs);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
*on_next_line = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_int(const char *str, int *val, const char *name)
|
static int parse_int(const char *str, int *val, const char *name)
|
||||||
{
|
{
|
||||||
char *end;
|
char *end;
|
||||||
@ -320,6 +341,18 @@ enum arch {
|
|||||||
ARCH_RISCV64 = 0x4,
|
ARCH_RISCV64 = 0x4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int get_current_arch(void)
|
||||||
|
{
|
||||||
|
#if defined(__x86_64__)
|
||||||
|
return ARCH_X86_64;
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
return ARCH_ARM64;
|
||||||
|
#elif defined(__riscv) && __riscv_xlen == 64
|
||||||
|
return ARCH_RISCV64;
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Uses btf_decl_tag attributes to describe the expected test
|
/* Uses btf_decl_tag attributes to describe the expected test
|
||||||
* behavior, see bpf_misc.h for detailed description of each attribute
|
* behavior, see bpf_misc.h for detailed description of each attribute
|
||||||
* and attribute combinations.
|
* and attribute combinations.
|
||||||
@ -332,9 +365,13 @@ static int parse_test_spec(struct test_loader *tester,
|
|||||||
const char *description = NULL;
|
const char *description = NULL;
|
||||||
bool has_unpriv_result = false;
|
bool has_unpriv_result = false;
|
||||||
bool has_unpriv_retval = false;
|
bool has_unpriv_retval = false;
|
||||||
|
bool unpriv_jit_on_next_line;
|
||||||
|
bool jit_on_next_line;
|
||||||
|
bool collect_jit = false;
|
||||||
int func_id, i, err = 0;
|
int func_id, i, err = 0;
|
||||||
u32 arch_mask = 0;
|
u32 arch_mask = 0;
|
||||||
struct btf *btf;
|
struct btf *btf;
|
||||||
|
enum arch arch;
|
||||||
|
|
||||||
memset(spec, 0, sizeof(*spec));
|
memset(spec, 0, sizeof(*spec));
|
||||||
|
|
||||||
@ -399,6 +436,30 @@ static int parse_test_spec(struct test_loader *tester,
|
|||||||
if (err)
|
if (err)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
spec->mode_mask |= UNPRIV;
|
spec->mode_mask |= UNPRIV;
|
||||||
|
} else if ((msg = skip_dynamic_pfx(s, TEST_TAG_JITED_PFX))) {
|
||||||
|
if (arch_mask == 0) {
|
||||||
|
PRINT_FAIL("__jited used before __arch_*");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (collect_jit) {
|
||||||
|
err = push_disasm_msg(msg, &jit_on_next_line,
|
||||||
|
&spec->priv.jited);
|
||||||
|
if (err)
|
||||||
|
goto cleanup;
|
||||||
|
spec->mode_mask |= PRIV;
|
||||||
|
}
|
||||||
|
} else if ((msg = skip_dynamic_pfx(s, TEST_TAG_JITED_PFX_UNPRIV))) {
|
||||||
|
if (arch_mask == 0) {
|
||||||
|
PRINT_FAIL("__unpriv_jited used before __arch_*");
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (collect_jit) {
|
||||||
|
err = push_disasm_msg(msg, &unpriv_jit_on_next_line,
|
||||||
|
&spec->unpriv.jited);
|
||||||
|
if (err)
|
||||||
|
goto cleanup;
|
||||||
|
spec->mode_mask |= UNPRIV;
|
||||||
|
}
|
||||||
} else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_XLATED_PFX))) {
|
} else if ((msg = skip_dynamic_pfx(s, TEST_TAG_EXPECT_XLATED_PFX))) {
|
||||||
err = push_msg(msg, &spec->priv.expect_xlated);
|
err = push_msg(msg, &spec->priv.expect_xlated);
|
||||||
if (err)
|
if (err)
|
||||||
@ -459,16 +520,20 @@ static int parse_test_spec(struct test_loader *tester,
|
|||||||
} else if (str_has_pfx(s, TEST_TAG_ARCH)) {
|
} else if (str_has_pfx(s, TEST_TAG_ARCH)) {
|
||||||
val = s + sizeof(TEST_TAG_ARCH) - 1;
|
val = s + sizeof(TEST_TAG_ARCH) - 1;
|
||||||
if (strcmp(val, "X86_64") == 0) {
|
if (strcmp(val, "X86_64") == 0) {
|
||||||
arch_mask |= ARCH_X86_64;
|
arch = ARCH_X86_64;
|
||||||
} else if (strcmp(val, "ARM64") == 0) {
|
} else if (strcmp(val, "ARM64") == 0) {
|
||||||
arch_mask |= ARCH_ARM64;
|
arch = ARCH_ARM64;
|
||||||
} else if (strcmp(val, "RISCV64") == 0) {
|
} else if (strcmp(val, "RISCV64") == 0) {
|
||||||
arch_mask |= ARCH_RISCV64;
|
arch = ARCH_RISCV64;
|
||||||
} else {
|
} else {
|
||||||
PRINT_FAIL("bad arch spec: '%s'", val);
|
PRINT_FAIL("bad arch spec: '%s'", val);
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
arch_mask |= arch;
|
||||||
|
collect_jit = get_current_arch() == arch;
|
||||||
|
unpriv_jit_on_next_line = true;
|
||||||
|
jit_on_next_line = true;
|
||||||
} else if (str_has_pfx(s, TEST_BTF_PATH)) {
|
} else if (str_has_pfx(s, TEST_BTF_PATH)) {
|
||||||
spec->btf_custom_path = s + sizeof(TEST_BTF_PATH) - 1;
|
spec->btf_custom_path = s + sizeof(TEST_BTF_PATH) - 1;
|
||||||
}
|
}
|
||||||
@ -521,6 +586,8 @@ static int parse_test_spec(struct test_loader *tester,
|
|||||||
clone_msgs(&spec->priv.expect_msgs, &spec->unpriv.expect_msgs);
|
clone_msgs(&spec->priv.expect_msgs, &spec->unpriv.expect_msgs);
|
||||||
if (spec->unpriv.expect_xlated.cnt == 0)
|
if (spec->unpriv.expect_xlated.cnt == 0)
|
||||||
clone_msgs(&spec->priv.expect_xlated, &spec->unpriv.expect_xlated);
|
clone_msgs(&spec->priv.expect_xlated, &spec->unpriv.expect_xlated);
|
||||||
|
if (spec->unpriv.jited.cnt == 0)
|
||||||
|
clone_msgs(&spec->priv.jited, &spec->unpriv.jited);
|
||||||
}
|
}
|
||||||
|
|
||||||
spec->valid = true;
|
spec->valid = true;
|
||||||
@ -575,16 +642,29 @@ static void emit_xlated(const char *xlated, bool force)
|
|||||||
fprintf(stdout, "XLATED:\n=============\n%s=============\n", xlated);
|
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,
|
static void validate_msgs(char *log_buf, struct expected_msgs *msgs,
|
||||||
void (*emit_fn)(const char *buf, bool force))
|
void (*emit_fn)(const char *buf, bool force))
|
||||||
{
|
{
|
||||||
|
const char *log = log_buf, *prev_match;
|
||||||
regmatch_t reg_match[1];
|
regmatch_t reg_match[1];
|
||||||
const char *log = log_buf;
|
int prev_match_line;
|
||||||
|
int match_line;
|
||||||
int i, j, err;
|
int i, j, err;
|
||||||
|
|
||||||
|
prev_match_line = -1;
|
||||||
|
match_line = 0;
|
||||||
|
prev_match = log;
|
||||||
for (i = 0; i < msgs->cnt; i++) {
|
for (i = 0; i < msgs->cnt; i++) {
|
||||||
struct expect_msg *msg = &msgs->patterns[i];
|
struct expect_msg *msg = &msgs->patterns[i];
|
||||||
const char *match = NULL;
|
const char *match = NULL, *pat_status;
|
||||||
|
bool wrong_line = false;
|
||||||
|
|
||||||
if (!msg->is_regex) {
|
if (!msg->is_regex) {
|
||||||
match = strstr(log, msg->substr);
|
match = strstr(log, msg->substr);
|
||||||
@ -598,19 +678,41 @@ static void validate_msgs(char *log_buf, struct expected_msgs *msgs,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!match) {
|
if (match) {
|
||||||
|
for (; prev_match < match; ++prev_match)
|
||||||
|
if (*prev_match == '\n')
|
||||||
|
++match_line;
|
||||||
|
wrong_line = msg->on_next_line && prev_match_line >= 0 &&
|
||||||
|
prev_match_line + 1 != match_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!match || wrong_line) {
|
||||||
PRINT_FAIL("expect_msg\n");
|
PRINT_FAIL("expect_msg\n");
|
||||||
if (env.verbosity == VERBOSE_NONE)
|
if (env.verbosity == VERBOSE_NONE)
|
||||||
emit_fn(log_buf, true /*force*/);
|
emit_fn(log_buf, true /*force*/);
|
||||||
for (j = 0; j <= i; j++) {
|
for (j = 0; j <= i; j++) {
|
||||||
msg = &msgs->patterns[j];
|
msg = &msgs->patterns[j];
|
||||||
|
if (j < i)
|
||||||
|
pat_status = "MATCHED ";
|
||||||
|
else if (wrong_line)
|
||||||
|
pat_status = "WRONG LINE";
|
||||||
|
else
|
||||||
|
pat_status = "EXPECTED ";
|
||||||
|
msg = &msgs->patterns[j];
|
||||||
fprintf(stderr, "%s %s: '%s'\n",
|
fprintf(stderr, "%s %s: '%s'\n",
|
||||||
j < i ? "MATCHED " : "EXPECTED",
|
pat_status,
|
||||||
msg->is_regex ? " REGEX" : "SUBSTR",
|
msg->is_regex ? " REGEX" : "SUBSTR",
|
||||||
msg->substr);
|
msg->substr);
|
||||||
}
|
}
|
||||||
return;
|
if (wrong_line) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"expecting match at line %d, actual match is at line %d\n",
|
||||||
|
prev_match_line + 1, match_line);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev_match_line = match_line;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -769,20 +871,6 @@ out:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool run_on_current_arch(int arch_mask)
|
|
||||||
{
|
|
||||||
if (arch_mask == 0)
|
|
||||||
return true;
|
|
||||||
#if defined(__x86_64__)
|
|
||||||
return arch_mask & ARCH_X86_64;
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
return arch_mask & ARCH_ARM64;
|
|
||||||
#elif defined(__riscv) && __riscv_xlen == 64
|
|
||||||
return arch_mask & ARCH_RISCV64;
|
|
||||||
#endif
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* this function is forced noinline and has short generic name to look better
|
/* this function is forced noinline and has short generic name to look better
|
||||||
* in test_progs output (in case of a failure)
|
* in test_progs output (in case of a failure)
|
||||||
*/
|
*/
|
||||||
@ -807,7 +895,7 @@ void run_subtest(struct test_loader *tester,
|
|||||||
if (!test__start_subtest(subspec->name))
|
if (!test__start_subtest(subspec->name))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!run_on_current_arch(spec->arch_mask)) {
|
if ((get_current_arch() & spec->arch_mask) == 0) {
|
||||||
test__skip();
|
test__skip();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -884,6 +972,21 @@ void run_subtest(struct test_loader *tester,
|
|||||||
validate_msgs(tester->log_buf, &subspec->expect_xlated, emit_xlated);
|
validate_msgs(tester->log_buf, &subspec->expect_xlated, emit_xlated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (subspec->jited.cnt) {
|
||||||
|
err = get_jited_program_text(bpf_program__fd(tprog),
|
||||||
|
tester->log_buf, tester->log_buf_sz);
|
||||||
|
if (err == -EOPNOTSUPP) {
|
||||||
|
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, emit_jited);
|
||||||
|
}
|
||||||
|
|
||||||
if (should_do_test_run(spec, subspec)) {
|
if (should_do_test_run(spec, subspec)) {
|
||||||
/* For some reason test_verifier executes programs
|
/* For some reason test_verifier executes programs
|
||||||
* with all capabilities restored. Do the same here.
|
* with all capabilities restored. Do the same here.
|
||||||
|
Loading…
Reference in New Issue
Block a user