1 // SPDX-License-Identifier: GPL-2.0 2 #include <test_progs.h> 3 4 #define MAX_INSNS 512 5 #define MAX_MATCHES 24 6 7 struct bpf_reg_match { 8 unsigned int line; 9 const char *reg; 10 const char *match; 11 }; 12 13 struct bpf_align_test { 14 const char *descr; 15 struct bpf_insn insns[MAX_INSNS]; 16 enum { 17 UNDEF, 18 ACCEPT, 19 REJECT 20 } 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 26 static struct bpf_align_test tests[] = { 27 /* Four tests of known constants. These aren't staggeringly 28 * 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 first 267 * 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 values 280 * 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 known 300 * 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 in 305 * 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 variable 311 * offset is considered using reg->aux_off_align which 312 * 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 BPF 318 * verifier's precision backtracking logging 319 * interfering we also have a no-op R4 = R5 320 * instruction to validate R5 state. We also check 321 * 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 in 326 * 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->off 331 * (14) which is 16. Then the variable offset is 4-byte 332 * aligned, so the total offset is 4-byte aligned and 333 * 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 a 342 * variable offset of (4n). See comment for insn #18 343 * 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_off 350 * 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), so 358 * the total offset is 4-byte aligned and meets the 359 * 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 known 398 * 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), so 410 * the total offset is 4-byte aligned and meets the 411 * load's requirements. 412 */ 413 {15, "R5", "var_off=(0x2; 0x7fc)"}, 414 /* Newly read value in R6 was shifted left by 2, so has 415 * known alignment of 4. 416 */ 417 {17, "R6_w", "var_off=(0x0; 0x3fc)"}, 418 /* Added (4n) to packet pointer's (4n+2) var_off, giving 419 * 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), so 426 * the total offset is 4-byte aligned and meets the 427 * 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 offset 442 * 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, because 466 * 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 able 476 * to overflow if the packet pointer started in the 477 * upper half of the address space. 478 * So we did not get a 'range' on R6, and the access 479 * 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 subtract 493 * it from the first one 494 */ 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 known 514 * 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), so 529 * the total offset is 4-byte aligned and meets the 530 * 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 bounded 539 * 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 == 2 551 */ 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 known 567 * 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), so 582 * the total offset is 4-byte aligned and meets the 583 * load's requirements. 584 */ 585 {20, "R5", "var_off=(0x2; 0x7fc)"}, 586 }, 587 }, 588 }; 589 590 static 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 600 static char bpf_vlog[32768]; 601 602 static 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 line 660 * did not have a corresponding bpf insn. Example: 661 * func#0 @0 662 * 0: R1=ctx() R10=fp0 663 * 0: (b7) r3 = 2 ; R3_w=2 664 * 665 * Sometimes it's actually two lines below, e.g. when 666 * 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=fp0 668 * 6: R0_w=pkt(off=8,r=8) R1=ctx() R2_w=pkt(r=8) R3_w=pkt_end() R10=fp0 669 * 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 691 void 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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.