|
@@ -23,6 +23,7 @@
|
|
|
#include <linux/netdevice.h>
|
|
|
#include <linux/if_vlan.h>
|
|
|
#include <linux/random.h>
|
|
|
+#include <linux/highmem.h>
|
|
|
|
|
|
/* General test specific settings */
|
|
|
#define MAX_SUBTESTS 3
|
|
@@ -56,6 +57,7 @@
|
|
|
/* Flags that can be passed to test cases */
|
|
|
#define FLAG_NO_DATA BIT(0)
|
|
|
#define FLAG_EXPECTED_FAIL BIT(1)
|
|
|
+#define FLAG_SKB_FRAG BIT(2)
|
|
|
|
|
|
enum {
|
|
|
CLASSIC = BIT(6), /* Old BPF instructions only. */
|
|
@@ -81,6 +83,7 @@ struct bpf_test {
|
|
|
__u32 result;
|
|
|
} test[MAX_SUBTESTS];
|
|
|
int (*fill_helper)(struct bpf_test *self);
|
|
|
+ __u8 frag_data[MAX_DATA];
|
|
|
};
|
|
|
|
|
|
/* Large test cases need separate allocation and fill handler. */
|
|
@@ -4490,6 +4493,602 @@ static struct bpf_test tests[] = {
|
|
|
{ { 1, 0xbef } },
|
|
|
.fill_helper = bpf_fill_ld_abs_vlan_push_pop,
|
|
|
},
|
|
|
+ /*
|
|
|
+ * LD_IND / LD_ABS on fragmented SKBs
|
|
|
+ */
|
|
|
+ {
|
|
|
+ "LD_IND byte frag",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LDX | BPF_IMM, 0x40),
|
|
|
+ BPF_STMT(BPF_LD | BPF_IND | BPF_B, 0x0),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_SKB_FRAG,
|
|
|
+ { },
|
|
|
+ { {0x40, 0x42} },
|
|
|
+ .frag_data = {
|
|
|
+ 0x42, 0x00, 0x00, 0x00,
|
|
|
+ 0x43, 0x44, 0x00, 0x00,
|
|
|
+ 0x21, 0x07, 0x19, 0x83,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_IND halfword frag",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LDX | BPF_IMM, 0x40),
|
|
|
+ BPF_STMT(BPF_LD | BPF_IND | BPF_H, 0x4),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_SKB_FRAG,
|
|
|
+ { },
|
|
|
+ { {0x40, 0x4344} },
|
|
|
+ .frag_data = {
|
|
|
+ 0x42, 0x00, 0x00, 0x00,
|
|
|
+ 0x43, 0x44, 0x00, 0x00,
|
|
|
+ 0x21, 0x07, 0x19, 0x83,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_IND word frag",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LDX | BPF_IMM, 0x40),
|
|
|
+ BPF_STMT(BPF_LD | BPF_IND | BPF_W, 0x8),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_SKB_FRAG,
|
|
|
+ { },
|
|
|
+ { {0x40, 0x21071983} },
|
|
|
+ .frag_data = {
|
|
|
+ 0x42, 0x00, 0x00, 0x00,
|
|
|
+ 0x43, 0x44, 0x00, 0x00,
|
|
|
+ 0x21, 0x07, 0x19, 0x83,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_IND halfword mixed head/frag",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LDX | BPF_IMM, 0x40),
|
|
|
+ BPF_STMT(BPF_LD | BPF_IND | BPF_H, -0x1),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_SKB_FRAG,
|
|
|
+ { [0x3e] = 0x25, [0x3f] = 0x05, },
|
|
|
+ { {0x40, 0x0519} },
|
|
|
+ .frag_data = { 0x19, 0x82 },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_IND word mixed head/frag",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LDX | BPF_IMM, 0x40),
|
|
|
+ BPF_STMT(BPF_LD | BPF_IND | BPF_W, -0x2),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_SKB_FRAG,
|
|
|
+ { [0x3e] = 0x25, [0x3f] = 0x05, },
|
|
|
+ { {0x40, 0x25051982} },
|
|
|
+ .frag_data = { 0x19, 0x82 },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_ABS byte frag",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LD | BPF_ABS | BPF_B, 0x40),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_SKB_FRAG,
|
|
|
+ { },
|
|
|
+ { {0x40, 0x42} },
|
|
|
+ .frag_data = {
|
|
|
+ 0x42, 0x00, 0x00, 0x00,
|
|
|
+ 0x43, 0x44, 0x00, 0x00,
|
|
|
+ 0x21, 0x07, 0x19, 0x83,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_ABS halfword frag",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LD | BPF_ABS | BPF_H, 0x44),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_SKB_FRAG,
|
|
|
+ { },
|
|
|
+ { {0x40, 0x4344} },
|
|
|
+ .frag_data = {
|
|
|
+ 0x42, 0x00, 0x00, 0x00,
|
|
|
+ 0x43, 0x44, 0x00, 0x00,
|
|
|
+ 0x21, 0x07, 0x19, 0x83,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_ABS word frag",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0x48),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_SKB_FRAG,
|
|
|
+ { },
|
|
|
+ { {0x40, 0x21071983} },
|
|
|
+ .frag_data = {
|
|
|
+ 0x42, 0x00, 0x00, 0x00,
|
|
|
+ 0x43, 0x44, 0x00, 0x00,
|
|
|
+ 0x21, 0x07, 0x19, 0x83,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_ABS halfword mixed head/frag",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LD | BPF_ABS | BPF_H, 0x3f),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_SKB_FRAG,
|
|
|
+ { [0x3e] = 0x25, [0x3f] = 0x05, },
|
|
|
+ { {0x40, 0x0519} },
|
|
|
+ .frag_data = { 0x19, 0x82 },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_ABS word mixed head/frag",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0x3e),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_SKB_FRAG,
|
|
|
+ { [0x3e] = 0x25, [0x3f] = 0x05, },
|
|
|
+ { {0x40, 0x25051982} },
|
|
|
+ .frag_data = { 0x19, 0x82 },
|
|
|
+ },
|
|
|
+ /*
|
|
|
+ * LD_IND / LD_ABS on non fragmented SKBs
|
|
|
+ */
|
|
|
+ {
|
|
|
+ /*
|
|
|
+ * this tests that the JIT/interpreter correctly resets X
|
|
|
+ * before using it in an LD_IND instruction.
|
|
|
+ */
|
|
|
+ "LD_IND byte default X",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LD | BPF_IND | BPF_B, 0x1),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ { [0x1] = 0x42 },
|
|
|
+ { {0x40, 0x42 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_IND byte positive offset",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LDX | BPF_IMM, 0x3e),
|
|
|
+ BPF_STMT(BPF_LD | BPF_IND | BPF_B, 0x1),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ { [0x3c] = 0x25, [0x3d] = 0x05, [0x3e] = 0x19, [0x3f] = 0x82 },
|
|
|
+ { {0x40, 0x82 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_IND byte negative offset",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LDX | BPF_IMM, 0x3e),
|
|
|
+ BPF_STMT(BPF_LD | BPF_IND | BPF_B, -0x1),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ { [0x3c] = 0x25, [0x3d] = 0x05, [0x3e] = 0x19, [0x3f] = 0x82 },
|
|
|
+ { {0x40, 0x05 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_IND halfword positive offset",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LDX | BPF_IMM, 0x20),
|
|
|
+ BPF_STMT(BPF_LD | BPF_IND | BPF_H, 0x2),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ {
|
|
|
+ [0x1c] = 0xaa, [0x1d] = 0x55,
|
|
|
+ [0x1e] = 0xbb, [0x1f] = 0x66,
|
|
|
+ [0x20] = 0xcc, [0x21] = 0x77,
|
|
|
+ [0x22] = 0xdd, [0x23] = 0x88,
|
|
|
+ },
|
|
|
+ { {0x40, 0xdd88 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_IND halfword negative offset",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LDX | BPF_IMM, 0x20),
|
|
|
+ BPF_STMT(BPF_LD | BPF_IND | BPF_H, -0x2),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ {
|
|
|
+ [0x1c] = 0xaa, [0x1d] = 0x55,
|
|
|
+ [0x1e] = 0xbb, [0x1f] = 0x66,
|
|
|
+ [0x20] = 0xcc, [0x21] = 0x77,
|
|
|
+ [0x22] = 0xdd, [0x23] = 0x88,
|
|
|
+ },
|
|
|
+ { {0x40, 0xbb66 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_IND halfword unaligned",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LDX | BPF_IMM, 0x20),
|
|
|
+ BPF_STMT(BPF_LD | BPF_IND | BPF_H, -0x1),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ {
|
|
|
+ [0x1c] = 0xaa, [0x1d] = 0x55,
|
|
|
+ [0x1e] = 0xbb, [0x1f] = 0x66,
|
|
|
+ [0x20] = 0xcc, [0x21] = 0x77,
|
|
|
+ [0x22] = 0xdd, [0x23] = 0x88,
|
|
|
+ },
|
|
|
+ { {0x40, 0x66cc } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_IND word positive offset",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LDX | BPF_IMM, 0x20),
|
|
|
+ BPF_STMT(BPF_LD | BPF_IND | BPF_W, 0x4),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ {
|
|
|
+ [0x1c] = 0xaa, [0x1d] = 0x55,
|
|
|
+ [0x1e] = 0xbb, [0x1f] = 0x66,
|
|
|
+ [0x20] = 0xcc, [0x21] = 0x77,
|
|
|
+ [0x22] = 0xdd, [0x23] = 0x88,
|
|
|
+ [0x24] = 0xee, [0x25] = 0x99,
|
|
|
+ [0x26] = 0xff, [0x27] = 0xaa,
|
|
|
+ },
|
|
|
+ { {0x40, 0xee99ffaa } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_IND word negative offset",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LDX | BPF_IMM, 0x20),
|
|
|
+ BPF_STMT(BPF_LD | BPF_IND | BPF_W, -0x4),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ {
|
|
|
+ [0x1c] = 0xaa, [0x1d] = 0x55,
|
|
|
+ [0x1e] = 0xbb, [0x1f] = 0x66,
|
|
|
+ [0x20] = 0xcc, [0x21] = 0x77,
|
|
|
+ [0x22] = 0xdd, [0x23] = 0x88,
|
|
|
+ [0x24] = 0xee, [0x25] = 0x99,
|
|
|
+ [0x26] = 0xff, [0x27] = 0xaa,
|
|
|
+ },
|
|
|
+ { {0x40, 0xaa55bb66 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_IND word unaligned (addr & 3 == 2)",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LDX | BPF_IMM, 0x20),
|
|
|
+ BPF_STMT(BPF_LD | BPF_IND | BPF_W, -0x2),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ {
|
|
|
+ [0x1c] = 0xaa, [0x1d] = 0x55,
|
|
|
+ [0x1e] = 0xbb, [0x1f] = 0x66,
|
|
|
+ [0x20] = 0xcc, [0x21] = 0x77,
|
|
|
+ [0x22] = 0xdd, [0x23] = 0x88,
|
|
|
+ [0x24] = 0xee, [0x25] = 0x99,
|
|
|
+ [0x26] = 0xff, [0x27] = 0xaa,
|
|
|
+ },
|
|
|
+ { {0x40, 0xbb66cc77 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_IND word unaligned (addr & 3 == 1)",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LDX | BPF_IMM, 0x20),
|
|
|
+ BPF_STMT(BPF_LD | BPF_IND | BPF_W, -0x3),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ {
|
|
|
+ [0x1c] = 0xaa, [0x1d] = 0x55,
|
|
|
+ [0x1e] = 0xbb, [0x1f] = 0x66,
|
|
|
+ [0x20] = 0xcc, [0x21] = 0x77,
|
|
|
+ [0x22] = 0xdd, [0x23] = 0x88,
|
|
|
+ [0x24] = 0xee, [0x25] = 0x99,
|
|
|
+ [0x26] = 0xff, [0x27] = 0xaa,
|
|
|
+ },
|
|
|
+ { {0x40, 0x55bb66cc } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_IND word unaligned (addr & 3 == 3)",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LDX | BPF_IMM, 0x20),
|
|
|
+ BPF_STMT(BPF_LD | BPF_IND | BPF_W, -0x1),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ {
|
|
|
+ [0x1c] = 0xaa, [0x1d] = 0x55,
|
|
|
+ [0x1e] = 0xbb, [0x1f] = 0x66,
|
|
|
+ [0x20] = 0xcc, [0x21] = 0x77,
|
|
|
+ [0x22] = 0xdd, [0x23] = 0x88,
|
|
|
+ [0x24] = 0xee, [0x25] = 0x99,
|
|
|
+ [0x26] = 0xff, [0x27] = 0xaa,
|
|
|
+ },
|
|
|
+ { {0x40, 0x66cc77dd } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_ABS byte",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LD | BPF_ABS | BPF_B, 0x20),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ {
|
|
|
+ [0x1c] = 0xaa, [0x1d] = 0x55,
|
|
|
+ [0x1e] = 0xbb, [0x1f] = 0x66,
|
|
|
+ [0x20] = 0xcc, [0x21] = 0x77,
|
|
|
+ [0x22] = 0xdd, [0x23] = 0x88,
|
|
|
+ [0x24] = 0xee, [0x25] = 0x99,
|
|
|
+ [0x26] = 0xff, [0x27] = 0xaa,
|
|
|
+ },
|
|
|
+ { {0x40, 0xcc } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_ABS halfword",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LD | BPF_ABS | BPF_H, 0x22),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ {
|
|
|
+ [0x1c] = 0xaa, [0x1d] = 0x55,
|
|
|
+ [0x1e] = 0xbb, [0x1f] = 0x66,
|
|
|
+ [0x20] = 0xcc, [0x21] = 0x77,
|
|
|
+ [0x22] = 0xdd, [0x23] = 0x88,
|
|
|
+ [0x24] = 0xee, [0x25] = 0x99,
|
|
|
+ [0x26] = 0xff, [0x27] = 0xaa,
|
|
|
+ },
|
|
|
+ { {0x40, 0xdd88 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_ABS halfword unaligned",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LD | BPF_ABS | BPF_H, 0x25),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ {
|
|
|
+ [0x1c] = 0xaa, [0x1d] = 0x55,
|
|
|
+ [0x1e] = 0xbb, [0x1f] = 0x66,
|
|
|
+ [0x20] = 0xcc, [0x21] = 0x77,
|
|
|
+ [0x22] = 0xdd, [0x23] = 0x88,
|
|
|
+ [0x24] = 0xee, [0x25] = 0x99,
|
|
|
+ [0x26] = 0xff, [0x27] = 0xaa,
|
|
|
+ },
|
|
|
+ { {0x40, 0x99ff } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_ABS word",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0x1c),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ {
|
|
|
+ [0x1c] = 0xaa, [0x1d] = 0x55,
|
|
|
+ [0x1e] = 0xbb, [0x1f] = 0x66,
|
|
|
+ [0x20] = 0xcc, [0x21] = 0x77,
|
|
|
+ [0x22] = 0xdd, [0x23] = 0x88,
|
|
|
+ [0x24] = 0xee, [0x25] = 0x99,
|
|
|
+ [0x26] = 0xff, [0x27] = 0xaa,
|
|
|
+ },
|
|
|
+ { {0x40, 0xaa55bb66 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_ABS word unaligned (addr & 3 == 2)",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0x22),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ {
|
|
|
+ [0x1c] = 0xaa, [0x1d] = 0x55,
|
|
|
+ [0x1e] = 0xbb, [0x1f] = 0x66,
|
|
|
+ [0x20] = 0xcc, [0x21] = 0x77,
|
|
|
+ [0x22] = 0xdd, [0x23] = 0x88,
|
|
|
+ [0x24] = 0xee, [0x25] = 0x99,
|
|
|
+ [0x26] = 0xff, [0x27] = 0xaa,
|
|
|
+ },
|
|
|
+ { {0x40, 0xdd88ee99 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_ABS word unaligned (addr & 3 == 1)",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0x21),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ {
|
|
|
+ [0x1c] = 0xaa, [0x1d] = 0x55,
|
|
|
+ [0x1e] = 0xbb, [0x1f] = 0x66,
|
|
|
+ [0x20] = 0xcc, [0x21] = 0x77,
|
|
|
+ [0x22] = 0xdd, [0x23] = 0x88,
|
|
|
+ [0x24] = 0xee, [0x25] = 0x99,
|
|
|
+ [0x26] = 0xff, [0x27] = 0xaa,
|
|
|
+ },
|
|
|
+ { {0x40, 0x77dd88ee } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "LD_ABS word unaligned (addr & 3 == 3)",
|
|
|
+ .u.insns = {
|
|
|
+ BPF_STMT(BPF_LD | BPF_ABS | BPF_W, 0x23),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC,
|
|
|
+ {
|
|
|
+ [0x1c] = 0xaa, [0x1d] = 0x55,
|
|
|
+ [0x1e] = 0xbb, [0x1f] = 0x66,
|
|
|
+ [0x20] = 0xcc, [0x21] = 0x77,
|
|
|
+ [0x22] = 0xdd, [0x23] = 0x88,
|
|
|
+ [0x24] = 0xee, [0x25] = 0x99,
|
|
|
+ [0x26] = 0xff, [0x27] = 0xaa,
|
|
|
+ },
|
|
|
+ { {0x40, 0x88ee99ff } },
|
|
|
+ },
|
|
|
+ /*
|
|
|
+ * verify that the interpreter or JIT correctly sets A and X
|
|
|
+ * to 0.
|
|
|
+ */
|
|
|
+ {
|
|
|
+ "ADD default X",
|
|
|
+ .u.insns = {
|
|
|
+ /*
|
|
|
+ * A = 0x42
|
|
|
+ * A = A + X
|
|
|
+ * ret A
|
|
|
+ */
|
|
|
+ BPF_STMT(BPF_LD | BPF_IMM, 0x42),
|
|
|
+ BPF_STMT(BPF_ALU | BPF_ADD | BPF_X, 0),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_NO_DATA,
|
|
|
+ {},
|
|
|
+ { {0x1, 0x42 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "ADD default A",
|
|
|
+ .u.insns = {
|
|
|
+ /*
|
|
|
+ * A = A + 0x42
|
|
|
+ * ret A
|
|
|
+ */
|
|
|
+ BPF_STMT(BPF_ALU | BPF_ADD | BPF_K, 0x42),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_NO_DATA,
|
|
|
+ {},
|
|
|
+ { {0x1, 0x42 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "SUB default X",
|
|
|
+ .u.insns = {
|
|
|
+ /*
|
|
|
+ * A = 0x66
|
|
|
+ * A = A - X
|
|
|
+ * ret A
|
|
|
+ */
|
|
|
+ BPF_STMT(BPF_LD | BPF_IMM, 0x66),
|
|
|
+ BPF_STMT(BPF_ALU | BPF_SUB | BPF_X, 0),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_NO_DATA,
|
|
|
+ {},
|
|
|
+ { {0x1, 0x66 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "SUB default A",
|
|
|
+ .u.insns = {
|
|
|
+ /*
|
|
|
+ * A = A - -0x66
|
|
|
+ * ret A
|
|
|
+ */
|
|
|
+ BPF_STMT(BPF_ALU | BPF_SUB | BPF_K, -0x66),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_NO_DATA,
|
|
|
+ {},
|
|
|
+ { {0x1, 0x66 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "MUL default X",
|
|
|
+ .u.insns = {
|
|
|
+ /*
|
|
|
+ * A = 0x42
|
|
|
+ * A = A * X
|
|
|
+ * ret A
|
|
|
+ */
|
|
|
+ BPF_STMT(BPF_LD | BPF_IMM, 0x42),
|
|
|
+ BPF_STMT(BPF_ALU | BPF_MUL | BPF_X, 0),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_NO_DATA,
|
|
|
+ {},
|
|
|
+ { {0x1, 0x0 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "MUL default A",
|
|
|
+ .u.insns = {
|
|
|
+ /*
|
|
|
+ * A = A * 0x66
|
|
|
+ * ret A
|
|
|
+ */
|
|
|
+ BPF_STMT(BPF_ALU | BPF_MUL | BPF_K, 0x66),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_NO_DATA,
|
|
|
+ {},
|
|
|
+ { {0x1, 0x0 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "DIV default X",
|
|
|
+ .u.insns = {
|
|
|
+ /*
|
|
|
+ * A = 0x42
|
|
|
+ * A = A / X ; this halt the filter execution if X is 0
|
|
|
+ * ret 0x42
|
|
|
+ */
|
|
|
+ BPF_STMT(BPF_LD | BPF_IMM, 0x42),
|
|
|
+ BPF_STMT(BPF_ALU | BPF_DIV | BPF_X, 0),
|
|
|
+ BPF_STMT(BPF_RET | BPF_K, 0x42),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_NO_DATA,
|
|
|
+ {},
|
|
|
+ { {0x1, 0x0 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "DIV default A",
|
|
|
+ .u.insns = {
|
|
|
+ /*
|
|
|
+ * A = A / 1
|
|
|
+ * ret A
|
|
|
+ */
|
|
|
+ BPF_STMT(BPF_ALU | BPF_DIV | BPF_K, 0x1),
|
|
|
+ BPF_STMT(BPF_RET | BPF_A, 0x0),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_NO_DATA,
|
|
|
+ {},
|
|
|
+ { {0x1, 0x0 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "JMP EQ default A",
|
|
|
+ .u.insns = {
|
|
|
+ /*
|
|
|
+ * cmp A, 0x0, 0, 1
|
|
|
+ * ret 0x42
|
|
|
+ * ret 0x66
|
|
|
+ */
|
|
|
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0, 0, 1),
|
|
|
+ BPF_STMT(BPF_RET | BPF_K, 0x42),
|
|
|
+ BPF_STMT(BPF_RET | BPF_K, 0x66),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_NO_DATA,
|
|
|
+ {},
|
|
|
+ { {0x1, 0x42 } },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "JMP EQ default X",
|
|
|
+ .u.insns = {
|
|
|
+ /*
|
|
|
+ * A = 0x0
|
|
|
+ * cmp A, X, 0, 1
|
|
|
+ * ret 0x42
|
|
|
+ * ret 0x66
|
|
|
+ */
|
|
|
+ BPF_STMT(BPF_LD | BPF_IMM, 0x0),
|
|
|
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0x0, 0, 1),
|
|
|
+ BPF_STMT(BPF_RET | BPF_K, 0x42),
|
|
|
+ BPF_STMT(BPF_RET | BPF_K, 0x66),
|
|
|
+ },
|
|
|
+ CLASSIC | FLAG_NO_DATA,
|
|
|
+ {},
|
|
|
+ { {0x1, 0x42 } },
|
|
|
+ },
|
|
|
};
|
|
|
|
|
|
static struct net_device dev;
|
|
@@ -4525,6 +5124,9 @@ static struct sk_buff *populate_skb(char *buf, int size)
|
|
|
|
|
|
static void *generate_test_data(struct bpf_test *test, int sub)
|
|
|
{
|
|
|
+ struct sk_buff *skb;
|
|
|
+ struct page *page;
|
|
|
+
|
|
|
if (test->aux & FLAG_NO_DATA)
|
|
|
return NULL;
|
|
|
|
|
@@ -4532,7 +5134,38 @@ static void *generate_test_data(struct bpf_test *test, int sub)
|
|
|
* subtests generate skbs of different sizes based on
|
|
|
* the same data.
|
|
|
*/
|
|
|
- return populate_skb(test->data, test->test[sub].data_size);
|
|
|
+ skb = populate_skb(test->data, test->test[sub].data_size);
|
|
|
+ if (!skb)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ if (test->aux & FLAG_SKB_FRAG) {
|
|
|
+ /*
|
|
|
+ * when the test requires a fragmented skb, add a
|
|
|
+ * single fragment to the skb, filled with
|
|
|
+ * test->frag_data.
|
|
|
+ */
|
|
|
+ void *ptr;
|
|
|
+
|
|
|
+ page = alloc_page(GFP_KERNEL);
|
|
|
+
|
|
|
+ if (!page)
|
|
|
+ goto err_kfree_skb;
|
|
|
+
|
|
|
+ ptr = kmap(page);
|
|
|
+ if (!ptr)
|
|
|
+ goto err_free_page;
|
|
|
+ memcpy(ptr, test->frag_data, MAX_DATA);
|
|
|
+ kunmap(page);
|
|
|
+ skb_add_rx_frag(skb, 0, page, 0, MAX_DATA, MAX_DATA);
|
|
|
+ }
|
|
|
+
|
|
|
+ return skb;
|
|
|
+
|
|
|
+err_free_page:
|
|
|
+ __free_page(page);
|
|
|
+err_kfree_skb:
|
|
|
+ kfree_skb(skb);
|
|
|
+ return NULL;
|
|
|
}
|
|
|
|
|
|
static void release_test_data(const struct bpf_test *test, void *data)
|
|
@@ -4672,6 +5305,11 @@ static int run_one(const struct bpf_prog *fp, struct bpf_test *test)
|
|
|
break;
|
|
|
|
|
|
data = generate_test_data(test, i);
|
|
|
+ if (!data && !(test->aux & FLAG_NO_DATA)) {
|
|
|
+ pr_cont("data generation failed ");
|
|
|
+ err_cnt++;
|
|
|
+ break;
|
|
|
+ }
|
|
|
ret = __run_one(fp, data, runs, &duration);
|
|
|
release_test_data(test, data);
|
|
|
|
|
@@ -4687,10 +5325,73 @@ static int run_one(const struct bpf_prog *fp, struct bpf_test *test)
|
|
|
return err_cnt;
|
|
|
}
|
|
|
|
|
|
+static char test_name[64];
|
|
|
+module_param_string(test_name, test_name, sizeof(test_name), 0);
|
|
|
+
|
|
|
+static int test_id = -1;
|
|
|
+module_param(test_id, int, 0);
|
|
|
+
|
|
|
+static int test_range[2] = { 0, ARRAY_SIZE(tests) - 1 };
|
|
|
+module_param_array(test_range, int, NULL, 0);
|
|
|
+
|
|
|
+static __init int find_test_index(const char *test_name)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
|
|
+ if (!strcmp(tests[i].descr, test_name))
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
static __init int prepare_bpf_tests(void)
|
|
|
{
|
|
|
int i;
|
|
|
|
|
|
+ if (test_id >= 0) {
|
|
|
+ /*
|
|
|
+ * if a test_id was specified, use test_range to
|
|
|
+ * cover only that test.
|
|
|
+ */
|
|
|
+ if (test_id >= ARRAY_SIZE(tests)) {
|
|
|
+ pr_err("test_bpf: invalid test_id specified.\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ test_range[0] = test_id;
|
|
|
+ test_range[1] = test_id;
|
|
|
+ } else if (*test_name) {
|
|
|
+ /*
|
|
|
+ * if a test_name was specified, find it and setup
|
|
|
+ * test_range to cover only that test.
|
|
|
+ */
|
|
|
+ int idx = find_test_index(test_name);
|
|
|
+
|
|
|
+ if (idx < 0) {
|
|
|
+ pr_err("test_bpf: no test named '%s' found.\n",
|
|
|
+ test_name);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ test_range[0] = idx;
|
|
|
+ test_range[1] = idx;
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * check that the supplied test_range is valid.
|
|
|
+ */
|
|
|
+ if (test_range[0] >= ARRAY_SIZE(tests) ||
|
|
|
+ test_range[1] >= ARRAY_SIZE(tests) ||
|
|
|
+ test_range[0] < 0 || test_range[1] < 0) {
|
|
|
+ pr_err("test_bpf: test_range is out of bound.\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (test_range[1] < test_range[0]) {
|
|
|
+ pr_err("test_bpf: test_range is ending before it starts.\n");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
|
|
if (tests[i].fill_helper &&
|
|
|
tests[i].fill_helper(&tests[i]) < 0)
|
|
@@ -4710,6 +5411,11 @@ static __init void destroy_bpf_tests(void)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static bool exclude_test(int test_id)
|
|
|
+{
|
|
|
+ return test_id < test_range[0] || test_id > test_range[1];
|
|
|
+}
|
|
|
+
|
|
|
static __init int test_bpf(void)
|
|
|
{
|
|
|
int i, err_cnt = 0, pass_cnt = 0;
|
|
@@ -4719,6 +5425,9 @@ static __init int test_bpf(void)
|
|
|
struct bpf_prog *fp;
|
|
|
int err;
|
|
|
|
|
|
+ if (exclude_test(i))
|
|
|
+ continue;
|
|
|
+
|
|
|
pr_info("#%d %s ", i, tests[i].descr);
|
|
|
|
|
|
fp = generate_filter(i, &err);
|