822 lines · c
1// SPDX-License-Identifier: GPL-2.02 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 cases19 * specified in the test_cases array. Each test case specifies a program20 * type, context field offset, and disassembly patterns that correspond21 * to read and write instructions generated by22 * 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 pattern30 * specified in the test case.31 */32struct 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) and48 * 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 67static 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#endif88 {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_ENDIAN149 .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#else156 .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#endif163 },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 N212 213static regex_t *ident_regex;214static regex_t *field_regex;215 216static char *skip_space(char *str)217{218 while (*str && isspace(*str))219 ++str;220 return str;221}222 223static char *skip_space_and_semi(char *str)224{225 while (*str && (isspace(*str) || *str == ';'))226 ++str;227 return str;228}229 230static 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 241static 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 252static 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 291static 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 strncpy(type_str, type, type_sz);312 strncpy(field_str, field, field_sz);313 btf_id = btf__find_by_name(btf, type_str);314 if (btf_id < 0) {315 PRINT_FAIL("No BTF info for type %s\n", type_str);316 return -1;317 }318 319 field_offset = find_field_offset_aux(btf, btf_id, field_str, 0);320 if (field_offset < 0) {321 PRINT_FAIL("No BTF info for field %s::%s\n", type_str, field_str);322 return -1;323 }324 325 return field_offset;326}327 328static regex_t *compile_regex(char *pat)329{330 regex_t *re;331 int err;332 333 re = malloc(sizeof(regex_t));334 if (!re) {335 PRINT_FAIL("Can't alloc regex\n");336 return NULL;337 }338 339 err = regcomp(re, pat, REG_EXTENDED);340 if (err) {341 char errbuf[512];342 343 regerror(err, re, errbuf, sizeof(errbuf));344 PRINT_FAIL("Can't compile regex: %s\n", errbuf);345 free(re);346 return NULL;347 }348 349 return re;350}351 352static void free_regex(regex_t *re)353{354 if (!re)355 return;356 357 regfree(re);358 free(re);359}360 361static u32 max_line_len(char *str)362{363 u32 max_line = 0;364 char *next = str;365 366 while (next) {367 next = strchr(str, '\n');368 if (next) {369 max_line = max_t(u32, max_line, (next - str));370 str = next + 1;371 } else {372 max_line = max_t(u32, max_line, strlen(str));373 }374 }375 376 return min(max_line, 60u);377}378 379/* Print strings `pattern_origin` and `text_origin` side by side,380 * assume `pattern_pos` and `text_pos` designate location within381 * corresponding origin string where match diverges.382 * The output should look like:383 *384 * Can't match disassembly(left) with pattern(right):385 * r2 = *(u64 *)(r1 +0) ; $dst = *(u64 *)($ctx + bpf_sockopt_kern::sk1)386 * ^ ^387 * r0 = 0 ;388 * exit ;389 */390static void print_match_error(FILE *out,391 char *pattern_origin, char *text_origin,392 char *pattern_pos, char *text_pos)393{394 char *pattern = pattern_origin;395 char *text = text_origin;396 int middle = max_line_len(text) + 2;397 398 fprintf(out, "Can't match disassembly(left) with pattern(right):\n");399 while (*pattern || *text) {400 int column = 0;401 int mark1 = -1;402 int mark2 = -1;403 404 /* Print one line from text */405 while (*text && *text != '\n') {406 if (text == text_pos)407 mark1 = column;408 fputc(*text, out);409 ++text;410 ++column;411 }412 if (text == text_pos)413 mark1 = column;414 415 /* Pad to the middle */416 while (column < middle) {417 fputc(' ', out);418 ++column;419 }420 fputs("; ", out);421 column += 3;422 423 /* Print one line from pattern, pattern lines are terminated by ';' */424 while (*pattern && *pattern != ';') {425 if (pattern == pattern_pos)426 mark2 = column;427 fputc(*pattern, out);428 ++pattern;429 ++column;430 }431 if (pattern == pattern_pos)432 mark2 = column;433 434 fputc('\n', out);435 if (*pattern)436 ++pattern;437 if (*text)438 ++text;439 440 /* If pattern and text diverge at this line, print an441 * additional line with '^' marks, highlighting442 * positions where match fails.443 */444 if (mark1 > 0 || mark2 > 0) {445 for (column = 0; column <= max(mark1, mark2); ++column) {446 if (column == mark1 || column == mark2)447 fputc('^', out);448 else449 fputc(' ', out);450 }451 fputc('\n', out);452 }453 }454}455 456/* Test if `text` matches `pattern`. Pattern consists of the following elements:457 *458 * - Field offset references:459 *460 * <type>::<field>461 *462 * When such reference is encountered BTF is used to compute numerical463 * value for the offset of <field> in <type>. The `text` is expected to464 * contain matching numerical value.465 *466 * - Field groups:467 *468 * $(<type>::<field> [+ <type>::<field>]*)469 *470 * Allows to specify an offset that is a sum of multiple field offsets.471 * The `text` is expected to contain matching numerical value.472 *473 * - Variable references, e.g. `$src`, `$dst`, `$ctx`.474 * These are substitutions specified in `reg_map` array.475 * If a substring of pattern is equal to `reg_map[i][0]` the `text` is476 * expected to contain `reg_map[i][1]` in the matching position.477 *478 * - Whitespace is ignored, ';' counts as whitespace for `pattern`.479 *480 * - Any other characters, `pattern` and `text` should match one-to-one.481 *482 * Example of a pattern:483 *484 * __________ fields group ________________485 * ' '486 * *(u16 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::tc_classid)) = $src;487 * ^^^^ '______________________'488 * variable reference field offset reference489 */490static bool match_pattern(struct btf *btf, char *pattern, char *text, char *reg_map[][2])491{492 char *pattern_origin = pattern;493 char *text_origin = text;494 regmatch_t matches[3];495 496_continue:497 while (*pattern) {498 if (!*text)499 goto err;500 501 /* Skip whitespace */502 if (isspace(*pattern) || *pattern == ';') {503 if (!isspace(*text) && text != text_origin && isalnum(text[-1]))504 goto err;505 pattern = skip_space_and_semi(pattern);506 text = skip_space(text);507 continue;508 }509 510 /* Check for variable references */511 for (int i = 0; reg_map[i][0]; ++i) {512 char *pattern_next, *text_next;513 514 pattern_next = match_str(pattern, reg_map[i][0]);515 if (!pattern_next)516 continue;517 518 text_next = match_str(text, reg_map[i][1]);519 if (!text_next)520 goto err;521 522 pattern = pattern_next;523 text = text_next;524 goto _continue;525 }526 527 /* Match field group:528 * $(sk_buff::cb + qdisc_skb_cb::tc_classid)529 */530 if (strncmp(pattern, "$(", 2) == 0) {531 char *group_start = pattern, *text_next;532 int acc_offset = 0;533 534 pattern += 2;535 536 for (;;) {537 int field_offset;538 539 pattern = skip_space(pattern);540 if (!*pattern) {541 PRINT_FAIL("Unexpected end of pattern\n");542 goto err;543 }544 545 if (*pattern == ')') {546 ++pattern;547 break;548 }549 550 if (*pattern == '+') {551 ++pattern;552 continue;553 }554 555 printf("pattern: %s\n", pattern);556 if (regexec(field_regex, pattern, 3, matches, 0) != 0) {557 PRINT_FAIL("Field reference expected\n");558 goto err;559 }560 561 field_offset = find_field_offset(btf, pattern, matches);562 if (field_offset < 0)563 goto err;564 565 pattern += matches[0].rm_eo;566 acc_offset += field_offset;567 }568 569 text_next = match_number(text, acc_offset);570 if (!text_next) {571 PRINT_FAIL("No match for group offset %.*s (%d)\n",572 (int)(pattern - group_start),573 group_start,574 acc_offset);575 goto err;576 }577 text = text_next;578 }579 580 /* Match field reference:581 * sk_buff::cb582 */583 if (regexec(field_regex, pattern, 3, matches, 0) == 0) {584 int field_offset;585 char *text_next;586 587 field_offset = find_field_offset(btf, pattern, matches);588 if (field_offset < 0)589 goto err;590 591 text_next = match_number(text, field_offset);592 if (!text_next) {593 PRINT_FAIL("No match for field offset %.*s (%d)\n",594 (int)matches[0].rm_eo, pattern, field_offset);595 goto err;596 }597 598 pattern += matches[0].rm_eo;599 text = text_next;600 continue;601 }602 603 /* If pattern points to identifier not followed by '::'604 * skip the identifier to avoid n^2 application of the605 * field reference rule.606 */607 if (regexec(ident_regex, pattern, 1, matches, 0) == 0) {608 if (strncmp(pattern, text, matches[0].rm_eo) != 0)609 goto err;610 611 pattern += matches[0].rm_eo;612 text += matches[0].rm_eo;613 continue;614 }615 616 /* Match literally */617 if (*pattern != *text)618 goto err;619 620 ++pattern;621 ++text;622 }623 624 return true;625 626err:627 test__fail();628 print_match_error(stdout, pattern_origin, text_origin, pattern, text);629 return false;630}631 632struct prog_info {633 char *prog_kind;634 enum bpf_prog_type prog_type;635 enum bpf_attach_type expected_attach_type;636 struct bpf_insn *prog;637 u32 prog_len;638};639 640static void match_program(struct btf *btf,641 struct prog_info *pinfo,642 char *pattern,643 char *reg_map[][2],644 bool skip_first_insn)645{646 struct bpf_insn *buf = NULL, *insn, *insn_end;647 int err = 0, prog_fd = 0;648 FILE *prog_out = NULL;649 char insn_buf[64];650 char *text = NULL;651 __u32 cnt = 0;652 653 text = calloc(MAX_PROG_TEXT_SZ, 1);654 if (!text) {655 PRINT_FAIL("Can't allocate %d bytes\n", MAX_PROG_TEXT_SZ);656 goto out;657 }658 659 // TODO: log level660 LIBBPF_OPTS(bpf_prog_load_opts, opts);661 opts.log_buf = text;662 opts.log_size = MAX_PROG_TEXT_SZ;663 opts.log_level = 1 | 2 | 4;664 opts.expected_attach_type = pinfo->expected_attach_type;665 666 prog_fd = bpf_prog_load(pinfo->prog_type, NULL, "GPL",667 pinfo->prog, pinfo->prog_len, &opts);668 if (prog_fd < 0) {669 PRINT_FAIL("Can't load program, errno %d (%s), verifier log:\n%s\n",670 errno, strerror(errno), text);671 goto out;672 }673 674 memset(text, 0, MAX_PROG_TEXT_SZ);675 676 err = get_xlated_program(prog_fd, &buf, &cnt);677 if (err) {678 PRINT_FAIL("Can't load back BPF program\n");679 goto out;680 }681 682 prog_out = fmemopen(text, MAX_PROG_TEXT_SZ - 1, "w");683 if (!prog_out) {684 PRINT_FAIL("Can't open memory stream\n");685 goto out;686 }687 insn_end = buf + cnt;688 insn = buf + (skip_first_insn ? 1 : 0);689 while (insn < insn_end) {690 insn = disasm_insn(insn, insn_buf, sizeof(insn_buf));691 fprintf(prog_out, "%s\n", insn_buf);692 }693 fclose(prog_out);694 695 ASSERT_TRUE(match_pattern(btf, pattern, text, reg_map),696 pinfo->prog_kind);697 698out:699 if (prog_fd)700 close(prog_fd);701 free(buf);702 free(text);703}704 705static void run_one_testcase(struct btf *btf, struct test_case *test)706{707 struct prog_info pinfo = {};708 int bpf_sz;709 710 if (!test__start_subtest(test->name))711 return;712 713 switch (test->field_sz) {714 case 8:715 bpf_sz = BPF_DW;716 break;717 case 4:718 bpf_sz = BPF_W;719 break;720 case 2:721 bpf_sz = BPF_H;722 break;723 case 1:724 bpf_sz = BPF_B;725 break;726 default:727 PRINT_FAIL("Unexpected field size: %d, want 8,4,2 or 1\n", test->field_sz);728 return;729 }730 731 pinfo.prog_type = test->prog_type;732 pinfo.expected_attach_type = test->expected_attach_type;733 734 if (test->read) {735 struct bpf_insn ldx_prog[] = {736 BPF_LDX_MEM(bpf_sz, BPF_REG_2, BPF_REG_1, test->field_offset),737 BPF_MOV64_IMM(BPF_REG_0, 0),738 BPF_EXIT_INSN(),739 };740 char *reg_map[][2] = {741 { "$ctx", "r1" },742 { "$dst", "r2" },743 {}744 };745 746 pinfo.prog_kind = "LDX";747 pinfo.prog = ldx_prog;748 pinfo.prog_len = ARRAY_SIZE(ldx_prog);749 match_program(btf, &pinfo, test->read, reg_map, false);750 }751 752 if (test->write || test->write_st || test->write_stx) {753 struct bpf_insn stx_prog[] = {754 BPF_MOV64_IMM(BPF_REG_2, 0),755 BPF_STX_MEM(bpf_sz, BPF_REG_1, BPF_REG_2, test->field_offset),756 BPF_MOV64_IMM(BPF_REG_0, 0),757 BPF_EXIT_INSN(),758 };759 char *stx_reg_map[][2] = {760 { "$ctx", "r1" },761 { "$src", "r2" },762 {}763 };764 struct bpf_insn st_prog[] = {765 BPF_ST_MEM(bpf_sz, BPF_REG_1, test->field_offset,766 test->st_value.use ? test->st_value.value : 42),767 BPF_MOV64_IMM(BPF_REG_0, 0),768 BPF_EXIT_INSN(),769 };770 char *st_reg_map[][2] = {771 { "$ctx", "r1" },772 { "$src", "42" },773 {}774 };775 776 if (test->write || test->write_stx) {777 char *pattern = test->write_stx ? test->write_stx : test->write;778 779 pinfo.prog_kind = "STX";780 pinfo.prog = stx_prog;781 pinfo.prog_len = ARRAY_SIZE(stx_prog);782 match_program(btf, &pinfo, pattern, stx_reg_map, true);783 }784 785 if (test->write || test->write_st) {786 char *pattern = test->write_st ? test->write_st : test->write;787 788 pinfo.prog_kind = "ST";789 pinfo.prog = st_prog;790 pinfo.prog_len = ARRAY_SIZE(st_prog);791 match_program(btf, &pinfo, pattern, st_reg_map, false);792 }793 }794 795 test__end_subtest();796}797 798void test_ctx_rewrite(void)799{800 struct btf *btf;801 int i;802 803 field_regex = compile_regex("^([[:alpha:]_][[:alnum:]_]+)::([[:alpha:]_][[:alnum:]_]+)");804 ident_regex = compile_regex("^[[:alpha:]_][[:alnum:]_]+");805 if (!field_regex || !ident_regex)806 return;807 808 btf = btf__load_vmlinux_btf();809 if (!btf) {810 PRINT_FAIL("Can't load vmlinux BTF, errno %d (%s)\n", errno, strerror(errno));811 goto out;812 }813 814 for (i = 0; i < ARRAY_SIZE(test_cases); ++i)815 run_one_testcase(btf, &test_cases[i]);816 817out:818 btf__free(btf);819 free_regex(field_regex);820 free_regex(ident_regex);821}822