selftests: bpf: add trivial JSET tests
We seem to have no JSET instruction test, and LLVM does not generate it at all, so let's add a simple hand-coded test to make sure JIT implementations are correct. v2: - extend test_verifier to handle multiple inputs and add the sample there (Daniel) - add a sign extension case Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
This commit is contained in:
committed by
Daniel Borkmann
parent
9df95e8ec5
commit
5a8d5209ac
@@ -49,6 +49,7 @@
|
|||||||
#define MAX_INSNS BPF_MAXINSNS
|
#define MAX_INSNS BPF_MAXINSNS
|
||||||
#define MAX_FIXUPS 8
|
#define MAX_FIXUPS 8
|
||||||
#define MAX_NR_MAPS 13
|
#define MAX_NR_MAPS 13
|
||||||
|
#define MAX_TEST_RUNS 8
|
||||||
#define POINTER_VALUE 0xcafe4all
|
#define POINTER_VALUE 0xcafe4all
|
||||||
#define TEST_DATA_LEN 64
|
#define TEST_DATA_LEN 64
|
||||||
|
|
||||||
@@ -86,6 +87,14 @@ struct bpf_test {
|
|||||||
uint8_t flags;
|
uint8_t flags;
|
||||||
__u8 data[TEST_DATA_LEN];
|
__u8 data[TEST_DATA_LEN];
|
||||||
void (*fill_helper)(struct bpf_test *self);
|
void (*fill_helper)(struct bpf_test *self);
|
||||||
|
uint8_t runs;
|
||||||
|
struct {
|
||||||
|
uint32_t retval, retval_unpriv;
|
||||||
|
union {
|
||||||
|
__u8 data[TEST_DATA_LEN];
|
||||||
|
__u64 data64[TEST_DATA_LEN / 8];
|
||||||
|
};
|
||||||
|
} retvals[MAX_TEST_RUNS];
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Note we want this to be 64 bit aligned so that the end of our array is
|
/* Note we want this to be 64 bit aligned so that the end of our array is
|
||||||
@@ -14161,6 +14170,101 @@ static struct bpf_test tests[] = {
|
|||||||
.errstr_unpriv = "R1 leaks addr",
|
.errstr_unpriv = "R1 leaks addr",
|
||||||
.result = REJECT,
|
.result = REJECT,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"jset: functional",
|
||||||
|
.insns = {
|
||||||
|
/* r0 = 0 */
|
||||||
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||||
|
/* prep for direct packet access via r2 */
|
||||||
|
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
|
||||||
|
offsetof(struct __sk_buff, data)),
|
||||||
|
BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
|
||||||
|
offsetof(struct __sk_buff, data_end)),
|
||||||
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_2),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 8),
|
||||||
|
BPF_JMP_REG(BPF_JLE, BPF_REG_4, BPF_REG_3, 1),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
|
||||||
|
BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_2, 0),
|
||||||
|
|
||||||
|
/* reg, bit 63 or bit 0 set, taken */
|
||||||
|
BPF_LD_IMM64(BPF_REG_8, 0x8000000000000001),
|
||||||
|
BPF_JMP_REG(BPF_JSET, BPF_REG_7, BPF_REG_8, 1),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
|
||||||
|
/* reg, bit 62, not taken */
|
||||||
|
BPF_LD_IMM64(BPF_REG_8, 0x4000000000000000),
|
||||||
|
BPF_JMP_REG(BPF_JSET, BPF_REG_7, BPF_REG_8, 1),
|
||||||
|
BPF_JMP_IMM(BPF_JA, 0, 0, 1),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
|
||||||
|
/* imm, any bit set, taken */
|
||||||
|
BPF_JMP_IMM(BPF_JSET, BPF_REG_7, -1, 1),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
|
||||||
|
/* imm, bit 31 set, taken */
|
||||||
|
BPF_JMP_IMM(BPF_JSET, BPF_REG_7, 0x80000000, 1),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
|
||||||
|
/* all good - return r0 == 2 */
|
||||||
|
BPF_MOV64_IMM(BPF_REG_0, 2),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||||
|
.result = ACCEPT,
|
||||||
|
.runs = 7,
|
||||||
|
.retvals = {
|
||||||
|
{ .retval = 2,
|
||||||
|
.data64 = { (1ULL << 63) | (1U << 31) | (1U << 0), }
|
||||||
|
},
|
||||||
|
{ .retval = 2,
|
||||||
|
.data64 = { (1ULL << 63) | (1U << 31), }
|
||||||
|
},
|
||||||
|
{ .retval = 2,
|
||||||
|
.data64 = { (1ULL << 31) | (1U << 0), }
|
||||||
|
},
|
||||||
|
{ .retval = 2,
|
||||||
|
.data64 = { (__u32)-1, }
|
||||||
|
},
|
||||||
|
{ .retval = 2,
|
||||||
|
.data64 = { ~0x4000000000000000ULL, }
|
||||||
|
},
|
||||||
|
{ .retval = 0,
|
||||||
|
.data64 = { 0, }
|
||||||
|
},
|
||||||
|
{ .retval = 0,
|
||||||
|
.data64 = { ~0ULL, }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"jset: sign-extend",
|
||||||
|
.insns = {
|
||||||
|
/* r0 = 0 */
|
||||||
|
BPF_MOV64_IMM(BPF_REG_0, 0),
|
||||||
|
/* prep for direct packet access via r2 */
|
||||||
|
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
|
||||||
|
offsetof(struct __sk_buff, data)),
|
||||||
|
BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
|
||||||
|
offsetof(struct __sk_buff, data_end)),
|
||||||
|
BPF_MOV64_REG(BPF_REG_4, BPF_REG_2),
|
||||||
|
BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 8),
|
||||||
|
BPF_JMP_REG(BPF_JLE, BPF_REG_4, BPF_REG_3, 1),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
|
||||||
|
BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_2, 0),
|
||||||
|
|
||||||
|
BPF_JMP_IMM(BPF_JSET, BPF_REG_7, 0x80000000, 1),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
|
||||||
|
BPF_MOV64_IMM(BPF_REG_0, 2),
|
||||||
|
BPF_EXIT_INSN(),
|
||||||
|
},
|
||||||
|
.prog_type = BPF_PROG_TYPE_SCHED_CLS,
|
||||||
|
.result = ACCEPT,
|
||||||
|
.retval = 2,
|
||||||
|
.data = { 1, 0, 0, 0, 0, 0, 0, 1, },
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int probe_filter_length(const struct bpf_insn *fp)
|
static int probe_filter_length(const struct bpf_insn *fp)
|
||||||
@@ -14443,16 +14547,42 @@ out:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int do_prog_test_run(int fd_prog, bool unpriv, uint32_t expected_val,
|
||||||
|
void *data, size_t size_data)
|
||||||
|
{
|
||||||
|
__u8 tmp[TEST_DATA_LEN << 2];
|
||||||
|
__u32 size_tmp = sizeof(tmp);
|
||||||
|
uint32_t retval;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (unpriv)
|
||||||
|
set_admin(true);
|
||||||
|
err = bpf_prog_test_run(fd_prog, 1, data, size_data,
|
||||||
|
tmp, &size_tmp, &retval, NULL);
|
||||||
|
if (unpriv)
|
||||||
|
set_admin(false);
|
||||||
|
if (err && errno != 524/*ENOTSUPP*/ && errno != EPERM) {
|
||||||
|
printf("Unexpected bpf_prog_test_run error ");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (!err && retval != expected_val &&
|
||||||
|
expected_val != POINTER_VALUE) {
|
||||||
|
printf("FAIL retval %d != %d ", retval, expected_val);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void do_test_single(struct bpf_test *test, bool unpriv,
|
static void do_test_single(struct bpf_test *test, bool unpriv,
|
||||||
int *passes, int *errors)
|
int *passes, int *errors)
|
||||||
{
|
{
|
||||||
int fd_prog, expected_ret, alignment_prevented_execution;
|
int fd_prog, expected_ret, alignment_prevented_execution;
|
||||||
int prog_len, prog_type = test->prog_type;
|
int prog_len, prog_type = test->prog_type;
|
||||||
struct bpf_insn *prog = test->insns;
|
struct bpf_insn *prog = test->insns;
|
||||||
|
int run_errs, run_successes;
|
||||||
int map_fds[MAX_NR_MAPS];
|
int map_fds[MAX_NR_MAPS];
|
||||||
const char *expected_err;
|
const char *expected_err;
|
||||||
uint32_t expected_val;
|
|
||||||
uint32_t retval;
|
|
||||||
__u32 pflags;
|
__u32 pflags;
|
||||||
int i, err;
|
int i, err;
|
||||||
|
|
||||||
@@ -14476,8 +14606,6 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
|
|||||||
test->result_unpriv : test->result;
|
test->result_unpriv : test->result;
|
||||||
expected_err = unpriv && test->errstr_unpriv ?
|
expected_err = unpriv && test->errstr_unpriv ?
|
||||||
test->errstr_unpriv : test->errstr;
|
test->errstr_unpriv : test->errstr;
|
||||||
expected_val = unpriv && test->retval_unpriv ?
|
|
||||||
test->retval_unpriv : test->retval;
|
|
||||||
|
|
||||||
alignment_prevented_execution = 0;
|
alignment_prevented_execution = 0;
|
||||||
|
|
||||||
@@ -14489,10 +14617,8 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
|
|||||||
}
|
}
|
||||||
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
|
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
|
||||||
if (fd_prog >= 0 &&
|
if (fd_prog >= 0 &&
|
||||||
(test->flags & F_NEEDS_EFFICIENT_UNALIGNED_ACCESS)) {
|
(test->flags & F_NEEDS_EFFICIENT_UNALIGNED_ACCESS))
|
||||||
alignment_prevented_execution = 1;
|
alignment_prevented_execution = 1;
|
||||||
goto test_ok;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
if (fd_prog >= 0) {
|
if (fd_prog >= 0) {
|
||||||
@@ -14519,33 +14645,54 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fd_prog >= 0) {
|
run_errs = 0;
|
||||||
__u8 tmp[TEST_DATA_LEN << 2];
|
run_successes = 0;
|
||||||
__u32 size_tmp = sizeof(tmp);
|
if (!alignment_prevented_execution && fd_prog >= 0) {
|
||||||
|
uint32_t expected_val;
|
||||||
|
int i;
|
||||||
|
|
||||||
if (unpriv)
|
if (!test->runs) {
|
||||||
set_admin(true);
|
expected_val = unpriv && test->retval_unpriv ?
|
||||||
err = bpf_prog_test_run(fd_prog, 1, test->data,
|
test->retval_unpriv : test->retval;
|
||||||
sizeof(test->data), tmp, &size_tmp,
|
|
||||||
&retval, NULL);
|
err = do_prog_test_run(fd_prog, unpriv, expected_val,
|
||||||
if (unpriv)
|
test->data, sizeof(test->data));
|
||||||
set_admin(false);
|
if (err)
|
||||||
if (err && errno != 524/*ENOTSUPP*/ && errno != EPERM) {
|
run_errs++;
|
||||||
printf("Unexpected bpf_prog_test_run error\n");
|
else
|
||||||
goto fail_log;
|
run_successes++;
|
||||||
}
|
}
|
||||||
if (!err && retval != expected_val &&
|
|
||||||
expected_val != POINTER_VALUE) {
|
for (i = 0; i < test->runs; i++) {
|
||||||
printf("FAIL retval %d != %d\n", retval, expected_val);
|
if (unpriv && test->retvals[i].retval_unpriv)
|
||||||
goto fail_log;
|
expected_val = test->retvals[i].retval_unpriv;
|
||||||
|
else
|
||||||
|
expected_val = test->retvals[i].retval;
|
||||||
|
|
||||||
|
err = do_prog_test_run(fd_prog, unpriv, expected_val,
|
||||||
|
test->retvals[i].data,
|
||||||
|
sizeof(test->retvals[i].data));
|
||||||
|
if (err) {
|
||||||
|
printf("(run %d/%d) ", i + 1, test->runs);
|
||||||
|
run_errs++;
|
||||||
|
} else {
|
||||||
|
run_successes++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
|
|
||||||
test_ok:
|
if (!run_errs) {
|
||||||
#endif
|
(*passes)++;
|
||||||
(*passes)++;
|
if (run_successes > 1)
|
||||||
printf("OK%s\n", alignment_prevented_execution ?
|
printf("%d cases ", run_successes);
|
||||||
" (NOTE: not executed due to unknown alignment)" : "");
|
printf("OK");
|
||||||
|
if (alignment_prevented_execution)
|
||||||
|
printf(" (NOTE: not executed due to unknown alignment)");
|
||||||
|
printf("\n");
|
||||||
|
} else {
|
||||||
|
printf("\n");
|
||||||
|
goto fail_log;
|
||||||
|
}
|
||||||
close_fds:
|
close_fds:
|
||||||
close(fd_prog);
|
close(fd_prog);
|
||||||
for (i = 0; i < MAX_NR_MAPS; i++)
|
for (i = 0; i < MAX_NR_MAPS; i++)
|
||||||
|
|||||||
Reference in New Issue
Block a user