xref: /linux/tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c (revision d7f39aee79f04eeaa42085728423501b33ac5be5)
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 			 "if w11 & 0x4 goto pc+1;"
73 			 "goto pc+4;"
74 			 "if w11 & 0x3 goto pc+1;"
75 			 "goto pc+2;"
76 			 "$dst = 0;"
77 			 "goto pc+1;"
78 			 "$dst = *(u64 *)($ctx + sk_buff::tstamp);",
79 		.write = "r11 = *(u8 *)($ctx + sk_buff::__mono_tc_offset);"
80 			 "if w11 & 0x4 goto pc+1;"
81 			 "goto pc+2;"
82 			 "w11 &= -4;"
83 			 "*(u8 *)($ctx + sk_buff::__mono_tc_offset) = r11;"
84 			 "*(u64 *)($ctx + sk_buff::tstamp) = $src;",
85 	},
86 #endif
87 	{
88 		N(SCHED_CLS, struct __sk_buff, priority),
89 		.read  = "$dst = *(u32 *)($ctx + sk_buff::priority);",
90 		.write = "*(u32 *)($ctx + sk_buff::priority) = $src;",
91 	},
92 	{
93 		N(SCHED_CLS, struct __sk_buff, mark),
94 		.read  = "$dst = *(u32 *)($ctx + sk_buff::mark);",
95 		.write = "*(u32 *)($ctx + sk_buff::mark) = $src;",
96 	},
97 	{
98 		N(SCHED_CLS, struct __sk_buff, cb[0]),
99 		.read  = "$dst = *(u32 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::data));",
100 		.write = "*(u32 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::data)) = $src;",
101 	},
102 	{
103 		N(SCHED_CLS, struct __sk_buff, tc_classid),
104 		.read  = "$dst = *(u16 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::tc_classid));",
105 		.write = "*(u16 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::tc_classid)) = $src;",
106 	},
107 	{
108 		N(SCHED_CLS, struct __sk_buff, tc_index),
109 		.read  = "$dst = *(u16 *)($ctx + sk_buff::tc_index);",
110 		.write = "*(u16 *)($ctx + sk_buff::tc_index) = $src;",
111 	},
112 	{
113 		N(SCHED_CLS, struct __sk_buff, queue_mapping),
114 		.read      = "$dst = *(u16 *)($ctx + sk_buff::queue_mapping);",
115 		.write_stx = "if $src >= 0xffff goto pc+1;"
116 			     "*(u16 *)($ctx + sk_buff::queue_mapping) = $src;",
117 		.write_st  = "*(u16 *)($ctx + sk_buff::queue_mapping) = $src;",
118 	},
119 	{
120 		/* This is a corner case in filter.c:bpf_convert_ctx_access() */
121 		N(SCHED_CLS, struct __sk_buff, queue_mapping, ".ushrt_max"),
122 		.st_value = { true, USHRT_MAX },
123 		.write_st = "goto pc+0;",
124 	},
125 	{
126 		N(CGROUP_SOCK, struct bpf_sock, bound_dev_if),
127 		.read  = "$dst = *(u32 *)($ctx + sock_common::skc_bound_dev_if);",
128 		.write = "*(u32 *)($ctx + sock_common::skc_bound_dev_if) = $src;",
129 	},
130 	{
131 		N(CGROUP_SOCK, struct bpf_sock, mark),
132 		.read  = "$dst = *(u32 *)($ctx + sock::sk_mark);",
133 		.write = "*(u32 *)($ctx + sock::sk_mark) = $src;",
134 	},
135 	{
136 		N(CGROUP_SOCK, struct bpf_sock, priority),
137 		.read  = "$dst = *(u32 *)($ctx + sock::sk_priority);",
138 		.write = "*(u32 *)($ctx + sock::sk_priority) = $src;",
139 	},
140 	{
141 		N(SOCK_OPS, struct bpf_sock_ops, replylong[0]),
142 		.read  = "$dst = *(u32 *)($ctx + bpf_sock_ops_kern::replylong);",
143 		.write = "*(u32 *)($ctx + bpf_sock_ops_kern::replylong) = $src;",
144 	},
145 	{
146 		N(CGROUP_SYSCTL, struct bpf_sysctl, file_pos),
147 #if __BYTE_ORDER == __LITTLE_ENDIAN
148 		.read  = "$dst = *(u64 *)($ctx + bpf_sysctl_kern::ppos);"
149 			 "$dst = *(u32 *)($dst +0);",
150 		.write = "*(u64 *)($ctx + bpf_sysctl_kern::tmp_reg) = r9;"
151 			 "r9 = *(u64 *)($ctx + bpf_sysctl_kern::ppos);"
152 			 "*(u32 *)(r9 +0) = $src;"
153 			 "r9 = *(u64 *)($ctx + bpf_sysctl_kern::tmp_reg);",
154 #else
155 		.read  = "$dst = *(u64 *)($ctx + bpf_sysctl_kern::ppos);"
156 			 "$dst = *(u32 *)($dst +4);",
157 		.write = "*(u64 *)($ctx + bpf_sysctl_kern::tmp_reg) = r9;"
158 			 "r9 = *(u64 *)($ctx + bpf_sysctl_kern::ppos);"
159 			 "*(u32 *)(r9 +4) = $src;"
160 			 "r9 = *(u64 *)($ctx + bpf_sysctl_kern::tmp_reg);",
161 #endif
162 	},
163 	{
164 		N(CGROUP_SOCKOPT, struct bpf_sockopt, sk),
165 		.read  = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::sk);",
166 		.expected_attach_type = BPF_CGROUP_GETSOCKOPT,
167 	},
168 	{
169 		N(CGROUP_SOCKOPT, struct bpf_sockopt, level),
170 		.read  = "$dst = *(u32 *)($ctx + bpf_sockopt_kern::level);",
171 		.write = "*(u32 *)($ctx + bpf_sockopt_kern::level) = $src;",
172 		.expected_attach_type = BPF_CGROUP_SETSOCKOPT,
173 	},
174 	{
175 		N(CGROUP_SOCKOPT, struct bpf_sockopt, optname),
176 		.read  = "$dst = *(u32 *)($ctx + bpf_sockopt_kern::optname);",
177 		.write = "*(u32 *)($ctx + bpf_sockopt_kern::optname) = $src;",
178 		.expected_attach_type = BPF_CGROUP_SETSOCKOPT,
179 	},
180 	{
181 		N(CGROUP_SOCKOPT, struct bpf_sockopt, optlen),
182 		.read  = "$dst = *(u32 *)($ctx + bpf_sockopt_kern::optlen);",
183 		.write = "*(u32 *)($ctx + bpf_sockopt_kern::optlen) = $src;",
184 		.expected_attach_type = BPF_CGROUP_SETSOCKOPT,
185 	},
186 	{
187 		N(CGROUP_SOCKOPT, struct bpf_sockopt, retval),
188 		.read  = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::current_task);"
189 			 "$dst = *(u64 *)($dst + task_struct::bpf_ctx);"
190 			 "$dst = *(u32 *)($dst + bpf_cg_run_ctx::retval);",
191 		.write = "*(u64 *)($ctx + bpf_sockopt_kern::tmp_reg) = r9;"
192 			 "r9 = *(u64 *)($ctx + bpf_sockopt_kern::current_task);"
193 			 "r9 = *(u64 *)(r9 + task_struct::bpf_ctx);"
194 			 "*(u32 *)(r9 + bpf_cg_run_ctx::retval) = $src;"
195 			 "r9 = *(u64 *)($ctx + bpf_sockopt_kern::tmp_reg);",
196 		.expected_attach_type = BPF_CGROUP_GETSOCKOPT,
197 	},
198 	{
199 		N(CGROUP_SOCKOPT, struct bpf_sockopt, optval),
200 		.read  = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::optval);",
201 		.expected_attach_type = BPF_CGROUP_GETSOCKOPT,
202 	},
203 	{
204 		N(CGROUP_SOCKOPT, struct bpf_sockopt, optval_end),
205 		.read  = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::optval_end);",
206 		.expected_attach_type = BPF_CGROUP_GETSOCKOPT,
207 	},
208 };
209 
210 #undef N
211 
212 static regex_t *ident_regex;
213 static regex_t *field_regex;
214 
215 static char *skip_space(char *str)
216 {
217 	while (*str && isspace(*str))
218 		++str;
219 	return str;
220 }
221 
222 static char *skip_space_and_semi(char *str)
223 {
224 	while (*str && (isspace(*str) || *str == ';'))
225 		++str;
226 	return str;
227 }
228 
229 static char *match_str(char *str, char *prefix)
230 {
231 	while (*str && *prefix && *str == *prefix) {
232 		++str;
233 		++prefix;
234 	}
235 	if (*prefix)
236 		return NULL;
237 	return str;
238 }
239 
240 static char *match_number(char *str, int num)
241 {
242 	char *next;
243 	int snum = strtol(str, &next, 10);
244 
245 	if (next - str == 0 || num != snum)
246 		return NULL;
247 
248 	return next;
249 }
250 
251 static int find_field_offset_aux(struct btf *btf, int btf_id, char *field_name, int off)
252 {
253 	const struct btf_type *type = btf__type_by_id(btf, btf_id);
254 	const struct btf_member *m;
255 	__u16 mnum;
256 	int i;
257 
258 	if (!type) {
259 		PRINT_FAIL("Can't find btf_type for id %d\n", btf_id);
260 		return -1;
261 	}
262 
263 	if (!btf_is_struct(type) && !btf_is_union(type)) {
264 		PRINT_FAIL("BTF id %d is not struct or union\n", btf_id);
265 		return -1;
266 	}
267 
268 	m = btf_members(type);
269 	mnum = btf_vlen(type);
270 
271 	for (i = 0; i < mnum; ++i, ++m) {
272 		const char *mname = btf__name_by_offset(btf, m->name_off);
273 
274 		if (strcmp(mname, "") == 0) {
275 			int msize = find_field_offset_aux(btf, m->type, field_name,
276 							  off + m->offset);
277 			if (msize >= 0)
278 				return msize;
279 		}
280 
281 		if (strcmp(mname, field_name))
282 			continue;
283 
284 		return (off + m->offset) / 8;
285 	}
286 
287 	return -1;
288 }
289 
290 static int find_field_offset(struct btf *btf, char *pattern, regmatch_t *matches)
291 {
292 	int type_sz  = matches[1].rm_eo - matches[1].rm_so;
293 	int field_sz = matches[2].rm_eo - matches[2].rm_so;
294 	char *type   = pattern + matches[1].rm_so;
295 	char *field  = pattern + matches[2].rm_so;
296 	char field_str[128] = {};
297 	char type_str[128] = {};
298 	int btf_id, field_offset;
299 
300 	if (type_sz >= sizeof(type_str)) {
301 		PRINT_FAIL("Malformed pattern: type ident is too long: %d\n", type_sz);
302 		return -1;
303 	}
304 
305 	if (field_sz >= sizeof(field_str)) {
306 		PRINT_FAIL("Malformed pattern: field ident is too long: %d\n", field_sz);
307 		return -1;
308 	}
309 
310 	strncpy(type_str, type, type_sz);
311 	strncpy(field_str, field, field_sz);
312 	btf_id = btf__find_by_name(btf, type_str);
313 	if (btf_id < 0) {
314 		PRINT_FAIL("No BTF info for type %s\n", type_str);
315 		return -1;
316 	}
317 
318 	field_offset = find_field_offset_aux(btf, btf_id, field_str, 0);
319 	if (field_offset < 0) {
320 		PRINT_FAIL("No BTF info for field %s::%s\n", type_str, field_str);
321 		return -1;
322 	}
323 
324 	return field_offset;
325 }
326 
327 static regex_t *compile_regex(char *pat)
328 {
329 	regex_t *re;
330 	int err;
331 
332 	re = malloc(sizeof(regex_t));
333 	if (!re) {
334 		PRINT_FAIL("Can't alloc regex\n");
335 		return NULL;
336 	}
337 
338 	err = regcomp(re, pat, REG_EXTENDED);
339 	if (err) {
340 		char errbuf[512];
341 
342 		regerror(err, re, errbuf, sizeof(errbuf));
343 		PRINT_FAIL("Can't compile regex: %s\n", errbuf);
344 		free(re);
345 		return NULL;
346 	}
347 
348 	return re;
349 }
350 
351 static void free_regex(regex_t *re)
352 {
353 	if (!re)
354 		return;
355 
356 	regfree(re);
357 	free(re);
358 }
359 
360 static u32 max_line_len(char *str)
361 {
362 	u32 max_line = 0;
363 	char *next = str;
364 
365 	while (next) {
366 		next = strchr(str, '\n');
367 		if (next) {
368 			max_line = max_t(u32, max_line, (next - str));
369 			str = next + 1;
370 		} else {
371 			max_line = max_t(u32, max_line, strlen(str));
372 		}
373 	}
374 
375 	return min(max_line, 60u);
376 }
377 
378 /* Print strings `pattern_origin` and `text_origin` side by side,
379  * assume `pattern_pos` and `text_pos` designate location within
380  * corresponding origin string where match diverges.
381  * The output should look like:
382  *
383  *   Can't match disassembly(left) with pattern(right):
384  *   r2 = *(u64 *)(r1 +0)  ;  $dst = *(u64 *)($ctx + bpf_sockopt_kern::sk1)
385  *                     ^                             ^
386  *   r0 = 0                ;
387  *   exit                  ;
388  */
389 static void print_match_error(FILE *out,
390 			      char *pattern_origin, char *text_origin,
391 			      char *pattern_pos, char *text_pos)
392 {
393 	char *pattern = pattern_origin;
394 	char *text = text_origin;
395 	int middle = max_line_len(text) + 2;
396 
397 	fprintf(out, "Can't match disassembly(left) with pattern(right):\n");
398 	while (*pattern || *text) {
399 		int column = 0;
400 		int mark1 = -1;
401 		int mark2 = -1;
402 
403 		/* Print one line from text */
404 		while (*text && *text != '\n') {
405 			if (text == text_pos)
406 				mark1 = column;
407 			fputc(*text, out);
408 			++text;
409 			++column;
410 		}
411 		if (text == text_pos)
412 			mark1 = column;
413 
414 		/* Pad to the middle */
415 		while (column < middle) {
416 			fputc(' ', out);
417 			++column;
418 		}
419 		fputs(";  ", out);
420 		column += 3;
421 
422 		/* Print one line from pattern, pattern lines are terminated by ';' */
423 		while (*pattern && *pattern != ';') {
424 			if (pattern == pattern_pos)
425 				mark2 = column;
426 			fputc(*pattern, out);
427 			++pattern;
428 			++column;
429 		}
430 		if (pattern == pattern_pos)
431 			mark2 = column;
432 
433 		fputc('\n', out);
434 		if (*pattern)
435 			++pattern;
436 		if (*text)
437 			++text;
438 
439 		/* If pattern and text diverge at this line, print an
440 		 * additional line with '^' marks, highlighting
441 		 * positions where match fails.
442 		 */
443 		if (mark1 > 0 || mark2 > 0) {
444 			for (column = 0; column <= max(mark1, mark2); ++column) {
445 				if (column == mark1 || column == mark2)
446 					fputc('^', out);
447 				else
448 					fputc(' ', out);
449 			}
450 			fputc('\n', out);
451 		}
452 	}
453 }
454 
455 /* Test if `text` matches `pattern`. Pattern consists of the following elements:
456  *
457  * - Field offset references:
458  *
459  *     <type>::<field>
460  *
461  *   When such reference is encountered BTF is used to compute numerical
462  *   value for the offset of <field> in <type>. The `text` is expected to
463  *   contain matching numerical value.
464  *
465  * - Field groups:
466  *
467  *     $(<type>::<field> [+ <type>::<field>]*)
468  *
469  *   Allows to specify an offset that is a sum of multiple field offsets.
470  *   The `text` is expected to contain matching numerical value.
471  *
472  * - Variable references, e.g. `$src`, `$dst`, `$ctx`.
473  *   These are substitutions specified in `reg_map` array.
474  *   If a substring of pattern is equal to `reg_map[i][0]` the `text` is
475  *   expected to contain `reg_map[i][1]` in the matching position.
476  *
477  * - Whitespace is ignored, ';' counts as whitespace for `pattern`.
478  *
479  * - Any other characters, `pattern` and `text` should match one-to-one.
480  *
481  * Example of a pattern:
482  *
483  *                    __________ fields group ________________
484  *                   '                                        '
485  *   *(u16 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::tc_classid)) = $src;
486  *            ^^^^                   '______________________'
487  *     variable reference             field offset reference
488  */
489 static bool match_pattern(struct btf *btf, char *pattern, char *text, char *reg_map[][2])
490 {
491 	char *pattern_origin = pattern;
492 	char *text_origin = text;
493 	regmatch_t matches[3];
494 
495 _continue:
496 	while (*pattern) {
497 		if (!*text)
498 			goto err;
499 
500 		/* Skip whitespace */
501 		if (isspace(*pattern) || *pattern == ';') {
502 			if (!isspace(*text) && text != text_origin && isalnum(text[-1]))
503 				goto err;
504 			pattern = skip_space_and_semi(pattern);
505 			text = skip_space(text);
506 			continue;
507 		}
508 
509 		/* Check for variable references */
510 		for (int i = 0; reg_map[i][0]; ++i) {
511 			char *pattern_next, *text_next;
512 
513 			pattern_next = match_str(pattern, reg_map[i][0]);
514 			if (!pattern_next)
515 				continue;
516 
517 			text_next = match_str(text, reg_map[i][1]);
518 			if (!text_next)
519 				goto err;
520 
521 			pattern = pattern_next;
522 			text = text_next;
523 			goto _continue;
524 		}
525 
526 		/* Match field group:
527 		 *   $(sk_buff::cb + qdisc_skb_cb::tc_classid)
528 		 */
529 		if (strncmp(pattern, "$(", 2) == 0) {
530 			char *group_start = pattern, *text_next;
531 			int acc_offset = 0;
532 
533 			pattern += 2;
534 
535 			for (;;) {
536 				int field_offset;
537 
538 				pattern = skip_space(pattern);
539 				if (!*pattern) {
540 					PRINT_FAIL("Unexpected end of pattern\n");
541 					goto err;
542 				}
543 
544 				if (*pattern == ')') {
545 					++pattern;
546 					break;
547 				}
548 
549 				if (*pattern == '+') {
550 					++pattern;
551 					continue;
552 				}
553 
554 				printf("pattern: %s\n", pattern);
555 				if (regexec(field_regex, pattern, 3, matches, 0) != 0) {
556 					PRINT_FAIL("Field reference expected\n");
557 					goto err;
558 				}
559 
560 				field_offset = find_field_offset(btf, pattern, matches);
561 				if (field_offset < 0)
562 					goto err;
563 
564 				pattern += matches[0].rm_eo;
565 				acc_offset += field_offset;
566 			}
567 
568 			text_next = match_number(text, acc_offset);
569 			if (!text_next) {
570 				PRINT_FAIL("No match for group offset %.*s (%d)\n",
571 					   (int)(pattern - group_start),
572 					   group_start,
573 					   acc_offset);
574 				goto err;
575 			}
576 			text = text_next;
577 		}
578 
579 		/* Match field reference:
580 		 *   sk_buff::cb
581 		 */
582 		if (regexec(field_regex, pattern, 3, matches, 0) == 0) {
583 			int field_offset;
584 			char *text_next;
585 
586 			field_offset = find_field_offset(btf, pattern, matches);
587 			if (field_offset < 0)
588 				goto err;
589 
590 			text_next = match_number(text, field_offset);
591 			if (!text_next) {
592 				PRINT_FAIL("No match for field offset %.*s (%d)\n",
593 					   (int)matches[0].rm_eo, pattern, field_offset);
594 				goto err;
595 			}
596 
597 			pattern += matches[0].rm_eo;
598 			text = text_next;
599 			continue;
600 		}
601 
602 		/* If pattern points to identifier not followed by '::'
603 		 * skip the identifier to avoid n^2 application of the
604 		 * field reference rule.
605 		 */
606 		if (regexec(ident_regex, pattern, 1, matches, 0) == 0) {
607 			if (strncmp(pattern, text, matches[0].rm_eo) != 0)
608 				goto err;
609 
610 			pattern += matches[0].rm_eo;
611 			text += matches[0].rm_eo;
612 			continue;
613 		}
614 
615 		/* Match literally */
616 		if (*pattern != *text)
617 			goto err;
618 
619 		++pattern;
620 		++text;
621 	}
622 
623 	return true;
624 
625 err:
626 	test__fail();
627 	print_match_error(stdout, pattern_origin, text_origin, pattern, text);
628 	return false;
629 }
630 
631 static void print_insn(void *private_data, const char *fmt, ...)
632 {
633 	va_list args;
634 
635 	va_start(args, fmt);
636 	vfprintf((FILE *)private_data, fmt, args);
637 	va_end(args);
638 }
639 
640 /* Disassemble instructions to a stream */
641 static void print_xlated(FILE *out, struct bpf_insn *insn, __u32 len)
642 {
643 	const struct bpf_insn_cbs cbs = {
644 		.cb_print	= print_insn,
645 		.cb_call	= NULL,
646 		.cb_imm		= NULL,
647 		.private_data	= out,
648 	};
649 	bool double_insn = false;
650 	int i;
651 
652 	for (i = 0; i < len; i++) {
653 		if (double_insn) {
654 			double_insn = false;
655 			continue;
656 		}
657 
658 		double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
659 		print_bpf_insn(&cbs, insn + i, true);
660 	}
661 }
662 
663 /* We share code with kernel BPF disassembler, it adds '(FF) ' prefix
664  * for each instruction (FF stands for instruction `code` byte).
665  * This function removes the prefix inplace for each line in `str`.
666  */
667 static void remove_insn_prefix(char *str, int size)
668 {
669 	const int prefix_size = 5;
670 
671 	int write_pos = 0, read_pos = prefix_size;
672 	int len = strlen(str);
673 	char c;
674 
675 	size = min(size, len);
676 
677 	while (read_pos < size) {
678 		c = str[read_pos++];
679 		if (c == 0)
680 			break;
681 		str[write_pos++] = c;
682 		if (c == '\n')
683 			read_pos += prefix_size;
684 	}
685 	str[write_pos] = 0;
686 }
687 
688 struct prog_info {
689 	char *prog_kind;
690 	enum bpf_prog_type prog_type;
691 	enum bpf_attach_type expected_attach_type;
692 	struct bpf_insn *prog;
693 	u32 prog_len;
694 };
695 
696 static void match_program(struct btf *btf,
697 			  struct prog_info *pinfo,
698 			  char *pattern,
699 			  char *reg_map[][2],
700 			  bool skip_first_insn)
701 {
702 	struct bpf_insn *buf = NULL;
703 	int err = 0, prog_fd = 0;
704 	FILE *prog_out = NULL;
705 	char *text = NULL;
706 	__u32 cnt = 0;
707 
708 	text = calloc(MAX_PROG_TEXT_SZ, 1);
709 	if (!text) {
710 		PRINT_FAIL("Can't allocate %d bytes\n", MAX_PROG_TEXT_SZ);
711 		goto out;
712 	}
713 
714 	// TODO: log level
715 	LIBBPF_OPTS(bpf_prog_load_opts, opts);
716 	opts.log_buf = text;
717 	opts.log_size = MAX_PROG_TEXT_SZ;
718 	opts.log_level = 1 | 2 | 4;
719 	opts.expected_attach_type = pinfo->expected_attach_type;
720 
721 	prog_fd = bpf_prog_load(pinfo->prog_type, NULL, "GPL",
722 				pinfo->prog, pinfo->prog_len, &opts);
723 	if (prog_fd < 0) {
724 		PRINT_FAIL("Can't load program, errno %d (%s), verifier log:\n%s\n",
725 			   errno, strerror(errno), text);
726 		goto out;
727 	}
728 
729 	memset(text, 0, MAX_PROG_TEXT_SZ);
730 
731 	err = get_xlated_program(prog_fd, &buf, &cnt);
732 	if (err) {
733 		PRINT_FAIL("Can't load back BPF program\n");
734 		goto out;
735 	}
736 
737 	prog_out = fmemopen(text, MAX_PROG_TEXT_SZ - 1, "w");
738 	if (!prog_out) {
739 		PRINT_FAIL("Can't open memory stream\n");
740 		goto out;
741 	}
742 	if (skip_first_insn)
743 		print_xlated(prog_out, buf + 1, cnt - 1);
744 	else
745 		print_xlated(prog_out, buf, cnt);
746 	fclose(prog_out);
747 	remove_insn_prefix(text, MAX_PROG_TEXT_SZ);
748 
749 	ASSERT_TRUE(match_pattern(btf, pattern, text, reg_map),
750 		    pinfo->prog_kind);
751 
752 out:
753 	if (prog_fd)
754 		close(prog_fd);
755 	free(buf);
756 	free(text);
757 }
758 
759 static void run_one_testcase(struct btf *btf, struct test_case *test)
760 {
761 	struct prog_info pinfo = {};
762 	int bpf_sz;
763 
764 	if (!test__start_subtest(test->name))
765 		return;
766 
767 	switch (test->field_sz) {
768 	case 8:
769 		bpf_sz = BPF_DW;
770 		break;
771 	case 4:
772 		bpf_sz = BPF_W;
773 		break;
774 	case 2:
775 		bpf_sz = BPF_H;
776 		break;
777 	case 1:
778 		bpf_sz = BPF_B;
779 		break;
780 	default:
781 		PRINT_FAIL("Unexpected field size: %d, want 8,4,2 or 1\n", test->field_sz);
782 		return;
783 	}
784 
785 	pinfo.prog_type = test->prog_type;
786 	pinfo.expected_attach_type = test->expected_attach_type;
787 
788 	if (test->read) {
789 		struct bpf_insn ldx_prog[] = {
790 			BPF_LDX_MEM(bpf_sz, BPF_REG_2, BPF_REG_1, test->field_offset),
791 			BPF_MOV64_IMM(BPF_REG_0, 0),
792 			BPF_EXIT_INSN(),
793 		};
794 		char *reg_map[][2] = {
795 			{ "$ctx", "r1" },
796 			{ "$dst", "r2" },
797 			{}
798 		};
799 
800 		pinfo.prog_kind = "LDX";
801 		pinfo.prog = ldx_prog;
802 		pinfo.prog_len = ARRAY_SIZE(ldx_prog);
803 		match_program(btf, &pinfo, test->read, reg_map, false);
804 	}
805 
806 	if (test->write || test->write_st || test->write_stx) {
807 		struct bpf_insn stx_prog[] = {
808 			BPF_MOV64_IMM(BPF_REG_2, 0),
809 			BPF_STX_MEM(bpf_sz, BPF_REG_1, BPF_REG_2, test->field_offset),
810 			BPF_MOV64_IMM(BPF_REG_0, 0),
811 			BPF_EXIT_INSN(),
812 		};
813 		char *stx_reg_map[][2] = {
814 			{ "$ctx", "r1" },
815 			{ "$src", "r2" },
816 			{}
817 		};
818 		struct bpf_insn st_prog[] = {
819 			BPF_ST_MEM(bpf_sz, BPF_REG_1, test->field_offset,
820 				   test->st_value.use ? test->st_value.value : 42),
821 			BPF_MOV64_IMM(BPF_REG_0, 0),
822 			BPF_EXIT_INSN(),
823 		};
824 		char *st_reg_map[][2] = {
825 			{ "$ctx", "r1" },
826 			{ "$src", "42" },
827 			{}
828 		};
829 
830 		if (test->write || test->write_stx) {
831 			char *pattern = test->write_stx ? test->write_stx : test->write;
832 
833 			pinfo.prog_kind = "STX";
834 			pinfo.prog = stx_prog;
835 			pinfo.prog_len = ARRAY_SIZE(stx_prog);
836 			match_program(btf, &pinfo, pattern, stx_reg_map, true);
837 		}
838 
839 		if (test->write || test->write_st) {
840 			char *pattern = test->write_st ? test->write_st : test->write;
841 
842 			pinfo.prog_kind = "ST";
843 			pinfo.prog = st_prog;
844 			pinfo.prog_len = ARRAY_SIZE(st_prog);
845 			match_program(btf, &pinfo, pattern, st_reg_map, false);
846 		}
847 	}
848 
849 	test__end_subtest();
850 }
851 
852 void test_ctx_rewrite(void)
853 {
854 	struct btf *btf;
855 	int i;
856 
857 	field_regex = compile_regex("^([[:alpha:]_][[:alnum:]_]+)::([[:alpha:]_][[:alnum:]_]+)");
858 	ident_regex = compile_regex("^[[:alpha:]_][[:alnum:]_]+");
859 	if (!field_regex || !ident_regex)
860 		return;
861 
862 	btf = btf__load_vmlinux_btf();
863 	if (!btf) {
864 		PRINT_FAIL("Can't load vmlinux BTF, errno %d (%s)\n", errno, strerror(errno));
865 		goto out;
866 	}
867 
868 	for (i = 0; i < ARRAY_SIZE(test_cases); ++i)
869 		run_one_testcase(btf, &test_cases[i]);
870 
871 out:
872 	btf__free(btf);
873 	free_regex(field_regex);
874 	free_regex(ident_regex);
875 }
876