Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <test_progs.h>
0003 
0004 #define MAX_INSNS   512
0005 #define MAX_MATCHES 16
0006 
0007 struct bpf_reg_match {
0008     unsigned int line;
0009     const char *match;
0010 };
0011 
0012 struct bpf_align_test {
0013     const char *descr;
0014     struct bpf_insn insns[MAX_INSNS];
0015     enum {
0016         UNDEF,
0017         ACCEPT,
0018         REJECT
0019     } result;
0020     enum bpf_prog_type prog_type;
0021     /* Matches must be in order of increasing line */
0022     struct bpf_reg_match matches[MAX_MATCHES];
0023 };
0024 
0025 static struct bpf_align_test tests[] = {
0026     /* Four tests of known constants.  These aren't staggeringly
0027      * interesting since we track exact values now.
0028      */
0029     {
0030         .descr = "mov",
0031         .insns = {
0032             BPF_MOV64_IMM(BPF_REG_3, 2),
0033             BPF_MOV64_IMM(BPF_REG_3, 4),
0034             BPF_MOV64_IMM(BPF_REG_3, 8),
0035             BPF_MOV64_IMM(BPF_REG_3, 16),
0036             BPF_MOV64_IMM(BPF_REG_3, 32),
0037             BPF_MOV64_IMM(BPF_REG_0, 0),
0038             BPF_EXIT_INSN(),
0039         },
0040         .prog_type = BPF_PROG_TYPE_SCHED_CLS,
0041         .matches = {
0042             {0, "R1=ctx(off=0,imm=0)"},
0043             {0, "R10=fp0"},
0044             {0, "R3_w=2"},
0045             {1, "R3_w=4"},
0046             {2, "R3_w=8"},
0047             {3, "R3_w=16"},
0048             {4, "R3_w=32"},
0049         },
0050     },
0051     {
0052         .descr = "shift",
0053         .insns = {
0054             BPF_MOV64_IMM(BPF_REG_3, 1),
0055             BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
0056             BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
0057             BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
0058             BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
0059             BPF_ALU64_IMM(BPF_RSH, BPF_REG_3, 4),
0060             BPF_MOV64_IMM(BPF_REG_4, 32),
0061             BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
0062             BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
0063             BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
0064             BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
0065             BPF_MOV64_IMM(BPF_REG_0, 0),
0066             BPF_EXIT_INSN(),
0067         },
0068         .prog_type = BPF_PROG_TYPE_SCHED_CLS,
0069         .matches = {
0070             {0, "R1=ctx(off=0,imm=0)"},
0071             {0, "R10=fp0"},
0072             {0, "R3_w=1"},
0073             {1, "R3_w=2"},
0074             {2, "R3_w=4"},
0075             {3, "R3_w=8"},
0076             {4, "R3_w=16"},
0077             {5, "R3_w=1"},
0078             {6, "R4_w=32"},
0079             {7, "R4_w=16"},
0080             {8, "R4_w=8"},
0081             {9, "R4_w=4"},
0082             {10, "R4_w=2"},
0083         },
0084     },
0085     {
0086         .descr = "addsub",
0087         .insns = {
0088             BPF_MOV64_IMM(BPF_REG_3, 4),
0089             BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 4),
0090             BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 2),
0091             BPF_MOV64_IMM(BPF_REG_4, 8),
0092             BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
0093             BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 2),
0094             BPF_MOV64_IMM(BPF_REG_0, 0),
0095             BPF_EXIT_INSN(),
0096         },
0097         .prog_type = BPF_PROG_TYPE_SCHED_CLS,
0098         .matches = {
0099             {0, "R1=ctx(off=0,imm=0)"},
0100             {0, "R10=fp0"},
0101             {0, "R3_w=4"},
0102             {1, "R3_w=8"},
0103             {2, "R3_w=10"},
0104             {3, "R4_w=8"},
0105             {4, "R4_w=12"},
0106             {5, "R4_w=14"},
0107         },
0108     },
0109     {
0110         .descr = "mul",
0111         .insns = {
0112             BPF_MOV64_IMM(BPF_REG_3, 7),
0113             BPF_ALU64_IMM(BPF_MUL, BPF_REG_3, 1),
0114             BPF_ALU64_IMM(BPF_MUL, BPF_REG_3, 2),
0115             BPF_ALU64_IMM(BPF_MUL, BPF_REG_3, 4),
0116             BPF_MOV64_IMM(BPF_REG_0, 0),
0117             BPF_EXIT_INSN(),
0118         },
0119         .prog_type = BPF_PROG_TYPE_SCHED_CLS,
0120         .matches = {
0121             {0, "R1=ctx(off=0,imm=0)"},
0122             {0, "R10=fp0"},
0123             {0, "R3_w=7"},
0124             {1, "R3_w=7"},
0125             {2, "R3_w=14"},
0126             {3, "R3_w=56"},
0127         },
0128     },
0129 
0130     /* Tests using unknown values */
0131 #define PREP_PKT_POINTERS \
0132     BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, \
0133             offsetof(struct __sk_buff, data)), \
0134     BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, \
0135             offsetof(struct __sk_buff, data_end))
0136 
0137 #define LOAD_UNKNOWN(DST_REG) \
0138     PREP_PKT_POINTERS, \
0139     BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), \
0140     BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), \
0141     BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 1), \
0142     BPF_EXIT_INSN(), \
0143     BPF_LDX_MEM(BPF_B, DST_REG, BPF_REG_2, 0)
0144 
0145     {
0146         .descr = "unknown shift",
0147         .insns = {
0148             LOAD_UNKNOWN(BPF_REG_3),
0149             BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
0150             BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
0151             BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
0152             BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
0153             LOAD_UNKNOWN(BPF_REG_4),
0154             BPF_ALU64_IMM(BPF_LSH, BPF_REG_4, 5),
0155             BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
0156             BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
0157             BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
0158             BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
0159             BPF_MOV64_IMM(BPF_REG_0, 0),
0160             BPF_EXIT_INSN(),
0161         },
0162         .prog_type = BPF_PROG_TYPE_SCHED_CLS,
0163         .matches = {
0164             {6, "R0_w=pkt(off=8,r=8,imm=0)"},
0165             {6, "R3_w=scalar(umax=255,var_off=(0x0; 0xff))"},
0166             {7, "R3_w=scalar(umax=510,var_off=(0x0; 0x1fe))"},
0167             {8, "R3_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
0168             {9, "R3_w=scalar(umax=2040,var_off=(0x0; 0x7f8))"},
0169             {10, "R3_w=scalar(umax=4080,var_off=(0x0; 0xff0))"},
0170             {12, "R3_w=pkt_end(off=0,imm=0)"},
0171             {17, "R4_w=scalar(umax=255,var_off=(0x0; 0xff))"},
0172             {18, "R4_w=scalar(umax=8160,var_off=(0x0; 0x1fe0))"},
0173             {19, "R4_w=scalar(umax=4080,var_off=(0x0; 0xff0))"},
0174             {20, "R4_w=scalar(umax=2040,var_off=(0x0; 0x7f8))"},
0175             {21, "R4_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
0176             {22, "R4_w=scalar(umax=510,var_off=(0x0; 0x1fe))"},
0177         },
0178     },
0179     {
0180         .descr = "unknown mul",
0181         .insns = {
0182             LOAD_UNKNOWN(BPF_REG_3),
0183             BPF_MOV64_REG(BPF_REG_4, BPF_REG_3),
0184             BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 1),
0185             BPF_MOV64_REG(BPF_REG_4, BPF_REG_3),
0186             BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 2),
0187             BPF_MOV64_REG(BPF_REG_4, BPF_REG_3),
0188             BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 4),
0189             BPF_MOV64_REG(BPF_REG_4, BPF_REG_3),
0190             BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 8),
0191             BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 2),
0192             BPF_MOV64_IMM(BPF_REG_0, 0),
0193             BPF_EXIT_INSN(),
0194         },
0195         .prog_type = BPF_PROG_TYPE_SCHED_CLS,
0196         .matches = {
0197             {6, "R3_w=scalar(umax=255,var_off=(0x0; 0xff))"},
0198             {7, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"},
0199             {8, "R4_w=scalar(umax=255,var_off=(0x0; 0xff))"},
0200             {9, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"},
0201             {10, "R4_w=scalar(umax=510,var_off=(0x0; 0x1fe))"},
0202             {11, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"},
0203             {12, "R4_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
0204             {13, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"},
0205             {14, "R4_w=scalar(umax=2040,var_off=(0x0; 0x7f8))"},
0206             {15, "R4_w=scalar(umax=4080,var_off=(0x0; 0xff0))"},
0207         },
0208     },
0209     {
0210         .descr = "packet const offset",
0211         .insns = {
0212             PREP_PKT_POINTERS,
0213             BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
0214 
0215             BPF_MOV64_IMM(BPF_REG_0, 0),
0216 
0217             /* Skip over ethernet header.  */
0218             BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
0219             BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
0220             BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
0221             BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
0222             BPF_EXIT_INSN(),
0223 
0224             BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_5, 0),
0225             BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_5, 1),
0226             BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_5, 2),
0227             BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_5, 3),
0228             BPF_LDX_MEM(BPF_H, BPF_REG_4, BPF_REG_5, 0),
0229             BPF_LDX_MEM(BPF_H, BPF_REG_4, BPF_REG_5, 2),
0230             BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
0231 
0232             BPF_MOV64_IMM(BPF_REG_0, 0),
0233             BPF_EXIT_INSN(),
0234         },
0235         .prog_type = BPF_PROG_TYPE_SCHED_CLS,
0236         .matches = {
0237             {2, "R5_w=pkt(off=0,r=0,imm=0)"},
0238             {4, "R5_w=pkt(off=14,r=0,imm=0)"},
0239             {5, "R4_w=pkt(off=14,r=0,imm=0)"},
0240             {9, "R2=pkt(off=0,r=18,imm=0)"},
0241             {10, "R5=pkt(off=14,r=18,imm=0)"},
0242             {10, "R4_w=scalar(umax=255,var_off=(0x0; 0xff))"},
0243             {13, "R4_w=scalar(umax=65535,var_off=(0x0; 0xffff))"},
0244             {14, "R4_w=scalar(umax=65535,var_off=(0x0; 0xffff))"},
0245         },
0246     },
0247     {
0248         .descr = "packet variable offset",
0249         .insns = {
0250             LOAD_UNKNOWN(BPF_REG_6),
0251             BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
0252 
0253             /* First, add a constant to the R5 packet pointer,
0254              * then a variable with a known alignment.
0255              */
0256             BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
0257             BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
0258             BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
0259             BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
0260             BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
0261             BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
0262             BPF_EXIT_INSN(),
0263             BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
0264 
0265             /* Now, test in the other direction.  Adding first
0266              * the variable offset to R5, then the constant.
0267              */
0268             BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
0269             BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
0270             BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
0271             BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
0272             BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
0273             BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
0274             BPF_EXIT_INSN(),
0275             BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
0276 
0277             /* Test multiple accumulations of unknown values
0278              * into a packet pointer.
0279              */
0280             BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
0281             BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
0282             BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
0283             BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 4),
0284             BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
0285             BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
0286             BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
0287             BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
0288             BPF_EXIT_INSN(),
0289             BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
0290 
0291             BPF_MOV64_IMM(BPF_REG_0, 0),
0292             BPF_EXIT_INSN(),
0293         },
0294         .prog_type = BPF_PROG_TYPE_SCHED_CLS,
0295         .matches = {
0296             /* Calculated offset in R6 has unknown value, but known
0297              * alignment of 4.
0298              */
0299             {6, "R2_w=pkt(off=0,r=8,imm=0)"},
0300             {7, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
0301             /* Offset is added to packet pointer R5, resulting in
0302              * known fixed offset, and variable offset from R6.
0303              */
0304             {11, "R5_w=pkt(id=1,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
0305             /* At the time the word size load is performed from R5,
0306              * it's total offset is NET_IP_ALIGN + reg->off (0) +
0307              * reg->aux_off (14) which is 16.  Then the variable
0308              * offset is considered using reg->aux_off_align which
0309              * is 4 and meets the load's requirements.
0310              */
0311             {15, "R4=pkt(id=1,off=18,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
0312             {15, "R5=pkt(id=1,off=14,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
0313             /* Variable offset is added to R5 packet pointer,
0314              * resulting in auxiliary alignment of 4.
0315              */
0316             {17, "R5_w=pkt(id=2,off=0,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
0317             /* Constant offset is added to R5, resulting in
0318              * reg->off of 14.
0319              */
0320             {18, "R5_w=pkt(id=2,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
0321             /* At the time the word size load is performed from R5,
0322              * its total fixed offset is NET_IP_ALIGN + reg->off
0323              * (14) which is 16.  Then the variable offset is 4-byte
0324              * aligned, so the total offset is 4-byte aligned and
0325              * meets the load's requirements.
0326              */
0327             {23, "R4=pkt(id=2,off=18,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
0328             {23, "R5=pkt(id=2,off=14,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
0329             /* Constant offset is added to R5 packet pointer,
0330              * resulting in reg->off value of 14.
0331              */
0332             {25, "R5_w=pkt(off=14,r=8"},
0333             /* Variable offset is added to R5, resulting in a
0334              * variable offset of (4n).
0335              */
0336             {26, "R5_w=pkt(id=3,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
0337             /* Constant is added to R5 again, setting reg->off to 18. */
0338             {27, "R5_w=pkt(id=3,off=18,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
0339             /* And once more we add a variable; resulting var_off
0340              * is still (4n), fixed offset is not changed.
0341              * Also, we create a new reg->id.
0342              */
0343             {28, "R5_w=pkt(id=4,off=18,r=0,umax=2040,var_off=(0x0; 0x7fc)"},
0344             /* At the time the word size load is performed from R5,
0345              * its total fixed offset is NET_IP_ALIGN + reg->off (18)
0346              * which is 20.  Then the variable offset is (4n), so
0347              * the total offset is 4-byte aligned and meets the
0348              * load's requirements.
0349              */
0350             {33, "R4=pkt(id=4,off=22,r=22,umax=2040,var_off=(0x0; 0x7fc)"},
0351             {33, "R5=pkt(id=4,off=18,r=22,umax=2040,var_off=(0x0; 0x7fc)"},
0352         },
0353     },
0354     {
0355         .descr = "packet variable offset 2",
0356         .insns = {
0357             /* Create an unknown offset, (4n+2)-aligned */
0358             LOAD_UNKNOWN(BPF_REG_6),
0359             BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
0360             BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14),
0361             /* Add it to the packet pointer */
0362             BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
0363             BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
0364             /* Check bounds and perform a read */
0365             BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
0366             BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
0367             BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
0368             BPF_EXIT_INSN(),
0369             BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
0370             /* Make a (4n) offset from the value we just read */
0371             BPF_ALU64_IMM(BPF_AND, BPF_REG_6, 0xff),
0372             BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
0373             /* Add it to the packet pointer */
0374             BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
0375             /* Check bounds and perform a read */
0376             BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
0377             BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
0378             BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
0379             BPF_EXIT_INSN(),
0380             BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
0381             BPF_MOV64_IMM(BPF_REG_0, 0),
0382             BPF_EXIT_INSN(),
0383         },
0384         .prog_type = BPF_PROG_TYPE_SCHED_CLS,
0385         .matches = {
0386             /* Calculated offset in R6 has unknown value, but known
0387              * alignment of 4.
0388              */
0389             {6, "R2_w=pkt(off=0,r=8,imm=0)"},
0390             {7, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
0391             /* Adding 14 makes R6 be (4n+2) */
0392             {8, "R6_w=scalar(umin=14,umax=1034,var_off=(0x2; 0x7fc))"},
0393             /* Packet pointer has (4n+2) offset */
0394             {11, "R5_w=pkt(id=1,off=0,r=0,umin=14,umax=1034,var_off=(0x2; 0x7fc)"},
0395             {12, "R4=pkt(id=1,off=4,r=0,umin=14,umax=1034,var_off=(0x2; 0x7fc)"},
0396             /* At the time the word size load is performed from R5,
0397              * its total fixed offset is NET_IP_ALIGN + reg->off (0)
0398              * which is 2.  Then the variable offset is (4n+2), so
0399              * the total offset is 4-byte aligned and meets the
0400              * load's requirements.
0401              */
0402             {15, "R5=pkt(id=1,off=0,r=4,umin=14,umax=1034,var_off=(0x2; 0x7fc)"},
0403             /* Newly read value in R6 was shifted left by 2, so has
0404              * known alignment of 4.
0405              */
0406             {17, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
0407             /* Added (4n) to packet pointer's (4n+2) var_off, giving
0408              * another (4n+2).
0409              */
0410             {19, "R5_w=pkt(id=2,off=0,r=0,umin=14,umax=2054,var_off=(0x2; 0xffc)"},
0411             {20, "R4=pkt(id=2,off=4,r=0,umin=14,umax=2054,var_off=(0x2; 0xffc)"},
0412             /* At the time the word size load is performed from R5,
0413              * its total fixed offset is NET_IP_ALIGN + reg->off (0)
0414              * which is 2.  Then the variable offset is (4n+2), so
0415              * the total offset is 4-byte aligned and meets the
0416              * load's requirements.
0417              */
0418             {23, "R5=pkt(id=2,off=0,r=4,umin=14,umax=2054,var_off=(0x2; 0xffc)"},
0419         },
0420     },
0421     {
0422         .descr = "dubious pointer arithmetic",
0423         .insns = {
0424             PREP_PKT_POINTERS,
0425             BPF_MOV64_IMM(BPF_REG_0, 0),
0426             /* (ptr - ptr) << 2 */
0427             BPF_MOV64_REG(BPF_REG_5, BPF_REG_3),
0428             BPF_ALU64_REG(BPF_SUB, BPF_REG_5, BPF_REG_2),
0429             BPF_ALU64_IMM(BPF_LSH, BPF_REG_5, 2),
0430             /* We have a (4n) value.  Let's make a packet offset
0431              * out of it.  First add 14, to make it a (4n+2)
0432              */
0433             BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
0434             /* Then make sure it's nonnegative */
0435             BPF_JMP_IMM(BPF_JSGE, BPF_REG_5, 0, 1),
0436             BPF_EXIT_INSN(),
0437             /* Add it to packet pointer */
0438             BPF_MOV64_REG(BPF_REG_6, BPF_REG_2),
0439             BPF_ALU64_REG(BPF_ADD, BPF_REG_6, BPF_REG_5),
0440             /* Check bounds and perform a read */
0441             BPF_MOV64_REG(BPF_REG_4, BPF_REG_6),
0442             BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
0443             BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
0444             BPF_EXIT_INSN(),
0445             BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_6, 0),
0446             BPF_EXIT_INSN(),
0447         },
0448         .prog_type = BPF_PROG_TYPE_SCHED_CLS,
0449         .result = REJECT,
0450         .matches = {
0451             {3, "R5_w=pkt_end(off=0,imm=0)"},
0452             /* (ptr - ptr) << 2 == unknown, (4n) */
0453             {5, "R5_w=scalar(smax=9223372036854775804,umax=18446744073709551612,var_off=(0x0; 0xfffffffffffffffc)"},
0454             /* (4n) + 14 == (4n+2).  We blow our bounds, because
0455              * the add could overflow.
0456              */
0457             {6, "R5_w=scalar(smin=-9223372036854775806,smax=9223372036854775806,umin=2,umax=18446744073709551614,var_off=(0x2; 0xfffffffffffffffc)"},
0458             /* Checked s>=0 */
0459             {9, "R5=scalar(umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
0460             /* packet pointer + nonnegative (4n+2) */
0461             {11, "R6_w=pkt(id=1,off=0,r=0,umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
0462             {12, "R4_w=pkt(id=1,off=4,r=0,umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
0463             /* NET_IP_ALIGN + (4n+2) == (4n), alignment is fine.
0464              * We checked the bounds, but it might have been able
0465              * to overflow if the packet pointer started in the
0466              * upper half of the address space.
0467              * So we did not get a 'range' on R6, and the access
0468              * attempt will fail.
0469              */
0470             {15, "R6_w=pkt(id=1,off=0,r=0,umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
0471         }
0472     },
0473     {
0474         .descr = "variable subtraction",
0475         .insns = {
0476             /* Create an unknown offset, (4n+2)-aligned */
0477             LOAD_UNKNOWN(BPF_REG_6),
0478             BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
0479             BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
0480             BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14),
0481             /* Create another unknown, (4n)-aligned, and subtract
0482              * it from the first one
0483              */
0484             BPF_ALU64_IMM(BPF_LSH, BPF_REG_7, 2),
0485             BPF_ALU64_REG(BPF_SUB, BPF_REG_6, BPF_REG_7),
0486             /* Bounds-check the result */
0487             BPF_JMP_IMM(BPF_JSGE, BPF_REG_6, 0, 1),
0488             BPF_EXIT_INSN(),
0489             /* Add it to the packet pointer */
0490             BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
0491             BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
0492             /* Check bounds and perform a read */
0493             BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
0494             BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
0495             BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
0496             BPF_EXIT_INSN(),
0497             BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
0498             BPF_EXIT_INSN(),
0499         },
0500         .prog_type = BPF_PROG_TYPE_SCHED_CLS,
0501         .matches = {
0502             /* Calculated offset in R6 has unknown value, but known
0503              * alignment of 4.
0504              */
0505             {6, "R2_w=pkt(off=0,r=8,imm=0)"},
0506             {8, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
0507             /* Adding 14 makes R6 be (4n+2) */
0508             {9, "R6_w=scalar(umin=14,umax=1034,var_off=(0x2; 0x7fc))"},
0509             /* New unknown value in R7 is (4n) */
0510             {10, "R7_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
0511             /* Subtracting it from R6 blows our unsigned bounds */
0512             {11, "R6=scalar(smin=-1006,smax=1034,umin=2,umax=18446744073709551614,var_off=(0x2; 0xfffffffffffffffc)"},
0513             /* Checked s>= 0 */
0514             {14, "R6=scalar(umin=2,umax=1034,var_off=(0x2; 0x7fc))"},
0515             /* At the time the word size load is performed from R5,
0516              * its total fixed offset is NET_IP_ALIGN + reg->off (0)
0517              * which is 2.  Then the variable offset is (4n+2), so
0518              * the total offset is 4-byte aligned and meets the
0519              * load's requirements.
0520              */
0521             {20, "R5=pkt(id=2,off=0,r=4,umin=2,umax=1034,var_off=(0x2; 0x7fc)"},
0522 
0523         },
0524     },
0525     {
0526         .descr = "pointer variable subtraction",
0527         .insns = {
0528             /* Create an unknown offset, (4n+2)-aligned and bounded
0529              * to [14,74]
0530              */
0531             LOAD_UNKNOWN(BPF_REG_6),
0532             BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
0533             BPF_ALU64_IMM(BPF_AND, BPF_REG_6, 0xf),
0534             BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
0535             BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14),
0536             /* Subtract it from the packet pointer */
0537             BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
0538             BPF_ALU64_REG(BPF_SUB, BPF_REG_5, BPF_REG_6),
0539             /* Create another unknown, (4n)-aligned and >= 74.
0540              * That in fact means >= 76, since 74 % 4 == 2
0541              */
0542             BPF_ALU64_IMM(BPF_LSH, BPF_REG_7, 2),
0543             BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 76),
0544             /* Add it to the packet pointer */
0545             BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_7),
0546             /* Check bounds and perform a read */
0547             BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
0548             BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
0549             BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
0550             BPF_EXIT_INSN(),
0551             BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
0552             BPF_EXIT_INSN(),
0553         },
0554         .prog_type = BPF_PROG_TYPE_SCHED_CLS,
0555         .matches = {
0556             /* Calculated offset in R6 has unknown value, but known
0557              * alignment of 4.
0558              */
0559             {6, "R2_w=pkt(off=0,r=8,imm=0)"},
0560             {9, "R6_w=scalar(umax=60,var_off=(0x0; 0x3c))"},
0561             /* Adding 14 makes R6 be (4n+2) */
0562             {10, "R6_w=scalar(umin=14,umax=74,var_off=(0x2; 0x7c))"},
0563             /* Subtracting from packet pointer overflows ubounds */
0564             {13, "R5_w=pkt(id=2,off=0,r=8,umin=18446744073709551542,umax=18446744073709551602,var_off=(0xffffffffffffff82; 0x7c)"},
0565             /* New unknown value in R7 is (4n), >= 76 */
0566             {14, "R7_w=scalar(umin=76,umax=1096,var_off=(0x0; 0x7fc))"},
0567             /* Adding it to packet pointer gives nice bounds again */
0568             {16, "R5_w=pkt(id=3,off=0,r=0,umin=2,umax=1082,var_off=(0x2; 0xfffffffc)"},
0569             /* At the time the word size load is performed from R5,
0570              * its total fixed offset is NET_IP_ALIGN + reg->off (0)
0571              * which is 2.  Then the variable offset is (4n+2), so
0572              * the total offset is 4-byte aligned and meets the
0573              * load's requirements.
0574              */
0575             {20, "R5=pkt(id=3,off=0,r=4,umin=2,umax=1082,var_off=(0x2; 0xfffffffc)"},
0576         },
0577     },
0578 };
0579 
0580 static int probe_filter_length(const struct bpf_insn *fp)
0581 {
0582     int len;
0583 
0584     for (len = MAX_INSNS - 1; len > 0; --len)
0585         if (fp[len].code != 0 || fp[len].imm != 0)
0586             break;
0587     return len + 1;
0588 }
0589 
0590 static char bpf_vlog[32768];
0591 
0592 static int do_test_single(struct bpf_align_test *test)
0593 {
0594     struct bpf_insn *prog = test->insns;
0595     int prog_type = test->prog_type;
0596     char bpf_vlog_copy[32768];
0597     LIBBPF_OPTS(bpf_prog_load_opts, opts,
0598         .prog_flags = BPF_F_STRICT_ALIGNMENT,
0599         .log_buf = bpf_vlog,
0600         .log_size = sizeof(bpf_vlog),
0601         .log_level = 2,
0602     );
0603     const char *line_ptr;
0604     int cur_line = -1;
0605     int prog_len, i;
0606     int fd_prog;
0607     int ret;
0608 
0609     prog_len = probe_filter_length(prog);
0610     fd_prog = bpf_prog_load(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL",
0611                 prog, prog_len, &opts);
0612     if (fd_prog < 0 && test->result != REJECT) {
0613         printf("Failed to load program.\n");
0614         printf("%s", bpf_vlog);
0615         ret = 1;
0616     } else if (fd_prog >= 0 && test->result == REJECT) {
0617         printf("Unexpected success to load!\n");
0618         printf("%s", bpf_vlog);
0619         ret = 1;
0620         close(fd_prog);
0621     } else {
0622         ret = 0;
0623         /* We make a local copy so that we can strtok() it */
0624         strncpy(bpf_vlog_copy, bpf_vlog, sizeof(bpf_vlog_copy));
0625         line_ptr = strtok(bpf_vlog_copy, "\n");
0626         for (i = 0; i < MAX_MATCHES; i++) {
0627             struct bpf_reg_match m = test->matches[i];
0628             int tmp;
0629 
0630             if (!m.match)
0631                 break;
0632             while (line_ptr) {
0633                 cur_line = -1;
0634                 sscanf(line_ptr, "%u: ", &cur_line);
0635                 if (cur_line == -1)
0636                     sscanf(line_ptr, "from %u to %u: ", &tmp, &cur_line);
0637                 if (cur_line == m.line)
0638                     break;
0639                 line_ptr = strtok(NULL, "\n");
0640             }
0641             if (!line_ptr) {
0642                 printf("Failed to find line %u for match: %s\n",
0643                        m.line, m.match);
0644                 ret = 1;
0645                 printf("%s", bpf_vlog);
0646                 break;
0647             }
0648             /* Check the next line as well in case the previous line
0649              * did not have a corresponding bpf insn. Example:
0650              * func#0 @0
0651              * 0: R1=ctx(off=0,imm=0) R10=fp0
0652              * 0: (b7) r3 = 2                 ; R3_w=2
0653              */
0654             if (!strstr(line_ptr, m.match)) {
0655                 cur_line = -1;
0656                 line_ptr = strtok(NULL, "\n");
0657                 sscanf(line_ptr, "%u: ", &cur_line);
0658             }
0659             if (cur_line != m.line || !line_ptr ||
0660                 !strstr(line_ptr, m.match)) {
0661                 printf("Failed to find match %u: %s\n",
0662                        m.line, m.match);
0663                 ret = 1;
0664                 printf("%s", bpf_vlog);
0665                 break;
0666             }
0667         }
0668         if (fd_prog >= 0)
0669             close(fd_prog);
0670     }
0671     return ret;
0672 }
0673 
0674 void test_align(void)
0675 {
0676     unsigned int i;
0677 
0678     for (i = 0; i < ARRAY_SIZE(tests); i++) {
0679         struct bpf_align_test *test = &tests[i];
0680 
0681         if (!test__start_subtest(test->descr))
0682             continue;
0683 
0684         CHECK_FAIL(do_test_single(test));
0685     }
0686 }