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