]> Git Repo - linux.git/blob - tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c
Merge patch series "riscv: Extension parsing fixes"
[linux.git] / tools / testing / selftests / bpf / prog_tests / ctx_rewrite.c
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 }
This page took 0.087178 seconds and 4 git commands to generate.