cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

align.c (22718B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <test_progs.h>
      3
      4#define MAX_INSNS	512
      5#define MAX_MATCHES	16
      6
      7struct bpf_reg_match {
      8	unsigned int line;
      9	const char *match;
     10};
     11
     12struct bpf_align_test {
     13	const char *descr;
     14	struct bpf_insn	insns[MAX_INSNS];
     15	enum {
     16		UNDEF,
     17		ACCEPT,
     18		REJECT
     19	} result;
     20	enum bpf_prog_type prog_type;
     21	/* Matches must be in order of increasing line */
     22	struct bpf_reg_match matches[MAX_MATCHES];
     23};
     24
     25static struct bpf_align_test tests[] = {
     26	/* Four tests of known constants.  These aren't staggeringly
     27	 * interesting since we track exact values now.
     28	 */
     29	{
     30		.descr = "mov",
     31		.insns = {
     32			BPF_MOV64_IMM(BPF_REG_3, 2),
     33			BPF_MOV64_IMM(BPF_REG_3, 4),
     34			BPF_MOV64_IMM(BPF_REG_3, 8),
     35			BPF_MOV64_IMM(BPF_REG_3, 16),
     36			BPF_MOV64_IMM(BPF_REG_3, 32),
     37			BPF_MOV64_IMM(BPF_REG_0, 0),
     38			BPF_EXIT_INSN(),
     39		},
     40		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
     41		.matches = {
     42			{0, "R1=ctx(off=0,imm=0)"},
     43			{0, "R10=fp0"},
     44			{0, "R3_w=2"},
     45			{1, "R3_w=4"},
     46			{2, "R3_w=8"},
     47			{3, "R3_w=16"},
     48			{4, "R3_w=32"},
     49		},
     50	},
     51	{
     52		.descr = "shift",
     53		.insns = {
     54			BPF_MOV64_IMM(BPF_REG_3, 1),
     55			BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
     56			BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
     57			BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
     58			BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
     59			BPF_ALU64_IMM(BPF_RSH, BPF_REG_3, 4),
     60			BPF_MOV64_IMM(BPF_REG_4, 32),
     61			BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
     62			BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
     63			BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
     64			BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
     65			BPF_MOV64_IMM(BPF_REG_0, 0),
     66			BPF_EXIT_INSN(),
     67		},
     68		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
     69		.matches = {
     70			{0, "R1=ctx(off=0,imm=0)"},
     71			{0, "R10=fp0"},
     72			{0, "R3_w=1"},
     73			{1, "R3_w=2"},
     74			{2, "R3_w=4"},
     75			{3, "R3_w=8"},
     76			{4, "R3_w=16"},
     77			{5, "R3_w=1"},
     78			{6, "R4_w=32"},
     79			{7, "R4_w=16"},
     80			{8, "R4_w=8"},
     81			{9, "R4_w=4"},
     82			{10, "R4_w=2"},
     83		},
     84	},
     85	{
     86		.descr = "addsub",
     87		.insns = {
     88			BPF_MOV64_IMM(BPF_REG_3, 4),
     89			BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 4),
     90			BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 2),
     91			BPF_MOV64_IMM(BPF_REG_4, 8),
     92			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
     93			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 2),
     94			BPF_MOV64_IMM(BPF_REG_0, 0),
     95			BPF_EXIT_INSN(),
     96		},
     97		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
     98		.matches = {
     99			{0, "R1=ctx(off=0,imm=0)"},
    100			{0, "R10=fp0"},
    101			{0, "R3_w=4"},
    102			{1, "R3_w=8"},
    103			{2, "R3_w=10"},
    104			{3, "R4_w=8"},
    105			{4, "R4_w=12"},
    106			{5, "R4_w=14"},
    107		},
    108	},
    109	{
    110		.descr = "mul",
    111		.insns = {
    112			BPF_MOV64_IMM(BPF_REG_3, 7),
    113			BPF_ALU64_IMM(BPF_MUL, BPF_REG_3, 1),
    114			BPF_ALU64_IMM(BPF_MUL, BPF_REG_3, 2),
    115			BPF_ALU64_IMM(BPF_MUL, BPF_REG_3, 4),
    116			BPF_MOV64_IMM(BPF_REG_0, 0),
    117			BPF_EXIT_INSN(),
    118		},
    119		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
    120		.matches = {
    121			{0, "R1=ctx(off=0,imm=0)"},
    122			{0, "R10=fp0"},
    123			{0, "R3_w=7"},
    124			{1, "R3_w=7"},
    125			{2, "R3_w=14"},
    126			{3, "R3_w=56"},
    127		},
    128	},
    129
    130	/* Tests using unknown values */
    131#define PREP_PKT_POINTERS \
    132	BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, \
    133		    offsetof(struct __sk_buff, data)), \
    134	BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, \
    135		    offsetof(struct __sk_buff, data_end))
    136
    137#define LOAD_UNKNOWN(DST_REG) \
    138	PREP_PKT_POINTERS, \
    139	BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), \
    140	BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), \
    141	BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 1), \
    142	BPF_EXIT_INSN(), \
    143	BPF_LDX_MEM(BPF_B, DST_REG, BPF_REG_2, 0)
    144
    145	{
    146		.descr = "unknown shift",
    147		.insns = {
    148			LOAD_UNKNOWN(BPF_REG_3),
    149			BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
    150			BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
    151			BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
    152			BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
    153			LOAD_UNKNOWN(BPF_REG_4),
    154			BPF_ALU64_IMM(BPF_LSH, BPF_REG_4, 5),
    155			BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
    156			BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
    157			BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
    158			BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
    159			BPF_MOV64_IMM(BPF_REG_0, 0),
    160			BPF_EXIT_INSN(),
    161		},
    162		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
    163		.matches = {
    164			{6, "R0_w=pkt(off=8,r=8,imm=0)"},
    165			{6, "R3_w=scalar(umax=255,var_off=(0x0; 0xff))"},
    166			{7, "R3_w=scalar(umax=510,var_off=(0x0; 0x1fe))"},
    167			{8, "R3_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
    168			{9, "R3_w=scalar(umax=2040,var_off=(0x0; 0x7f8))"},
    169			{10, "R3_w=scalar(umax=4080,var_off=(0x0; 0xff0))"},
    170			{12, "R3_w=pkt_end(off=0,imm=0)"},
    171			{17, "R4_w=scalar(umax=255,var_off=(0x0; 0xff))"},
    172			{18, "R4_w=scalar(umax=8160,var_off=(0x0; 0x1fe0))"},
    173			{19, "R4_w=scalar(umax=4080,var_off=(0x0; 0xff0))"},
    174			{20, "R4_w=scalar(umax=2040,var_off=(0x0; 0x7f8))"},
    175			{21, "R4_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
    176			{22, "R4_w=scalar(umax=510,var_off=(0x0; 0x1fe))"},
    177		},
    178	},
    179	{
    180		.descr = "unknown mul",
    181		.insns = {
    182			LOAD_UNKNOWN(BPF_REG_3),
    183			BPF_MOV64_REG(BPF_REG_4, BPF_REG_3),
    184			BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 1),
    185			BPF_MOV64_REG(BPF_REG_4, BPF_REG_3),
    186			BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 2),
    187			BPF_MOV64_REG(BPF_REG_4, BPF_REG_3),
    188			BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 4),
    189			BPF_MOV64_REG(BPF_REG_4, BPF_REG_3),
    190			BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 8),
    191			BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 2),
    192			BPF_MOV64_IMM(BPF_REG_0, 0),
    193			BPF_EXIT_INSN(),
    194		},
    195		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
    196		.matches = {
    197			{6, "R3_w=scalar(umax=255,var_off=(0x0; 0xff))"},
    198			{7, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"},
    199			{8, "R4_w=scalar(umax=255,var_off=(0x0; 0xff))"},
    200			{9, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"},
    201			{10, "R4_w=scalar(umax=510,var_off=(0x0; 0x1fe))"},
    202			{11, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"},
    203			{12, "R4_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
    204			{13, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"},
    205			{14, "R4_w=scalar(umax=2040,var_off=(0x0; 0x7f8))"},
    206			{15, "R4_w=scalar(umax=4080,var_off=(0x0; 0xff0))"},
    207		},
    208	},
    209	{
    210		.descr = "packet const offset",
    211		.insns = {
    212			PREP_PKT_POINTERS,
    213			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
    214
    215			BPF_MOV64_IMM(BPF_REG_0, 0),
    216
    217			/* Skip over ethernet header.  */
    218			BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
    219			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
    220			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
    221			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
    222			BPF_EXIT_INSN(),
    223
    224			BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_5, 0),
    225			BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_5, 1),
    226			BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_5, 2),
    227			BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_5, 3),
    228			BPF_LDX_MEM(BPF_H, BPF_REG_4, BPF_REG_5, 0),
    229			BPF_LDX_MEM(BPF_H, BPF_REG_4, BPF_REG_5, 2),
    230			BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
    231
    232			BPF_MOV64_IMM(BPF_REG_0, 0),
    233			BPF_EXIT_INSN(),
    234		},
    235		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
    236		.matches = {
    237			{2, "R5_w=pkt(off=0,r=0,imm=0)"},
    238			{4, "R5_w=pkt(off=14,r=0,imm=0)"},
    239			{5, "R4_w=pkt(off=14,r=0,imm=0)"},
    240			{9, "R2=pkt(off=0,r=18,imm=0)"},
    241			{10, "R5=pkt(off=14,r=18,imm=0)"},
    242			{10, "R4_w=scalar(umax=255,var_off=(0x0; 0xff))"},
    243			{13, "R4_w=scalar(umax=65535,var_off=(0x0; 0xffff))"},
    244			{14, "R4_w=scalar(umax=65535,var_off=(0x0; 0xffff))"},
    245		},
    246	},
    247	{
    248		.descr = "packet variable offset",
    249		.insns = {
    250			LOAD_UNKNOWN(BPF_REG_6),
    251			BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
    252
    253			/* First, add a constant to the R5 packet pointer,
    254			 * then a variable with a known alignment.
    255			 */
    256			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
    257			BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
    258			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
    259			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
    260			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
    261			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
    262			BPF_EXIT_INSN(),
    263			BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
    264
    265			/* Now, test in the other direction.  Adding first
    266			 * the variable offset to R5, then the constant.
    267			 */
    268			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
    269			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
    270			BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
    271			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
    272			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
    273			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
    274			BPF_EXIT_INSN(),
    275			BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
    276
    277			/* Test multiple accumulations of unknown values
    278			 * into a packet pointer.
    279			 */
    280			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
    281			BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
    282			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
    283			BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 4),
    284			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
    285			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
    286			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
    287			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
    288			BPF_EXIT_INSN(),
    289			BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
    290
    291			BPF_MOV64_IMM(BPF_REG_0, 0),
    292			BPF_EXIT_INSN(),
    293		},
    294		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
    295		.matches = {
    296			/* Calculated offset in R6 has unknown value, but known
    297			 * alignment of 4.
    298			 */
    299			{6, "R2_w=pkt(off=0,r=8,imm=0)"},
    300			{7, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
    301			/* Offset is added to packet pointer R5, resulting in
    302			 * known fixed offset, and variable offset from R6.
    303			 */
    304			{11, "R5_w=pkt(id=1,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
    305			/* At the time the word size load is performed from R5,
    306			 * it's total offset is NET_IP_ALIGN + reg->off (0) +
    307			 * reg->aux_off (14) which is 16.  Then the variable
    308			 * offset is considered using reg->aux_off_align which
    309			 * is 4 and meets the load's requirements.
    310			 */
    311			{15, "R4=pkt(id=1,off=18,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
    312			{15, "R5=pkt(id=1,off=14,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
    313			/* Variable offset is added to R5 packet pointer,
    314			 * resulting in auxiliary alignment of 4.
    315			 */
    316			{17, "R5_w=pkt(id=2,off=0,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
    317			/* Constant offset is added to R5, resulting in
    318			 * reg->off of 14.
    319			 */
    320			{18, "R5_w=pkt(id=2,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
    321			/* At the time the word size load is performed from R5,
    322			 * its total fixed offset is NET_IP_ALIGN + reg->off
    323			 * (14) which is 16.  Then the variable offset is 4-byte
    324			 * aligned, so the total offset is 4-byte aligned and
    325			 * meets the load's requirements.
    326			 */
    327			{23, "R4=pkt(id=2,off=18,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
    328			{23, "R5=pkt(id=2,off=14,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
    329			/* Constant offset is added to R5 packet pointer,
    330			 * resulting in reg->off value of 14.
    331			 */
    332			{25, "R5_w=pkt(off=14,r=8"},
    333			/* Variable offset is added to R5, resulting in a
    334			 * variable offset of (4n).
    335			 */
    336			{26, "R5_w=pkt(id=3,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
    337			/* Constant is added to R5 again, setting reg->off to 18. */
    338			{27, "R5_w=pkt(id=3,off=18,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
    339			/* And once more we add a variable; resulting var_off
    340			 * is still (4n), fixed offset is not changed.
    341			 * Also, we create a new reg->id.
    342			 */
    343			{28, "R5_w=pkt(id=4,off=18,r=0,umax=2040,var_off=(0x0; 0x7fc)"},
    344			/* At the time the word size load is performed from R5,
    345			 * its total fixed offset is NET_IP_ALIGN + reg->off (18)
    346			 * which is 20.  Then the variable offset is (4n), so
    347			 * the total offset is 4-byte aligned and meets the
    348			 * load's requirements.
    349			 */
    350			{33, "R4=pkt(id=4,off=22,r=22,umax=2040,var_off=(0x0; 0x7fc)"},
    351			{33, "R5=pkt(id=4,off=18,r=22,umax=2040,var_off=(0x0; 0x7fc)"},
    352		},
    353	},
    354	{
    355		.descr = "packet variable offset 2",
    356		.insns = {
    357			/* Create an unknown offset, (4n+2)-aligned */
    358			LOAD_UNKNOWN(BPF_REG_6),
    359			BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
    360			BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14),
    361			/* Add it to the packet pointer */
    362			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
    363			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
    364			/* Check bounds and perform a read */
    365			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
    366			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
    367			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
    368			BPF_EXIT_INSN(),
    369			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
    370			/* Make a (4n) offset from the value we just read */
    371			BPF_ALU64_IMM(BPF_AND, BPF_REG_6, 0xff),
    372			BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
    373			/* Add it to the packet pointer */
    374			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
    375			/* Check bounds and perform a read */
    376			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
    377			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
    378			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
    379			BPF_EXIT_INSN(),
    380			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
    381			BPF_MOV64_IMM(BPF_REG_0, 0),
    382			BPF_EXIT_INSN(),
    383		},
    384		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
    385		.matches = {
    386			/* Calculated offset in R6 has unknown value, but known
    387			 * alignment of 4.
    388			 */
    389			{6, "R2_w=pkt(off=0,r=8,imm=0)"},
    390			{7, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
    391			/* Adding 14 makes R6 be (4n+2) */
    392			{8, "R6_w=scalar(umin=14,umax=1034,var_off=(0x2; 0x7fc))"},
    393			/* Packet pointer has (4n+2) offset */
    394			{11, "R5_w=pkt(id=1,off=0,r=0,umin=14,umax=1034,var_off=(0x2; 0x7fc)"},
    395			{12, "R4=pkt(id=1,off=4,r=0,umin=14,umax=1034,var_off=(0x2; 0x7fc)"},
    396			/* At the time the word size load is performed from R5,
    397			 * its total fixed offset is NET_IP_ALIGN + reg->off (0)
    398			 * which is 2.  Then the variable offset is (4n+2), so
    399			 * the total offset is 4-byte aligned and meets the
    400			 * load's requirements.
    401			 */
    402			{15, "R5=pkt(id=1,off=0,r=4,umin=14,umax=1034,var_off=(0x2; 0x7fc)"},
    403			/* Newly read value in R6 was shifted left by 2, so has
    404			 * known alignment of 4.
    405			 */
    406			{17, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
    407			/* Added (4n) to packet pointer's (4n+2) var_off, giving
    408			 * another (4n+2).
    409			 */
    410			{19, "R5_w=pkt(id=2,off=0,r=0,umin=14,umax=2054,var_off=(0x2; 0xffc)"},
    411			{20, "R4=pkt(id=2,off=4,r=0,umin=14,umax=2054,var_off=(0x2; 0xffc)"},
    412			/* At the time the word size load is performed from R5,
    413			 * its total fixed offset is NET_IP_ALIGN + reg->off (0)
    414			 * which is 2.  Then the variable offset is (4n+2), so
    415			 * the total offset is 4-byte aligned and meets the
    416			 * load's requirements.
    417			 */
    418			{23, "R5=pkt(id=2,off=0,r=4,umin=14,umax=2054,var_off=(0x2; 0xffc)"},
    419		},
    420	},
    421	{
    422		.descr = "dubious pointer arithmetic",
    423		.insns = {
    424			PREP_PKT_POINTERS,
    425			BPF_MOV64_IMM(BPF_REG_0, 0),
    426			/* (ptr - ptr) << 2 */
    427			BPF_MOV64_REG(BPF_REG_5, BPF_REG_3),
    428			BPF_ALU64_REG(BPF_SUB, BPF_REG_5, BPF_REG_2),
    429			BPF_ALU64_IMM(BPF_LSH, BPF_REG_5, 2),
    430			/* We have a (4n) value.  Let's make a packet offset
    431			 * out of it.  First add 14, to make it a (4n+2)
    432			 */
    433			BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
    434			/* Then make sure it's nonnegative */
    435			BPF_JMP_IMM(BPF_JSGE, BPF_REG_5, 0, 1),
    436			BPF_EXIT_INSN(),
    437			/* Add it to packet pointer */
    438			BPF_MOV64_REG(BPF_REG_6, BPF_REG_2),
    439			BPF_ALU64_REG(BPF_ADD, BPF_REG_6, BPF_REG_5),
    440			/* Check bounds and perform a read */
    441			BPF_MOV64_REG(BPF_REG_4, BPF_REG_6),
    442			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
    443			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
    444			BPF_EXIT_INSN(),
    445			BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_6, 0),
    446			BPF_EXIT_INSN(),
    447		},
    448		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
    449		.result = REJECT,
    450		.matches = {
    451			{3, "R5_w=pkt_end(off=0,imm=0)"},
    452			/* (ptr - ptr) << 2 == unknown, (4n) */
    453			{5, "R5_w=scalar(smax=9223372036854775804,umax=18446744073709551612,var_off=(0x0; 0xfffffffffffffffc)"},
    454			/* (4n) + 14 == (4n+2).  We blow our bounds, because
    455			 * the add could overflow.
    456			 */
    457			{6, "R5_w=scalar(smin=-9223372036854775806,smax=9223372036854775806,umin=2,umax=18446744073709551614,var_off=(0x2; 0xfffffffffffffffc)"},
    458			/* Checked s>=0 */
    459			{9, "R5=scalar(umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
    460			/* packet pointer + nonnegative (4n+2) */
    461			{11, "R6_w=pkt(id=1,off=0,r=0,umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
    462			{12, "R4_w=pkt(id=1,off=4,r=0,umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
    463			/* NET_IP_ALIGN + (4n+2) == (4n), alignment is fine.
    464			 * We checked the bounds, but it might have been able
    465			 * to overflow if the packet pointer started in the
    466			 * upper half of the address space.
    467			 * So we did not get a 'range' on R6, and the access
    468			 * attempt will fail.
    469			 */
    470			{15, "R6_w=pkt(id=1,off=0,r=0,umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
    471		}
    472	},
    473	{
    474		.descr = "variable subtraction",
    475		.insns = {
    476			/* Create an unknown offset, (4n+2)-aligned */
    477			LOAD_UNKNOWN(BPF_REG_6),
    478			BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
    479			BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
    480			BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14),
    481			/* Create another unknown, (4n)-aligned, and subtract
    482			 * it from the first one
    483			 */
    484			BPF_ALU64_IMM(BPF_LSH, BPF_REG_7, 2),
    485			BPF_ALU64_REG(BPF_SUB, BPF_REG_6, BPF_REG_7),
    486			/* Bounds-check the result */
    487			BPF_JMP_IMM(BPF_JSGE, BPF_REG_6, 0, 1),
    488			BPF_EXIT_INSN(),
    489			/* Add it to the packet pointer */
    490			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
    491			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
    492			/* Check bounds and perform a read */
    493			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
    494			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
    495			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
    496			BPF_EXIT_INSN(),
    497			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
    498			BPF_EXIT_INSN(),
    499		},
    500		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
    501		.matches = {
    502			/* Calculated offset in R6 has unknown value, but known
    503			 * alignment of 4.
    504			 */
    505			{6, "R2_w=pkt(off=0,r=8,imm=0)"},
    506			{8, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
    507			/* Adding 14 makes R6 be (4n+2) */
    508			{9, "R6_w=scalar(umin=14,umax=1034,var_off=(0x2; 0x7fc))"},
    509			/* New unknown value in R7 is (4n) */
    510			{10, "R7_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
    511			/* Subtracting it from R6 blows our unsigned bounds */
    512			{11, "R6=scalar(smin=-1006,smax=1034,umin=2,umax=18446744073709551614,var_off=(0x2; 0xfffffffffffffffc)"},
    513			/* Checked s>= 0 */
    514			{14, "R6=scalar(umin=2,umax=1034,var_off=(0x2; 0x7fc))"},
    515			/* At the time the word size load is performed from R5,
    516			 * its total fixed offset is NET_IP_ALIGN + reg->off (0)
    517			 * which is 2.  Then the variable offset is (4n+2), so
    518			 * the total offset is 4-byte aligned and meets the
    519			 * load's requirements.
    520			 */
    521			{20, "R5=pkt(id=2,off=0,r=4,umin=2,umax=1034,var_off=(0x2; 0x7fc)"},
    522
    523		},
    524	},
    525	{
    526		.descr = "pointer variable subtraction",
    527		.insns = {
    528			/* Create an unknown offset, (4n+2)-aligned and bounded
    529			 * to [14,74]
    530			 */
    531			LOAD_UNKNOWN(BPF_REG_6),
    532			BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
    533			BPF_ALU64_IMM(BPF_AND, BPF_REG_6, 0xf),
    534			BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
    535			BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14),
    536			/* Subtract it from the packet pointer */
    537			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
    538			BPF_ALU64_REG(BPF_SUB, BPF_REG_5, BPF_REG_6),
    539			/* Create another unknown, (4n)-aligned and >= 74.
    540			 * That in fact means >= 76, since 74 % 4 == 2
    541			 */
    542			BPF_ALU64_IMM(BPF_LSH, BPF_REG_7, 2),
    543			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 76),
    544			/* Add it to the packet pointer */
    545			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_7),
    546			/* Check bounds and perform a read */
    547			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
    548			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
    549			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
    550			BPF_EXIT_INSN(),
    551			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
    552			BPF_EXIT_INSN(),
    553		},
    554		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
    555		.matches = {
    556			/* Calculated offset in R6 has unknown value, but known
    557			 * alignment of 4.
    558			 */
    559			{6, "R2_w=pkt(off=0,r=8,imm=0)"},
    560			{9, "R6_w=scalar(umax=60,var_off=(0x0; 0x3c))"},
    561			/* Adding 14 makes R6 be (4n+2) */
    562			{10, "R6_w=scalar(umin=14,umax=74,var_off=(0x2; 0x7c))"},
    563			/* Subtracting from packet pointer overflows ubounds */
    564			{13, "R5_w=pkt(id=2,off=0,r=8,umin=18446744073709551542,umax=18446744073709551602,var_off=(0xffffffffffffff82; 0x7c)"},
    565			/* New unknown value in R7 is (4n), >= 76 */
    566			{14, "R7_w=scalar(umin=76,umax=1096,var_off=(0x0; 0x7fc))"},
    567			/* Adding it to packet pointer gives nice bounds again */
    568			{16, "R5_w=pkt(id=3,off=0,r=0,umin=2,umax=1082,var_off=(0x2; 0xfffffffc)"},
    569			/* At the time the word size load is performed from R5,
    570			 * its total fixed offset is NET_IP_ALIGN + reg->off (0)
    571			 * which is 2.  Then the variable offset is (4n+2), so
    572			 * the total offset is 4-byte aligned and meets the
    573			 * load's requirements.
    574			 */
    575			{20, "R5=pkt(id=3,off=0,r=4,umin=2,umax=1082,var_off=(0x2; 0xfffffffc)"},
    576		},
    577	},
    578};
    579
    580static int probe_filter_length(const struct bpf_insn *fp)
    581{
    582	int len;
    583
    584	for (len = MAX_INSNS - 1; len > 0; --len)
    585		if (fp[len].code != 0 || fp[len].imm != 0)
    586			break;
    587	return len + 1;
    588}
    589
    590static char bpf_vlog[32768];
    591
    592static int do_test_single(struct bpf_align_test *test)
    593{
    594	struct bpf_insn *prog = test->insns;
    595	int prog_type = test->prog_type;
    596	char bpf_vlog_copy[32768];
    597	LIBBPF_OPTS(bpf_prog_load_opts, opts,
    598		.prog_flags = BPF_F_STRICT_ALIGNMENT,
    599		.log_buf = bpf_vlog,
    600		.log_size = sizeof(bpf_vlog),
    601		.log_level = 2,
    602	);
    603	const char *line_ptr;
    604	int cur_line = -1;
    605	int prog_len, i;
    606	int fd_prog;
    607	int ret;
    608
    609	prog_len = probe_filter_length(prog);
    610	fd_prog = bpf_prog_load(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL",
    611				prog, prog_len, &opts);
    612	if (fd_prog < 0 && test->result != REJECT) {
    613		printf("Failed to load program.\n");
    614		printf("%s", bpf_vlog);
    615		ret = 1;
    616	} else if (fd_prog >= 0 && test->result == REJECT) {
    617		printf("Unexpected success to load!\n");
    618		printf("%s", bpf_vlog);
    619		ret = 1;
    620		close(fd_prog);
    621	} else {
    622		ret = 0;
    623		/* We make a local copy so that we can strtok() it */
    624		strncpy(bpf_vlog_copy, bpf_vlog, sizeof(bpf_vlog_copy));
    625		line_ptr = strtok(bpf_vlog_copy, "\n");
    626		for (i = 0; i < MAX_MATCHES; i++) {
    627			struct bpf_reg_match m = test->matches[i];
    628			int tmp;
    629
    630			if (!m.match)
    631				break;
    632			while (line_ptr) {
    633				cur_line = -1;
    634				sscanf(line_ptr, "%u: ", &cur_line);
    635				if (cur_line == -1)
    636					sscanf(line_ptr, "from %u to %u: ", &tmp, &cur_line);
    637				if (cur_line == m.line)
    638					break;
    639				line_ptr = strtok(NULL, "\n");
    640			}
    641			if (!line_ptr) {
    642				printf("Failed to find line %u for match: %s\n",
    643				       m.line, m.match);
    644				ret = 1;
    645				printf("%s", bpf_vlog);
    646				break;
    647			}
    648			/* Check the next line as well in case the previous line
    649			 * did not have a corresponding bpf insn. Example:
    650			 * func#0 @0
    651			 * 0: R1=ctx(off=0,imm=0) R10=fp0
    652			 * 0: (b7) r3 = 2                 ; R3_w=2
    653			 */
    654			if (!strstr(line_ptr, m.match)) {
    655				cur_line = -1;
    656				line_ptr = strtok(NULL, "\n");
    657				sscanf(line_ptr, "%u: ", &cur_line);
    658			}
    659			if (cur_line != m.line || !line_ptr ||
    660			    !strstr(line_ptr, m.match)) {
    661				printf("Failed to find match %u: %s\n",
    662				       m.line, m.match);
    663				ret = 1;
    664				printf("%s", bpf_vlog);
    665				break;
    666			}
    667		}
    668		if (fd_prog >= 0)
    669			close(fd_prog);
    670	}
    671	return ret;
    672}
    673
    674void test_align(void)
    675{
    676	unsigned int i;
    677
    678	for (i = 0; i < ARRAY_SIZE(tests); i++) {
    679		struct bpf_align_test *test = &tests[i];
    680
    681		if (!test__start_subtest(test->descr))
    682			continue;
    683
    684		CHECK_FAIL(do_test_single(test));
    685	}
    686}