1 // SPDX-License-Identifier: GPL-2.0 2 3 #include <limits.h> 4 #include <stdio.h> 5 #include <string.h> 6 #include <ctype.h> 7 #include <regex.h> 8 #include <test_progs.h> 9 10 #include "bpf/btf.h" 11 #include "bpf_util.h" 12 #include "linux/filter.h" 13 #include "linux/kernel.h" 14 #include "disasm_helpers.h" 15 16 #define MAX_PROG_TEXT_SZ (32 * 1024) 17 18 /* The code in this file serves the sole purpose of executing test cases 19 * specified in the test_cases array. Each test case specifies a program 20 * type, context field offset, and disassembly patterns that correspond 21 * to read and write instructions generated by 22 * verifier.c:convert_ctx_access() for accessing that field. 23 * 24 * For each test case, up to three programs are created: 25 * - One that uses BPF_LDX_MEM to read the context field. 26 * - One that uses BPF_STX_MEM to write to the context field. 27 * - One that uses BPF_ST_MEM to write to the context field. 28 * 29 * The disassembly of each program is then compared with the pattern 30 * specified in the test case. 31 */ 32 struct test_case { 33 char *name; 34 enum bpf_prog_type prog_type; 35 enum bpf_attach_type expected_attach_type; 36 int field_offset; 37 int field_sz; 38 /* Program generated for BPF_ST_MEM uses value 42 by default, 39 * this field allows to specify custom value. 40 */ 41 struct { 42 bool use; 43 int value; 44 } st_value; 45 /* Pattern for BPF_LDX_MEM(field_sz, dst, ctx, field_offset) */ 46 char *read; 47 /* Pattern for BPF_STX_MEM(field_sz, ctx, src, field_offset) and 48 * BPF_ST_MEM (field_sz, ctx, src, field_offset) 49 */ 50 char *write; 51 /* Pattern for BPF_ST_MEM(field_sz, ctx, src, field_offset), 52 * takes priority over `write`. 53 */ 54 char *write_st; 55 /* Pattern for BPF_STX_MEM (field_sz, ctx, src, field_offset), 56 * takes priority over `write`. 57 */ 58 char *write_stx; 59 }; 60 61 #define N(_prog_type, type, field, name_extra...) \ 62 .name = #_prog_type "." #field name_extra, \ 63 .prog_type = BPF_PROG_TYPE_##_prog_type, \ 64 .field_offset = offsetof(type, field), \ 65 .field_sz = sizeof(typeof(((type *)NULL)->field)) 66 67 static struct test_case test_cases[] = { 68 /* Sign extension on s390 changes the pattern */ 69 #if defined(__x86_64__) || defined(__aarch64__) 70 { 71 N(SCHED_CLS, struct __sk_buff, tstamp), 72 .read = "r11 = *(u8 *)($ctx + sk_buff::__mono_tc_offset);" 73 "if w11 & 0x4 goto pc+1;" 74 "goto pc+4;" 75 "if w11 & 0x3 goto pc+1;" 76 "goto pc+2;" 77 "$dst = 0;" 78 "goto pc+1;" 79 "$dst = *(u64 *)($ctx + sk_buff::tstamp);", 80 .write = "r11 = *(u8 *)($ctx + sk_buff::__mono_tc_offset);" 81 "if w11 & 0x4 goto pc+1;" 82 "goto pc+2;" 83 "w11 &= -4;" 84 "*(u8 *)($ctx + sk_buff::__mono_tc_offset) = r11;" 85 "*(u64 *)($ctx + sk_buff::tstamp) = $src;", 86 }, 87 #endif 88 { 89 N(SCHED_CLS, struct __sk_buff, priority), 90 .read = "$dst = *(u32 *)($ctx + sk_buff::priority);", 91 .write = "*(u32 *)($ctx + sk_buff::priority) = $src;", 92 }, 93 { 94 N(SCHED_CLS, struct __sk_buff, mark), 95 .read = "$dst = *(u32 *)($ctx + sk_buff::mark);", 96 .write = "*(u32 *)($ctx + sk_buff::mark) = $src;", 97 }, 98 { 99 N(SCHED_CLS, struct __sk_buff, cb[0]), 100 .read = "$dst = *(u32 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::data));", 101 .write = "*(u32 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::data)) = $src;", 102 }, 103 { 104 N(SCHED_CLS, struct __sk_buff, tc_classid), 105 .read = "$dst = *(u16 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::tc_classid));", 106 .write = "*(u16 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::tc_classid)) = $src;", 107 }, 108 { 109 N(SCHED_CLS, struct __sk_buff, tc_index), 110 .read = "$dst = *(u16 *)($ctx + sk_buff::tc_index);", 111 .write = "*(u16 *)($ctx + sk_buff::tc_index) = $src;", 112 }, 113 { 114 N(SCHED_CLS, struct __sk_buff, queue_mapping), 115 .read = "$dst = *(u16 *)($ctx + sk_buff::queue_mapping);", 116 .write_stx = "if $src >= 0xffff goto pc+1;" 117 "*(u16 *)($ctx + sk_buff::queue_mapping) = $src;", 118 .write_st = "*(u16 *)($ctx + sk_buff::queue_mapping) = $src;", 119 }, 120 { 121 /* This is a corner case in filter.c:bpf_convert_ctx_access() */ 122 N(SCHED_CLS, struct __sk_buff, queue_mapping, ".ushrt_max"), 123 .st_value = { true, USHRT_MAX }, 124 .write_st = "goto pc+0;", 125 }, 126 { 127 N(CGROUP_SOCK, struct bpf_sock, bound_dev_if), 128 .read = "$dst = *(u32 *)($ctx + sock_common::skc_bound_dev_if);", 129 .write = "*(u32 *)($ctx + sock_common::skc_bound_dev_if) = $src;", 130 }, 131 { 132 N(CGROUP_SOCK, struct bpf_sock, mark), 133 .read = "$dst = *(u32 *)($ctx + sock::sk_mark);", 134 .write = "*(u32 *)($ctx + sock::sk_mark) = $src;", 135 }, 136 { 137 N(CGROUP_SOCK, struct bpf_sock, priority), 138 .read = "$dst = *(u32 *)($ctx + sock::sk_priority);", 139 .write = "*(u32 *)($ctx + sock::sk_priority) = $src;", 140 }, 141 { 142 N(SOCK_OPS, struct bpf_sock_ops, replylong[0]), 143 .read = "$dst = *(u32 *)($ctx + bpf_sock_ops_kern::replylong);", 144 .write = "*(u32 *)($ctx + bpf_sock_ops_kern::replylong) = $src;", 145 }, 146 { 147 N(CGROUP_SYSCTL, struct bpf_sysctl, file_pos), 148 #if __BYTE_ORDER == __LITTLE_ENDIAN 149 .read = "$dst = *(u64 *)($ctx + bpf_sysctl_kern::ppos);" 150 "$dst = *(u32 *)($dst +0);", 151 .write = "*(u64 *)($ctx + bpf_sysctl_kern::tmp_reg) = r9;" 152 "r9 = *(u64 *)($ctx + bpf_sysctl_kern::ppos);" 153 "*(u32 *)(r9 +0) = $src;" 154 "r9 = *(u64 *)($ctx + bpf_sysctl_kern::tmp_reg);", 155 #else 156 .read = "$dst = *(u64 *)($ctx + bpf_sysctl_kern::ppos);" 157 "$dst = *(u32 *)($dst +4);", 158 .write = "*(u64 *)($ctx + bpf_sysctl_kern::tmp_reg) = r9;" 159 "r9 = *(u64 *)($ctx + bpf_sysctl_kern::ppos);" 160 "*(u32 *)(r9 +4) = $src;" 161 "r9 = *(u64 *)($ctx + bpf_sysctl_kern::tmp_reg);", 162 #endif 163 }, 164 { 165 N(CGROUP_SOCKOPT, struct bpf_sockopt, sk), 166 .read = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::sk);", 167 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 168 }, 169 { 170 N(CGROUP_SOCKOPT, struct bpf_sockopt, level), 171 .read = "$dst = *(u32 *)($ctx + bpf_sockopt_kern::level);", 172 .write = "*(u32 *)($ctx + bpf_sockopt_kern::level) = $src;", 173 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 174 }, 175 { 176 N(CGROUP_SOCKOPT, struct bpf_sockopt, optname), 177 .read = "$dst = *(u32 *)($ctx + bpf_sockopt_kern::optname);", 178 .write = "*(u32 *)($ctx + bpf_sockopt_kern::optname) = $src;", 179 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 180 }, 181 { 182 N(CGROUP_SOCKOPT, struct bpf_sockopt, optlen), 183 .read = "$dst = *(u32 *)($ctx + bpf_sockopt_kern::optlen);", 184 .write = "*(u32 *)($ctx + bpf_sockopt_kern::optlen) = $src;", 185 .expected_attach_type = BPF_CGROUP_SETSOCKOPT, 186 }, 187 { 188 N(CGROUP_SOCKOPT, struct bpf_sockopt, retval), 189 .read = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::current_task);" 190 "$dst = *(u64 *)($dst + task_struct::bpf_ctx);" 191 "$dst = *(u32 *)($dst + bpf_cg_run_ctx::retval);", 192 .write = "*(u64 *)($ctx + bpf_sockopt_kern::tmp_reg) = r9;" 193 "r9 = *(u64 *)($ctx + bpf_sockopt_kern::current_task);" 194 "r9 = *(u64 *)(r9 + task_struct::bpf_ctx);" 195 "*(u32 *)(r9 + bpf_cg_run_ctx::retval) = $src;" 196 "r9 = *(u64 *)($ctx + bpf_sockopt_kern::tmp_reg);", 197 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 198 }, 199 { 200 N(CGROUP_SOCKOPT, struct bpf_sockopt, optval), 201 .read = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::optval);", 202 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 203 }, 204 { 205 N(CGROUP_SOCKOPT, struct bpf_sockopt, optval_end), 206 .read = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::optval_end);", 207 .expected_attach_type = BPF_CGROUP_GETSOCKOPT, 208 }, 209 }; 210 211 #undef N 212 213 static regex_t *ident_regex; 214 static regex_t *field_regex; 215 216 static char *skip_space(char *str) 217 { 218 while (*str && isspace(*str)) 219 ++str; 220 return str; 221 } 222 223 static char *skip_space_and_semi(char *str) 224 { 225 while (*str && (isspace(*str) || *str == ';')) 226 ++str; 227 return str; 228 } 229 230 static char *match_str(char *str, char *prefix) 231 { 232 while (*str && *prefix && *str == *prefix) { 233 ++str; 234 ++prefix; 235 } 236 if (*prefix) 237 return NULL; 238 return str; 239 } 240 241 static char *match_number(char *str, int num) 242 { 243 char *next; 244 int snum = strtol(str, &next, 10); 245 246 if (next - str == 0 || num != snum) 247 return NULL; 248 249 return next; 250 } 251 252 static int find_field_offset_aux(struct btf *btf, int btf_id, char *field_name, int off) 253 { 254 const struct btf_type *type = btf__type_by_id(btf, btf_id); 255 const struct btf_member *m; 256 __u16 mnum; 257 int i; 258 259 if (!type) { 260 PRINT_FAIL("Can't find btf_type for id %d\n", btf_id); 261 return -1; 262 } 263 264 if (!btf_is_struct(type) && !btf_is_union(type)) { 265 PRINT_FAIL("BTF id %d is not struct or union\n", btf_id); 266 return -1; 267 } 268 269 m = btf_members(type); 270 mnum = btf_vlen(type); 271 272 for (i = 0; i < mnum; ++i, ++m) { 273 const char *mname = btf__name_by_offset(btf, m->name_off); 274 275 if (strcmp(mname, "") == 0) { 276 int msize = find_field_offset_aux(btf, m->type, field_name, 277 off + m->offset); 278 if (msize >= 0) 279 return msize; 280 } 281 282 if (strcmp(mname, field_name)) 283 continue; 284 285 return (off + m->offset) / 8; 286 } 287 288 return -1; 289 } 290 291 static int find_field_offset(struct btf *btf, char *pattern, regmatch_t *matches) 292 { 293 int type_sz = matches[1].rm_eo - matches[1].rm_so; 294 int field_sz = matches[2].rm_eo - matches[2].rm_so; 295 char *type = pattern + matches[1].rm_so; 296 char *field = pattern + matches[2].rm_so; 297 char field_str[128] = {}; 298 char type_str[128] = {}; 299 int btf_id, field_offset; 300 301 if (type_sz >= sizeof(type_str)) { 302 PRINT_FAIL("Malformed pattern: type ident is too long: %d\n", type_sz); 303 return -1; 304 } 305 306 if (field_sz >= sizeof(field_str)) { 307 PRINT_FAIL("Malformed pattern: field ident is too long: %d\n", field_sz); 308 return -1; 309 } 310 311 memcpy(type_str, type, type_sz); 312 type_str[type_sz] = '\0'; 313 memcpy(field_str, field, field_sz); 314 field_str[field_sz] = '\0'; 315 btf_id = btf__find_by_name(btf, type_str); 316 if (btf_id < 0) { 317 PRINT_FAIL("No BTF info for type %s\n", type_str); 318 return -1; 319 } 320 321 field_offset = find_field_offset_aux(btf, btf_id, field_str, 0); 322 if (field_offset < 0) { 323 PRINT_FAIL("No BTF info for field %s::%s\n", type_str, field_str); 324 return -1; 325 } 326 327 return field_offset; 328 } 329 330 static regex_t *compile_regex(char *pat) 331 { 332 regex_t *re; 333 int err; 334 335 re = malloc(sizeof(regex_t)); 336 if (!re) { 337 PRINT_FAIL("Can't alloc regex\n"); 338 return NULL; 339 } 340 341 err = regcomp(re, pat, REG_EXTENDED); 342 if (err) { 343 char errbuf[512]; 344 345 regerror(err, re, errbuf, sizeof(errbuf)); 346 PRINT_FAIL("Can't compile regex: %s\n", errbuf); 347 free(re); 348 return NULL; 349 } 350 351 return re; 352 } 353 354 static void free_regex(regex_t *re) 355 { 356 if (!re) 357 return; 358 359 regfree(re); 360 free(re); 361 } 362 363 static u32 max_line_len(char *str) 364 { 365 u32 max_line = 0; 366 char *next = str; 367 368 while (next) { 369 next = strchr(str, '\n'); 370 if (next) { 371 max_line = max_t(u32, max_line, (next - str)); 372 str = next + 1; 373 } else { 374 max_line = max_t(u32, max_line, strlen(str)); 375 } 376 } 377 378 return min(max_line, 60u); 379 } 380 381 /* Print strings `pattern_origin` and `text_origin` side by side, 382 * assume `pattern_pos` and `text_pos` designate location within 383 * corresponding origin string where match diverges. 384 * The output should look like: 385 * 386 * Can't match disassembly(left) with pattern(right): 387 * r2 = *(u64 *)(r1 +0) ; $dst = *(u64 *)($ctx + bpf_sockopt_kern::sk1) 388 * ^ ^ 389 * r0 = 0 ; 390 * exit ; 391 */ 392 static void print_match_error(FILE *out, 393 char *pattern_origin, char *text_origin, 394 char *pattern_pos, char *text_pos) 395 { 396 char *pattern = pattern_origin; 397 char *text = text_origin; 398 int middle = max_line_len(text) + 2; 399 400 fprintf(out, "Can't match disassembly(left) with pattern(right):\n"); 401 while (*pattern || *text) { 402 int column = 0; 403 int mark1 = -1; 404 int mark2 = -1; 405 406 /* Print one line from text */ 407 while (*text && *text != '\n') { 408 if (text == text_pos) 409 mark1 = column; 410 fputc(*text, out); 411 ++text; 412 ++column; 413 } 414 if (text == text_pos) 415 mark1 = column; 416 417 /* Pad to the middle */ 418 while (column < middle) { 419 fputc(' ', out); 420 ++column; 421 } 422 fputs("; ", out); 423 column += 3; 424 425 /* Print one line from pattern, pattern lines are terminated by ';' */ 426 while (*pattern && *pattern != ';') { 427 if (pattern == pattern_pos) 428 mark2 = column; 429 fputc(*pattern, out); 430 ++pattern; 431 ++column; 432 } 433 if (pattern == pattern_pos) 434 mark2 = column; 435 436 fputc('\n', out); 437 if (*pattern) 438 ++pattern; 439 if (*text) 440 ++text; 441 442 /* If pattern and text diverge at this line, print an 443 * additional line with '^' marks, highlighting 444 * positions where match fails. 445 */ 446 if (mark1 > 0 || mark2 > 0) { 447 for (column = 0; column <= max(mark1, mark2); ++column) { 448 if (column == mark1 || column == mark2) 449 fputc('^', out); 450 else 451 fputc(' ', out); 452 } 453 fputc('\n', out); 454 } 455 } 456 } 457 458 /* Test if `text` matches `pattern`. Pattern consists of the following elements: 459 * 460 * - Field offset references: 461 * 462 * <type>::<field> 463 * 464 * When such reference is encountered BTF is used to compute numerical 465 * value for the offset of <field> in <type>. The `text` is expected to 466 * contain matching numerical value. 467 * 468 * - Field groups: 469 * 470 * $(<type>::<field> [+ <type>::<field>]*) 471 * 472 * Allows to specify an offset that is a sum of multiple field offsets. 473 * The `text` is expected to contain matching numerical value. 474 * 475 * - Variable references, e.g. `$src`, `$dst`, `$ctx`. 476 * These are substitutions specified in `reg_map` array. 477 * If a substring of pattern is equal to `reg_map[i][0]` the `text` is 478 * expected to contain `reg_map[i][1]` in the matching position. 479 * 480 * - Whitespace is ignored, ';' counts as whitespace for `pattern`. 481 * 482 * - Any other characters, `pattern` and `text` should match one-to-one. 483 * 484 * Example of a pattern: 485 * 486 * __________ fields group ________________ 487 * ' ' 488 * *(u16 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::tc_classid)) = $src; 489 * ^^^^ '______________________' 490 * variable reference field offset reference 491 */ 492 static bool match_pattern(struct btf *btf, char *pattern, char *text, char *reg_map[][2]) 493 { 494 char *pattern_origin = pattern; 495 char *text_origin = text; 496 regmatch_t matches[3]; 497 498 _continue: 499 while (*pattern) { 500 if (!*text) 501 goto err; 502 503 /* Skip whitespace */ 504 if (isspace(*pattern) || *pattern == ';') { 505 if (!isspace(*text) && text != text_origin && isalnum(text[-1])) 506 goto err; 507 pattern = skip_space_and_semi(pattern); 508 text = skip_space(text); 509 continue; 510 } 511 512 /* Check for variable references */ 513 for (int i = 0; reg_map[i][0]; ++i) { 514 char *pattern_next, *text_next; 515 516 pattern_next = match_str(pattern, reg_map[i][0]); 517 if (!pattern_next) 518 continue; 519 520 text_next = match_str(text, reg_map[i][1]); 521 if (!text_next) 522 goto err; 523 524 pattern = pattern_next; 525 text = text_next; 526 goto _continue; 527 } 528 529 /* Match field group: 530 * $(sk_buff::cb + qdisc_skb_cb::tc_classid) 531 */ 532 if (strncmp(pattern, "$(", 2) == 0) { 533 char *group_start = pattern, *text_next; 534 int acc_offset = 0; 535 536 pattern += 2; 537 538 for (;;) { 539 int field_offset; 540 541 pattern = skip_space(pattern); 542 if (!*pattern) { 543 PRINT_FAIL("Unexpected end of pattern\n"); 544 goto err; 545 } 546 547 if (*pattern == ')') { 548 ++pattern; 549 break; 550 } 551 552 if (*pattern == '+') { 553 ++pattern; 554 continue; 555 } 556 557 printf("pattern: %s\n", pattern); 558 if (regexec(field_regex, pattern, 3, matches, 0) != 0) { 559 PRINT_FAIL("Field reference expected\n"); 560 goto err; 561 } 562 563 field_offset = find_field_offset(btf, pattern, matches); 564 if (field_offset < 0) 565 goto err; 566 567 pattern += matches[0].rm_eo; 568 acc_offset += field_offset; 569 } 570 571 text_next = match_number(text, acc_offset); 572 if (!text_next) { 573 PRINT_FAIL("No match for group offset %.*s (%d)\n", 574 (int)(pattern - group_start), 575 group_start, 576 acc_offset); 577 goto err; 578 } 579 text = text_next; 580 } 581 582 /* Match field reference: 583 * sk_buff::cb 584 */ 585 if (regexec(field_regex, pattern, 3, matches, 0) == 0) { 586 int field_offset; 587 char *text_next; 588 589 field_offset = find_field_offset(btf, pattern, matches); 590 if (field_offset < 0) 591 goto err; 592 593 text_next = match_number(text, field_offset); 594 if (!text_next) { 595 PRINT_FAIL("No match for field offset %.*s (%d)\n", 596 (int)matches[0].rm_eo, pattern, field_offset); 597 goto err; 598 } 599 600 pattern += matches[0].rm_eo; 601 text = text_next; 602 continue; 603 } 604 605 /* If pattern points to identifier not followed by '::' 606 * skip the identifier to avoid n^2 application of the 607 * field reference rule. 608 */ 609 if (regexec(ident_regex, pattern, 1, matches, 0) == 0) { 610 if (strncmp(pattern, text, matches[0].rm_eo) != 0) 611 goto err; 612 613 pattern += matches[0].rm_eo; 614 text += matches[0].rm_eo; 615 continue; 616 } 617 618 /* Match literally */ 619 if (*pattern != *text) 620 goto err; 621 622 ++pattern; 623 ++text; 624 } 625 626 return true; 627 628 err: 629 test__fail(); 630 print_match_error(stdout, pattern_origin, text_origin, pattern, text); 631 return false; 632 } 633 634 struct prog_info { 635 char *prog_kind; 636 enum bpf_prog_type prog_type; 637 enum bpf_attach_type expected_attach_type; 638 struct bpf_insn *prog; 639 u32 prog_len; 640 }; 641 642 static void match_program(struct btf *btf, 643 struct prog_info *pinfo, 644 char *pattern, 645 char *reg_map[][2], 646 bool skip_first_insn) 647 { 648 struct bpf_insn *buf = NULL, *insn, *insn_end; 649 int err = 0, prog_fd = 0; 650 FILE *prog_out = NULL; 651 char insn_buf[64]; 652 char *text = NULL; 653 __u32 cnt = 0; 654 655 text = calloc(MAX_PROG_TEXT_SZ, 1); 656 if (!text) { 657 PRINT_FAIL("Can't allocate %d bytes\n", MAX_PROG_TEXT_SZ); 658 goto out; 659 } 660 661 // TODO: log level 662 LIBBPF_OPTS(bpf_prog_load_opts, opts); 663 opts.log_buf = text; 664 opts.log_size = MAX_PROG_TEXT_SZ; 665 opts.log_level = 1 | 2 | 4; 666 opts.expected_attach_type = pinfo->expected_attach_type; 667 668 prog_fd = bpf_prog_load(pinfo->prog_type, NULL, "GPL", 669 pinfo->prog, pinfo->prog_len, &opts); 670 if (prog_fd < 0) { 671 PRINT_FAIL("Can't load program, errno %d (%s), verifier log:\n%s\n", 672 errno, strerror(errno), text); 673 goto out; 674 } 675 676 memset(text, 0, MAX_PROG_TEXT_SZ); 677 678 err = get_xlated_program(prog_fd, &buf, &cnt); 679 if (err) { 680 PRINT_FAIL("Can't load back BPF program\n"); 681 goto out; 682 } 683 684 prog_out = fmemopen(text, MAX_PROG_TEXT_SZ - 1, "w"); 685 if (!prog_out) { 686 PRINT_FAIL("Can't open memory stream\n"); 687 goto out; 688 } 689 insn_end = buf + cnt; 690 insn = buf + (skip_first_insn ? 1 : 0); 691 while (insn < insn_end) { 692 insn = disasm_insn(insn, insn_buf, sizeof(insn_buf)); 693 fprintf(prog_out, "%s\n", insn_buf); 694 } 695 fclose(prog_out); 696 697 ASSERT_TRUE(match_pattern(btf, pattern, text, reg_map), 698 pinfo->prog_kind); 699 700 out: 701 if (prog_fd) 702 close(prog_fd); 703 free(buf); 704 free(text); 705 } 706 707 static void run_one_testcase(struct btf *btf, struct test_case *test) 708 { 709 struct prog_info pinfo = {}; 710 int bpf_sz; 711 712 if (!test__start_subtest(test->name)) 713 return; 714 715 switch (test->field_sz) { 716 case 8: 717 bpf_sz = BPF_DW; 718 break; 719 case 4: 720 bpf_sz = BPF_W; 721 break; 722 case 2: 723 bpf_sz = BPF_H; 724 break; 725 case 1: 726 bpf_sz = BPF_B; 727 break; 728 default: 729 PRINT_FAIL("Unexpected field size: %d, want 8,4,2 or 1\n", test->field_sz); 730 return; 731 } 732 733 pinfo.prog_type = test->prog_type; 734 pinfo.expected_attach_type = test->expected_attach_type; 735 736 if (test->read) { 737 struct bpf_insn ldx_prog[] = { 738 BPF_LDX_MEM(bpf_sz, BPF_REG_2, BPF_REG_1, test->field_offset), 739 BPF_MOV64_IMM(BPF_REG_0, 0), 740 BPF_EXIT_INSN(), 741 }; 742 char *reg_map[][2] = { 743 { "$ctx", "r1" }, 744 { "$dst", "r2" }, 745 {} 746 }; 747 748 pinfo.prog_kind = "LDX"; 749 pinfo.prog = ldx_prog; 750 pinfo.prog_len = ARRAY_SIZE(ldx_prog); 751 match_program(btf, &pinfo, test->read, reg_map, false); 752 } 753 754 if (test->write || test->write_st || test->write_stx) { 755 struct bpf_insn stx_prog[] = { 756 BPF_MOV64_IMM(BPF_REG_2, 0), 757 BPF_STX_MEM(bpf_sz, BPF_REG_1, BPF_REG_2, test->field_offset), 758 BPF_MOV64_IMM(BPF_REG_0, 0), 759 BPF_EXIT_INSN(), 760 }; 761 char *stx_reg_map[][2] = { 762 { "$ctx", "r1" }, 763 { "$src", "r2" }, 764 {} 765 }; 766 struct bpf_insn st_prog[] = { 767 BPF_ST_MEM(bpf_sz, BPF_REG_1, test->field_offset, 768 test->st_value.use ? test->st_value.value : 42), 769 BPF_MOV64_IMM(BPF_REG_0, 0), 770 BPF_EXIT_INSN(), 771 }; 772 char *st_reg_map[][2] = { 773 { "$ctx", "r1" }, 774 { "$src", "42" }, 775 {} 776 }; 777 778 if (test->write || test->write_stx) { 779 char *pattern = test->write_stx ? test->write_stx : test->write; 780 781 pinfo.prog_kind = "STX"; 782 pinfo.prog = stx_prog; 783 pinfo.prog_len = ARRAY_SIZE(stx_prog); 784 match_program(btf, &pinfo, pattern, stx_reg_map, true); 785 } 786 787 if (test->write || test->write_st) { 788 char *pattern = test->write_st ? test->write_st : test->write; 789 790 pinfo.prog_kind = "ST"; 791 pinfo.prog = st_prog; 792 pinfo.prog_len = ARRAY_SIZE(st_prog); 793 match_program(btf, &pinfo, pattern, st_reg_map, false); 794 } 795 } 796 797 test__end_subtest(); 798 } 799 800 void test_ctx_rewrite(void) 801 { 802 struct btf *btf; 803 int i; 804 805 field_regex = compile_regex("^([[:alpha:]_][[:alnum:]_]+)::([[:alpha:]_][[:alnum:]_]+)"); 806 ident_regex = compile_regex("^[[:alpha:]_][[:alnum:]_]+"); 807 if (!field_regex || !ident_regex) 808 return; 809 810 btf = btf__load_vmlinux_btf(); 811 if (!btf) { 812 PRINT_FAIL("Can't load vmlinux BTF, errno %d (%s)\n", errno, strerror(errno)); 813 goto out; 814 } 815 816 for (i = 0; i < ARRAY_SIZE(test_cases); ++i) 817 run_one_testcase(btf, &test_cases[i]); 818 819 out: 820 btf__free(btf); 821 free_regex(field_regex); 822 free_regex(ident_regex); 823 } 824