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