xref: /linux/tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c (revision c771600c6af14749609b49565ffb4cac2959710d)
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  	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  
compile_regex(char * pat)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  
free_regex(regex_t * re)352  static void free_regex(regex_t *re)
353  {
354  	if (!re)
355  		return;
356  
357  	regfree(re);
358  	free(re);
359  }
360  
max_line_len(char * str)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   */
print_match_error(FILE * out,char * pattern_origin,char * text_origin,char * pattern_pos,char * text_pos)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   */
match_pattern(struct btf * btf,char * pattern,char * text,char * reg_map[][2])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  
match_program(struct btf * btf,struct prog_info * pinfo,char * pattern,char * reg_map[][2],bool skip_first_insn)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  
run_one_testcase(struct btf * btf,struct test_case * test)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  
test_ctx_rewrite(void)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  }
822