Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.9.
  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 */
 32struct 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
 67static 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
213static regex_t *ident_regex;
214static regex_t *field_regex;
215
216static char *skip_space(char *str)
217{
218	while (*str && isspace(*str))
219		++str;
220	return str;
221}
222
223static char *skip_space_and_semi(char *str)
224{
225	while (*str && (isspace(*str) || *str == ';'))
226		++str;
227	return str;
228}
229
230static 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
241static 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
252static 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
291static 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
328static 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
352static void free_regex(regex_t *re)
353{
354	if (!re)
355		return;
356
357	regfree(re);
358	free(re);
359}
360
361static 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 */
390static 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 */
490static 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
626err:
627	test__fail();
628	print_match_error(stdout, pattern_origin, text_origin, pattern, text);
629	return false;
630}
631
632struct 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
640static 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
698out:
699	if (prog_fd)
700		close(prog_fd);
701	free(buf);
702	free(text);
703}
704
705static 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
798void 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
817out:
818	btf__free(btf);
819	free_regex(field_regex);
820	free_regex(ident_regex);
821}