]> Git Repo - J-linux.git/blob - tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-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 "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
216 static char *skip_space(char *str)
217 {
218         while (*str && isspace(*str))
219                 ++str;
220         return str;
221 }
222
223 static char *skip_space_and_semi(char *str)
224 {
225         while (*str && (isspace(*str) || *str == ';'))
226                 ++str;
227         return str;
228 }
229
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
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
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
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         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
328 static 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
352 static void free_regex(regex_t *re)
353 {
354         if (!re)
355                 return;
356
357         regfree(re);
358         free(re);
359 }
360
361 static 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 within
381  * 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  */
390 static 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 an
441                  * additional line with '^' marks, highlighting
442                  * 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                                 else
449                                         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 numerical
463  *   value for the offset of <field> in <type>. The `text` is expected to
464  *   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` is
476  *   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 reference
489  */
490 static 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::cb
582                  */
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 the
605                  * 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
626 err:
627         test__fail();
628         print_match_error(stdout, pattern_origin, text_origin, pattern, text);
629         return false;
630 }
631
632 struct 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
640 static 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 level
660         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
698 out:
699         if (prog_fd)
700                 close(prog_fd);
701         free(buf);
702         free(text);
703 }
704
705 static 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
798 void 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
817 out:
818         btf__free(btf);
819         free_regex(field_regex);
820         free_regex(ident_regex);
821 }
This page took 0.076062 seconds and 4 git commands to generate.