|
@@ -21,6 +21,7 @@
|
|
|
#include <linux/skbuff.h>
|
|
|
#include <linux/netdevice.h>
|
|
|
#include <linux/if_vlan.h>
|
|
|
+#include <linux/random.h>
|
|
|
|
|
|
/* General test specific settings */
|
|
|
#define MAX_SUBTESTS 3
|
|
@@ -67,6 +68,10 @@ struct bpf_test {
|
|
|
union {
|
|
|
struct sock_filter insns[MAX_INSNS];
|
|
|
struct bpf_insn insns_int[MAX_INSNS];
|
|
|
+ struct {
|
|
|
+ void *insns;
|
|
|
+ unsigned int len;
|
|
|
+ } ptr;
|
|
|
} u;
|
|
|
__u8 aux;
|
|
|
__u8 data[MAX_DATA];
|
|
@@ -74,8 +79,190 @@ struct bpf_test {
|
|
|
int data_size;
|
|
|
__u32 result;
|
|
|
} 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[] = {
|
|
|
{
|
|
|
"TAX",
|
|
@@ -3998,6 +4185,73 @@ static struct bpf_test tests[] = {
|
|
|
{ },
|
|
|
{ { 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;
|
|
@@ -4051,10 +4305,15 @@ static void release_test_data(const struct bpf_test *test, void *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)
|
|
|
if (fp[len].code != 0 || fp[len].k != 0)
|
|
|
break;
|
|
@@ -4062,16 +4321,25 @@ static int probe_filter_length(struct sock_filter *fp)
|
|
|
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)
|
|
|
{
|
|
|
- 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;
|
|
|
+ unsigned int flen = filter_length(which);
|
|
|
+ void *fptr = filter_pointer(which);
|
|
|
+ struct sock_fprog_kern fprog;
|
|
|
+ struct bpf_prog *fp;
|
|
|
|
|
|
switch (test_type) {
|
|
|
case CLASSIC:
|
|
|
- fprog.filter = tests[which].u.insns;
|
|
|
+ fprog.filter = fptr;
|
|
|
fprog.len = flen;
|
|
|
|
|
|
*err = bpf_prog_create(&fp, &fprog);
|
|
@@ -4107,8 +4375,7 @@ static struct bpf_prog *generate_filter(int which, int *err)
|
|
|
}
|
|
|
|
|
|
fp->len = flen;
|
|
|
- memcpy(fp->insnsi, tests[which].u.insns_int,
|
|
|
- fp->len * sizeof(struct bpf_insn));
|
|
|
+ memcpy(fp->insnsi, fptr, fp->len * sizeof(struct bpf_insn));
|
|
|
|
|
|
bpf_prog_select_runtime(fp);
|
|
|
break;
|
|
@@ -4180,6 +4447,29 @@ static int run_one(const struct bpf_prog *fp, struct bpf_test *test)
|
|
|
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)
|
|
|
{
|
|
|
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)
|
|
|
{
|
|
|
- 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)
|