1 // SPDX-License-Identifier: GPL-2.0
8 #include <test_progs.h>
12 #include "linux/filter.h"
15 #define MAX_PROG_TEXT_SZ (32 * 1024)
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.
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.
28 * The disassembly of each program is then compared with the pattern
29 * specified in the test case.
33 enum bpf_prog_type prog_type;
34 enum bpf_attach_type expected_attach_type;
37 /* Program generated for BPF_ST_MEM uses value 42 by default,
38 * this field allows to specify custom value.
44 /* Pattern for BPF_LDX_MEM(field_sz, dst, ctx, field_offset) */
46 /* Pattern for BPF_STX_MEM(field_sz, ctx, src, field_offset) and
47 * BPF_ST_MEM (field_sz, ctx, src, field_offset)
50 /* Pattern for BPF_ST_MEM(field_sz, ctx, src, field_offset),
51 * takes priority over `write`.
54 /* Pattern for BPF_STX_MEM (field_sz, ctx, src, field_offset),
55 * takes priority over `write`.
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))
66 static struct test_case test_cases[] = {
67 /* Sign extension on s390 changes the pattern */
68 #if defined(__x86_64__) || defined(__aarch64__)
70 N(SCHED_CLS, struct __sk_buff, tstamp),
71 .read = "r11 = *(u8 *)($ctx + sk_buff::__mono_tc_offset);"
73 "if w11 != 0x3 goto pc+2;"
76 "$dst = *(u64 *)($ctx + sk_buff::tstamp);",
77 .write = "r11 = *(u8 *)($ctx + sk_buff::__mono_tc_offset);"
78 "if w11 & 0x2 goto pc+1;"
81 "*(u8 *)($ctx + sk_buff::__mono_tc_offset) = r11;"
82 "*(u64 *)($ctx + sk_buff::tstamp) = $src;",
86 N(SCHED_CLS, struct __sk_buff, priority),
87 .read = "$dst = *(u32 *)($ctx + sk_buff::priority);",
88 .write = "*(u32 *)($ctx + sk_buff::priority) = $src;",
91 N(SCHED_CLS, struct __sk_buff, mark),
92 .read = "$dst = *(u32 *)($ctx + sk_buff::mark);",
93 .write = "*(u32 *)($ctx + sk_buff::mark) = $src;",
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;",
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;",
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;",
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;",
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;",
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;",
129 N(CGROUP_SOCK, struct bpf_sock, mark),
130 .read = "$dst = *(u32 *)($ctx + sock::sk_mark);",
131 .write = "*(u32 *)($ctx + sock::sk_mark) = $src;",
134 N(CGROUP_SOCK, struct bpf_sock, priority),
135 .read = "$dst = *(u32 *)($ctx + sock::sk_priority);",
136 .write = "*(u32 *)($ctx + sock::sk_priority) = $src;",
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;",
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);",
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);",
162 N(CGROUP_SOCKOPT, struct bpf_sockopt, sk),
163 .read = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::sk);",
164 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
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,
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,
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,
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,
197 N(CGROUP_SOCKOPT, struct bpf_sockopt, optval),
198 .read = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::optval);",
199 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
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,
210 static regex_t *ident_regex;
211 static regex_t *field_regex;
213 static char *skip_space(char *str)
215 while (*str && isspace(*str))
220 static char *skip_space_and_semi(char *str)
222 while (*str && (isspace(*str) || *str == ';'))
227 static char *match_str(char *str, char *prefix)
229 while (*str && *prefix && *str == *prefix) {
238 static char *match_number(char *str, int num)
241 int snum = strtol(str, &next, 10);
243 if (next - str == 0 || num != snum)
249 static int find_field_offset_aux(struct btf *btf, int btf_id, char *field_name, int off)
251 const struct btf_type *type = btf__type_by_id(btf, btf_id);
252 const struct btf_member *m;
257 PRINT_FAIL("Can't find btf_type for id %d\n", btf_id);
261 if (!btf_is_struct(type) && !btf_is_union(type)) {
262 PRINT_FAIL("BTF id %d is not struct or union\n", btf_id);
266 m = btf_members(type);
267 mnum = btf_vlen(type);
269 for (i = 0; i < mnum; ++i, ++m) {
270 const char *mname = btf__name_by_offset(btf, m->name_off);
272 if (strcmp(mname, "") == 0) {
273 int msize = find_field_offset_aux(btf, m->type, field_name,
279 if (strcmp(mname, field_name))
282 return (off + m->offset) / 8;
288 static int find_field_offset(struct btf *btf, char *pattern, regmatch_t *matches)
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;
298 if (type_sz >= sizeof(type_str)) {
299 PRINT_FAIL("Malformed pattern: type ident is too long: %d\n", type_sz);
303 if (field_sz >= sizeof(field_str)) {
304 PRINT_FAIL("Malformed pattern: field ident is too long: %d\n", field_sz);
308 strncpy(type_str, type, type_sz);
309 strncpy(field_str, field, field_sz);
310 btf_id = btf__find_by_name(btf, type_str);
312 PRINT_FAIL("No BTF info for type %s\n", type_str);
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);
325 static regex_t *compile_regex(char *pat)
330 re = malloc(sizeof(regex_t));
332 PRINT_FAIL("Can't alloc regex\n");
336 err = regcomp(re, pat, REG_EXTENDED);
340 regerror(err, re, errbuf, sizeof(errbuf));
341 PRINT_FAIL("Can't compile regex: %s\n", errbuf);
349 static void free_regex(regex_t *re)
358 static u32 max_line_len(char *str)
364 next = strchr(str, '\n');
366 max_line = max_t(u32, max_line, (next - str));
369 max_line = max_t(u32, max_line, strlen(str));
373 return min(max_line, 60u);
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:
381 * Can't match disassembly(left) with pattern(right):
382 * r2 = *(u64 *)(r1 +0) ; $dst = *(u64 *)($ctx + bpf_sockopt_kern::sk1)
387 static void print_match_error(FILE *out,
388 char *pattern_origin, char *text_origin,
389 char *pattern_pos, char *text_pos)
391 char *pattern = pattern_origin;
392 char *text = text_origin;
393 int middle = max_line_len(text) + 2;
395 fprintf(out, "Can't match disassembly(left) with pattern(right):\n");
396 while (*pattern || *text) {
401 /* Print one line from text */
402 while (*text && *text != '\n') {
403 if (text == text_pos)
409 if (text == text_pos)
412 /* Pad to the middle */
413 while (column < middle) {
420 /* Print one line from pattern, pattern lines are terminated by ';' */
421 while (*pattern && *pattern != ';') {
422 if (pattern == pattern_pos)
424 fputc(*pattern, out);
428 if (pattern == pattern_pos)
437 /* If pattern and text diverge at this line, print an
438 * additional line with '^' marks, highlighting
439 * positions where match fails.
441 if (mark1 > 0 || mark2 > 0) {
442 for (column = 0; column <= max(mark1, mark2); ++column) {
443 if (column == mark1 || column == mark2)
453 /* Test if `text` matches `pattern`. Pattern consists of the following elements:
455 * - Field offset references:
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.
465 * $(<type>::<field> [+ <type>::<field>]*)
467 * Allows to specify an offset that is a sum of multiple field offsets.
468 * The `text` is expected to contain matching numerical value.
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.
475 * - Whitespace is ignored, ';' counts as whitespace for `pattern`.
477 * - Any other characters, `pattern` and `text` should match one-to-one.
479 * Example of a pattern:
481 * __________ fields group ________________
483 * *(u16 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::tc_classid)) = $src;
484 * ^^^^ '______________________'
485 * variable reference field offset reference
487 static bool match_pattern(struct btf *btf, char *pattern, char *text, char *reg_map[][2])
489 char *pattern_origin = pattern;
490 char *text_origin = text;
491 regmatch_t matches[3];
498 /* Skip whitespace */
499 if (isspace(*pattern) || *pattern == ';') {
500 if (!isspace(*text) && text != text_origin && isalnum(text[-1]))
502 pattern = skip_space_and_semi(pattern);
503 text = skip_space(text);
507 /* Check for variable references */
508 for (int i = 0; reg_map[i][0]; ++i) {
509 char *pattern_next, *text_next;
511 pattern_next = match_str(pattern, reg_map[i][0]);
515 text_next = match_str(text, reg_map[i][1]);
519 pattern = pattern_next;
524 /* Match field group:
525 * $(sk_buff::cb + qdisc_skb_cb::tc_classid)
527 if (strncmp(pattern, "$(", 2) == 0) {
528 char *group_start = pattern, *text_next;
536 pattern = skip_space(pattern);
538 PRINT_FAIL("Unexpected end of pattern\n");
542 if (*pattern == ')') {
547 if (*pattern == '+') {
552 printf("pattern: %s\n", pattern);
553 if (regexec(field_regex, pattern, 3, matches, 0) != 0) {
554 PRINT_FAIL("Field reference expected\n");
558 field_offset = find_field_offset(btf, pattern, matches);
559 if (field_offset < 0)
562 pattern += matches[0].rm_eo;
563 acc_offset += field_offset;
566 text_next = match_number(text, acc_offset);
568 PRINT_FAIL("No match for group offset %.*s (%d)\n",
569 (int)(pattern - group_start),
577 /* Match field reference:
580 if (regexec(field_regex, pattern, 3, matches, 0) == 0) {
584 field_offset = find_field_offset(btf, pattern, matches);
585 if (field_offset < 0)
588 text_next = match_number(text, field_offset);
590 PRINT_FAIL("No match for field offset %.*s (%d)\n",
591 (int)matches[0].rm_eo, pattern, field_offset);
595 pattern += matches[0].rm_eo;
600 /* If pattern points to identifier not followed by '::'
601 * skip the identifier to avoid n^2 application of the
602 * field reference rule.
604 if (regexec(ident_regex, pattern, 1, matches, 0) == 0) {
605 if (strncmp(pattern, text, matches[0].rm_eo) != 0)
608 pattern += matches[0].rm_eo;
609 text += matches[0].rm_eo;
613 /* Match literally */
614 if (*pattern != *text)
625 print_match_error(stdout, pattern_origin, text_origin, pattern, text);
629 static void print_insn(void *private_data, const char *fmt, ...)
634 vfprintf((FILE *)private_data, fmt, args);
638 /* Disassemble instructions to a stream */
639 static void print_xlated(FILE *out, struct bpf_insn *insn, __u32 len)
641 const struct bpf_insn_cbs cbs = {
642 .cb_print = print_insn,
647 bool double_insn = false;
650 for (i = 0; i < len; i++) {
656 double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
657 print_bpf_insn(&cbs, insn + i, true);
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`.
665 static void remove_insn_prefix(char *str, int size)
667 const int prefix_size = 5;
669 int write_pos = 0, read_pos = prefix_size;
670 int len = strlen(str);
673 size = min(size, len);
675 while (read_pos < size) {
679 str[write_pos++] = c;
681 read_pos += prefix_size;
688 enum bpf_prog_type prog_type;
689 enum bpf_attach_type expected_attach_type;
690 struct bpf_insn *prog;
694 static void match_program(struct btf *btf,
695 struct prog_info *pinfo,
698 bool skip_first_insn)
700 struct bpf_insn *buf = NULL;
701 int err = 0, prog_fd = 0;
702 FILE *prog_out = NULL;
706 text = calloc(MAX_PROG_TEXT_SZ, 1);
708 PRINT_FAIL("Can't allocate %d bytes\n", MAX_PROG_TEXT_SZ);
713 LIBBPF_OPTS(bpf_prog_load_opts, opts);
715 opts.log_size = MAX_PROG_TEXT_SZ;
716 opts.log_level = 1 | 2 | 4;
717 opts.expected_attach_type = pinfo->expected_attach_type;
719 prog_fd = bpf_prog_load(pinfo->prog_type, NULL, "GPL",
720 pinfo->prog, pinfo->prog_len, &opts);
722 PRINT_FAIL("Can't load program, errno %d (%s), verifier log:\n%s\n",
723 errno, strerror(errno), text);
727 memset(text, 0, MAX_PROG_TEXT_SZ);
729 err = get_xlated_program(prog_fd, &buf, &cnt);
731 PRINT_FAIL("Can't load back BPF program\n");
735 prog_out = fmemopen(text, MAX_PROG_TEXT_SZ - 1, "w");
737 PRINT_FAIL("Can't open memory stream\n");
741 print_xlated(prog_out, buf + 1, cnt - 1);
743 print_xlated(prog_out, buf, cnt);
745 remove_insn_prefix(text, MAX_PROG_TEXT_SZ);
747 ASSERT_TRUE(match_pattern(btf, pattern, text, reg_map),
757 static void run_one_testcase(struct btf *btf, struct test_case *test)
759 struct prog_info pinfo = {};
762 if (!test__start_subtest(test->name))
765 switch (test->field_sz) {
779 PRINT_FAIL("Unexpected field size: %d, want 8,4,2 or 1\n", test->field_sz);
783 pinfo.prog_type = test->prog_type;
784 pinfo.expected_attach_type = test->expected_attach_type;
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),
792 char *reg_map[][2] = {
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);
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),
811 char *stx_reg_map[][2] = {
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),
822 char *st_reg_map[][2] = {
828 if (test->write || test->write_stx) {
829 char *pattern = test->write_stx ? test->write_stx : test->write;
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);
837 if (test->write || test->write_st) {
838 char *pattern = test->write_st ? test->write_st : test->write;
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);
850 void test_ctx_rewrite(void)
855 field_regex = compile_regex("^([[:alpha:]_][[:alnum:]_]+)::([[:alpha:]_][[:alnum:]_]+)");
856 ident_regex = compile_regex("^[[:alpha:]_][[:alnum:]_]+");
857 if (!field_regex || !ident_regex)
860 btf = btf__load_vmlinux_btf();
862 PRINT_FAIL("Can't load vmlinux BTF, errno %d (%s)\n", errno, strerror(errno));
866 for (i = 0; i < ARRAY_SIZE(test_cases); ++i)
867 run_one_testcase(btf, &test_cases[i]);
871 free_regex(field_regex);
872 free_regex(ident_regex);