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
skip_space(char * str)216 static char *skip_space(char *str)
217 {
218 while (*str && isspace(*str))
219 ++str;
220 return str;
221 }
222
skip_space_and_semi(char * str)223 static char *skip_space_and_semi(char *str)
224 {
225 while (*str && (isspace(*str) || *str == ';'))
226 ++str;
227 return str;
228 }
229
match_str(char * str,char * prefix)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
match_number(char * str,int num)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
find_field_offset_aux(struct btf * btf,int btf_id,char * field_name,int off)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
find_field_offset(struct btf * btf,char * pattern,regmatch_t * matches)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
compile_regex(char * pat)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
free_regex(regex_t * re)354 static void free_regex(regex_t *re)
355 {
356 if (!re)
357 return;
358
359 regfree(re);
360 free(re);
361 }
362
max_line_len(char * str)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 */
print_match_error(FILE * out,char * pattern_origin,char * text_origin,char * pattern_pos,char * text_pos)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 */
match_pattern(struct btf * btf,char * pattern,char * text,char * reg_map[][2])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
match_program(struct btf * btf,struct prog_info * pinfo,char * pattern,char * reg_map[][2],bool skip_first_insn)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
run_one_testcase(struct btf * btf,struct test_case * test)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
test_ctx_rewrite(void)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