test_bpf: add tests related to BPF_MAXINSNS
Couple of torture test cases related to the bug fixed in 0b59d8806a
("ARM: net: delegate filter to kernel interpreter when imm_offset()
return value can't fit into 12bits.").
I've added a helper to allocate and fill the insn space. Output on
x86_64 from my laptop:
test_bpf: #233 BPF_MAXINSNS: Maximum possible literals jited:0 7 PASS
test_bpf: #234 BPF_MAXINSNS: Single literal jited:0 8 PASS
test_bpf: #235 BPF_MAXINSNS: Run/add until end jited:0 11553 PASS
test_bpf: #236 BPF_MAXINSNS: Too many instructions PASS
test_bpf: #237 BPF_MAXINSNS: Very long jump jited:0 9 PASS
test_bpf: #238 BPF_MAXINSNS: Ctx heavy transformations jited:0 20329 20398 PASS
test_bpf: #239 BPF_MAXINSNS: Call heavy transformations jited:0 32178 32475 PASS
test_bpf: #240 BPF_MAXINSNS: Jump heavy test jited:0 10518 PASS
test_bpf: #233 BPF_MAXINSNS: Maximum possible literals jited:1 4 PASS
test_bpf: #234 BPF_MAXINSNS: Single literal jited:1 4 PASS
test_bpf: #235 BPF_MAXINSNS: Run/add until end jited:1 1625 PASS
test_bpf: #236 BPF_MAXINSNS: Too many instructions PASS
test_bpf: #237 BPF_MAXINSNS: Very long jump jited:1 8 PASS
test_bpf: #238 BPF_MAXINSNS: Ctx heavy transformations jited:1 3301 3174 PASS
test_bpf: #239 BPF_MAXINSNS: Call heavy transformations jited:1 24107 23491 PASS
test_bpf: #240 BPF_MAXINSNS: Jump heavy test jited:1 8651 PASS
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Cc: Alexei Starovoitov <ast@plumgrid.com>
Cc: Nicolas Schichan <nschichan@freebox.fr>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
264ea103a7
commit
a4afd37b26
|
@ -277,6 +277,14 @@ struct bpf_prog_aux;
|
||||||
.off = 0, \
|
.off = 0, \
|
||||||
.imm = 0 })
|
.imm = 0 })
|
||||||
|
|
||||||
|
/* Internal classic blocks for direct assignment */
|
||||||
|
|
||||||
|
#define __BPF_STMT(CODE, K) \
|
||||||
|
((struct sock_filter) BPF_STMT(CODE, K))
|
||||||
|
|
||||||
|
#define __BPF_JUMP(CODE, K, JT, JF) \
|
||||||
|
((struct sock_filter) BPF_JUMP(CODE, K, JT, JF))
|
||||||
|
|
||||||
#define bytes_to_bpf_size(bytes) \
|
#define bytes_to_bpf_size(bytes) \
|
||||||
({ \
|
({ \
|
||||||
int bpf_size = -EINVAL; \
|
int bpf_size = -EINVAL; \
|
||||||
|
|
317
lib/test_bpf.c
317
lib/test_bpf.c
|
@ -21,6 +21,7 @@
|
||||||
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
||||||
#include <linux/netdevice.h>
|
#include <linux/netdevice.h>
|
||||||
#include <linux/if_vlan.h>
|
#include <linux/if_vlan.h>
|
||||||
|
#include <linux/random.h>
|
||||||
|
|
||||||
/* General test specific settings */
|
/* General test specific settings */
|
||||||
#define MAX_SUBTESTS 3
|
#define MAX_SUBTESTS 3
|
||||||
|
@ -67,6 +68,10 @@ struct bpf_test {
|
||||||
union {
|
union {
|
||||||
struct sock_filter insns[MAX_INSNS];
|
struct sock_filter insns[MAX_INSNS];
|
||||||
struct bpf_insn insns_int[MAX_INSNS];
|
struct bpf_insn insns_int[MAX_INSNS];
|
||||||
|
struct {
|
||||||
|
void *insns;
|
||||||
|
unsigned int len;
|
||||||
|
} ptr;
|
||||||
} u;
|
} u;
|
||||||
__u8 aux;
|
__u8 aux;
|
||||||
__u8 data[MAX_DATA];
|
__u8 data[MAX_DATA];
|
||||||
|
@ -74,8 +79,190 @@ struct bpf_test {
|
||||||
int data_size;
|
int data_size;
|
||||||
__u32 result;
|
__u32 result;
|
||||||
} test[MAX_SUBTESTS];
|
} test[MAX_SUBTESTS];
|
||||||
|
int (*fill_helper)(struct bpf_test *self);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Large test cases need separate allocation and fill handler. */
|
||||||
|
|
||||||
|
static int bpf_fill_maxinsns1(struct bpf_test *self)
|
||||||
|
{
|
||||||
|
unsigned int len = BPF_MAXINSNS;
|
||||||
|
struct sock_filter *insn;
|
||||||
|
__u32 k = ~0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
insn = kmalloc_array(len, sizeof(*insn), GFP_KERNEL);
|
||||||
|
if (!insn)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++, k--)
|
||||||
|
insn[i] = __BPF_STMT(BPF_RET | BPF_K, k);
|
||||||
|
|
||||||
|
self->u.ptr.insns = insn;
|
||||||
|
self->u.ptr.len = len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bpf_fill_maxinsns2(struct bpf_test *self)
|
||||||
|
{
|
||||||
|
unsigned int len = BPF_MAXINSNS;
|
||||||
|
struct sock_filter *insn;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
insn = kmalloc_array(len, sizeof(*insn), GFP_KERNEL);
|
||||||
|
if (!insn)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
insn[i] = __BPF_STMT(BPF_RET | BPF_K, 0xfefefefe);
|
||||||
|
|
||||||
|
self->u.ptr.insns = insn;
|
||||||
|
self->u.ptr.len = len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bpf_fill_maxinsns3(struct bpf_test *self)
|
||||||
|
{
|
||||||
|
unsigned int len = BPF_MAXINSNS;
|
||||||
|
struct sock_filter *insn;
|
||||||
|
struct rnd_state rnd;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
insn = kmalloc_array(len, sizeof(*insn), GFP_KERNEL);
|
||||||
|
if (!insn)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
prandom_seed_state(&rnd, 3141592653589793238ULL);
|
||||||
|
|
||||||
|
for (i = 0; i < len - 1; i++) {
|
||||||
|
__u32 k = prandom_u32_state(&rnd);
|
||||||
|
|
||||||
|
insn[i] = __BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, k);
|
||||||
|
}
|
||||||
|
|
||||||
|
insn[len - 1] = __BPF_STMT(BPF_RET | BPF_A, 0);
|
||||||
|
|
||||||
|
self->u.ptr.insns = insn;
|
||||||
|
self->u.ptr.len = len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bpf_fill_maxinsns4(struct bpf_test *self)
|
||||||
|
{
|
||||||
|
unsigned int len = BPF_MAXINSNS + 1;
|
||||||
|
struct sock_filter *insn;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
insn = kmalloc_array(len, sizeof(*insn), GFP_KERNEL);
|
||||||
|
if (!insn)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
insn[i] = __BPF_STMT(BPF_RET | BPF_K, 0xfefefefe);
|
||||||
|
|
||||||
|
self->u.ptr.insns = insn;
|
||||||
|
self->u.ptr.len = len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bpf_fill_maxinsns5(struct bpf_test *self)
|
||||||
|
{
|
||||||
|
unsigned int len = BPF_MAXINSNS;
|
||||||
|
struct sock_filter *insn;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
insn = kmalloc_array(len, sizeof(*insn), GFP_KERNEL);
|
||||||
|
if (!insn)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
insn[0] = __BPF_JUMP(BPF_JMP | BPF_JA, len - 2, 0, 0);
|
||||||
|
|
||||||
|
for (i = 1; i < len - 1; i++)
|
||||||
|
insn[i] = __BPF_STMT(BPF_RET | BPF_K, 0xfefefefe);
|
||||||
|
|
||||||
|
insn[len - 1] = __BPF_STMT(BPF_RET | BPF_K, 0xabababab);
|
||||||
|
|
||||||
|
self->u.ptr.insns = insn;
|
||||||
|
self->u.ptr.len = len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bpf_fill_maxinsns6(struct bpf_test *self)
|
||||||
|
{
|
||||||
|
unsigned int len = BPF_MAXINSNS;
|
||||||
|
struct sock_filter *insn;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
insn = kmalloc_array(len, sizeof(*insn), GFP_KERNEL);
|
||||||
|
if (!insn)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < len - 1; i++)
|
||||||
|
insn[i] = __BPF_STMT(BPF_LD | BPF_W | BPF_ABS, SKF_AD_OFF +
|
||||||
|
SKF_AD_VLAN_TAG_PRESENT);
|
||||||
|
|
||||||
|
insn[len - 1] = __BPF_STMT(BPF_RET | BPF_A, 0);
|
||||||
|
|
||||||
|
self->u.ptr.insns = insn;
|
||||||
|
self->u.ptr.len = len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bpf_fill_maxinsns7(struct bpf_test *self)
|
||||||
|
{
|
||||||
|
unsigned int len = BPF_MAXINSNS;
|
||||||
|
struct sock_filter *insn;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
insn = kmalloc_array(len, sizeof(*insn), GFP_KERNEL);
|
||||||
|
if (!insn)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < len - 4; i++)
|
||||||
|
insn[i] = __BPF_STMT(BPF_LD | BPF_W | BPF_ABS, SKF_AD_OFF +
|
||||||
|
SKF_AD_CPU);
|
||||||
|
|
||||||
|
insn[len - 4] = __BPF_STMT(BPF_MISC | BPF_TAX, 0);
|
||||||
|
insn[len - 3] = __BPF_STMT(BPF_LD | BPF_W | BPF_ABS, SKF_AD_OFF +
|
||||||
|
SKF_AD_CPU);
|
||||||
|
insn[len - 2] = __BPF_STMT(BPF_ALU | BPF_SUB | BPF_X, 0);
|
||||||
|
insn[len - 1] = __BPF_STMT(BPF_RET | BPF_A, 0);
|
||||||
|
|
||||||
|
self->u.ptr.insns = insn;
|
||||||
|
self->u.ptr.len = len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bpf_fill_maxinsns8(struct bpf_test *self)
|
||||||
|
{
|
||||||
|
unsigned int len = BPF_MAXINSNS;
|
||||||
|
struct sock_filter *insn;
|
||||||
|
int i, jmp_off = len - 3;
|
||||||
|
|
||||||
|
insn = kmalloc_array(len, sizeof(*insn), GFP_KERNEL);
|
||||||
|
if (!insn)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
insn[0] = __BPF_STMT(BPF_LD | BPF_IMM, 0xffffffff);
|
||||||
|
|
||||||
|
for (i = 1; i < len - 1; i++)
|
||||||
|
insn[i] = __BPF_JUMP(BPF_JMP | BPF_JGT, 0xffffffff, jmp_off--, 0);
|
||||||
|
|
||||||
|
insn[len - 1] = __BPF_STMT(BPF_RET | BPF_A, 0);
|
||||||
|
|
||||||
|
self->u.ptr.insns = insn;
|
||||||
|
self->u.ptr.len = len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct bpf_test tests[] = {
|
static struct bpf_test tests[] = {
|
||||||
{
|
{
|
||||||
"TAX",
|
"TAX",
|
||||||
|
@ -3998,6 +4185,73 @@ static struct bpf_test tests[] = {
|
||||||
{ },
|
{ },
|
||||||
{ { 0, 1 } },
|
{ { 0, 1 } },
|
||||||
},
|
},
|
||||||
|
{ /* Mainly checking JIT here. */
|
||||||
|
"BPF_MAXINSNS: Maximum possible literals",
|
||||||
|
{ },
|
||||||
|
CLASSIC | FLAG_NO_DATA,
|
||||||
|
{ },
|
||||||
|
{ { 0, 0xffffffff } },
|
||||||
|
.fill_helper = bpf_fill_maxinsns1,
|
||||||
|
},
|
||||||
|
{ /* Mainly checking JIT here. */
|
||||||
|
"BPF_MAXINSNS: Single literal",
|
||||||
|
{ },
|
||||||
|
CLASSIC | FLAG_NO_DATA,
|
||||||
|
{ },
|
||||||
|
{ { 0, 0xfefefefe } },
|
||||||
|
.fill_helper = bpf_fill_maxinsns2,
|
||||||
|
},
|
||||||
|
{ /* Mainly checking JIT here. */
|
||||||
|
"BPF_MAXINSNS: Run/add until end",
|
||||||
|
{ },
|
||||||
|
CLASSIC | FLAG_NO_DATA,
|
||||||
|
{ },
|
||||||
|
{ { 0, 0x947bf368 } },
|
||||||
|
.fill_helper = bpf_fill_maxinsns3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"BPF_MAXINSNS: Too many instructions",
|
||||||
|
{ },
|
||||||
|
CLASSIC | FLAG_NO_DATA | FLAG_EXPECTED_FAIL,
|
||||||
|
{ },
|
||||||
|
{ },
|
||||||
|
.fill_helper = bpf_fill_maxinsns4,
|
||||||
|
},
|
||||||
|
{ /* Mainly checking JIT here. */
|
||||||
|
"BPF_MAXINSNS: Very long jump",
|
||||||
|
{ },
|
||||||
|
CLASSIC | FLAG_NO_DATA,
|
||||||
|
{ },
|
||||||
|
{ { 0, 0xabababab } },
|
||||||
|
.fill_helper = bpf_fill_maxinsns5,
|
||||||
|
},
|
||||||
|
{ /* Mainly checking JIT here. */
|
||||||
|
"BPF_MAXINSNS: Ctx heavy transformations",
|
||||||
|
{ },
|
||||||
|
CLASSIC,
|
||||||
|
{ },
|
||||||
|
{
|
||||||
|
{ 1, !!(SKB_VLAN_TCI & VLAN_TAG_PRESENT) },
|
||||||
|
{ 10, !!(SKB_VLAN_TCI & VLAN_TAG_PRESENT) }
|
||||||
|
},
|
||||||
|
.fill_helper = bpf_fill_maxinsns6,
|
||||||
|
},
|
||||||
|
{ /* Mainly checking JIT here. */
|
||||||
|
"BPF_MAXINSNS: Call heavy transformations",
|
||||||
|
{ },
|
||||||
|
CLASSIC | FLAG_NO_DATA,
|
||||||
|
{ },
|
||||||
|
{ { 1, 0 }, { 10, 0 } },
|
||||||
|
.fill_helper = bpf_fill_maxinsns7,
|
||||||
|
},
|
||||||
|
{ /* Mainly checking JIT here. */
|
||||||
|
"BPF_MAXINSNS: Jump heavy test",
|
||||||
|
{ },
|
||||||
|
CLASSIC | FLAG_NO_DATA,
|
||||||
|
{ },
|
||||||
|
{ { 0, 0xffffffff } },
|
||||||
|
.fill_helper = bpf_fill_maxinsns8,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct net_device dev;
|
static struct net_device dev;
|
||||||
|
@ -4051,10 +4305,15 @@ static void release_test_data(const struct bpf_test *test, void *data)
|
||||||
kfree_skb(data);
|
kfree_skb(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int probe_filter_length(struct sock_filter *fp)
|
static int filter_length(int which)
|
||||||
{
|
{
|
||||||
int len = 0;
|
struct sock_filter *fp;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (tests[which].fill_helper)
|
||||||
|
return tests[which].u.ptr.len;
|
||||||
|
|
||||||
|
fp = tests[which].u.insns;
|
||||||
for (len = MAX_INSNS - 1; len > 0; --len)
|
for (len = MAX_INSNS - 1; len > 0; --len)
|
||||||
if (fp[len].code != 0 || fp[len].k != 0)
|
if (fp[len].code != 0 || fp[len].k != 0)
|
||||||
break;
|
break;
|
||||||
|
@ -4062,16 +4321,25 @@ static int probe_filter_length(struct sock_filter *fp)
|
||||||
return len + 1;
|
return len + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *filter_pointer(int which)
|
||||||
|
{
|
||||||
|
if (tests[which].fill_helper)
|
||||||
|
return tests[which].u.ptr.insns;
|
||||||
|
else
|
||||||
|
return tests[which].u.insns;
|
||||||
|
}
|
||||||
|
|
||||||
static struct bpf_prog *generate_filter(int which, int *err)
|
static struct bpf_prog *generate_filter(int which, int *err)
|
||||||
{
|
{
|
||||||
struct bpf_prog *fp;
|
|
||||||
struct sock_fprog_kern fprog;
|
|
||||||
unsigned int flen = probe_filter_length(tests[which].u.insns);
|
|
||||||
__u8 test_type = tests[which].aux & TEST_TYPE_MASK;
|
__u8 test_type = tests[which].aux & TEST_TYPE_MASK;
|
||||||
|
unsigned int flen = filter_length(which);
|
||||||
|
void *fptr = filter_pointer(which);
|
||||||
|
struct sock_fprog_kern fprog;
|
||||||
|
struct bpf_prog *fp;
|
||||||
|
|
||||||
switch (test_type) {
|
switch (test_type) {
|
||||||
case CLASSIC:
|
case CLASSIC:
|
||||||
fprog.filter = tests[which].u.insns;
|
fprog.filter = fptr;
|
||||||
fprog.len = flen;
|
fprog.len = flen;
|
||||||
|
|
||||||
*err = bpf_prog_create(&fp, &fprog);
|
*err = bpf_prog_create(&fp, &fprog);
|
||||||
|
@ -4107,8 +4375,7 @@ static struct bpf_prog *generate_filter(int which, int *err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fp->len = flen;
|
fp->len = flen;
|
||||||
memcpy(fp->insnsi, tests[which].u.insns_int,
|
memcpy(fp->insnsi, fptr, fp->len * sizeof(struct bpf_insn));
|
||||||
fp->len * sizeof(struct bpf_insn));
|
|
||||||
|
|
||||||
bpf_prog_select_runtime(fp);
|
bpf_prog_select_runtime(fp);
|
||||||
break;
|
break;
|
||||||
|
@ -4180,6 +4447,29 @@ static int run_one(const struct bpf_prog *fp, struct bpf_test *test)
|
||||||
return err_cnt;
|
return err_cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static __init int prepare_bpf_tests(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||||
|
if (tests[i].fill_helper &&
|
||||||
|
tests[i].fill_helper(&tests[i]) < 0)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static __init void destroy_bpf_tests(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||||
|
if (tests[i].fill_helper)
|
||||||
|
kfree(tests[i].u.ptr.insns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static __init int test_bpf(void)
|
static __init int test_bpf(void)
|
||||||
{
|
{
|
||||||
int i, err_cnt = 0, pass_cnt = 0;
|
int i, err_cnt = 0, pass_cnt = 0;
|
||||||
|
@ -4227,7 +4517,16 @@ static __init int test_bpf(void)
|
||||||
|
|
||||||
static int __init test_bpf_init(void)
|
static int __init test_bpf_init(void)
|
||||||
{
|
{
|
||||||
return test_bpf();
|
int ret;
|
||||||
|
|
||||||
|
ret = prepare_bpf_tests();
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = test_bpf();
|
||||||
|
|
||||||
|
destroy_bpf_tests();
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit test_bpf_exit(void)
|
static void __exit test_bpf_exit(void)
|
||||||
|
|
Loading…
Reference in New Issue