brintos

brintos / linux-shallow public Read only

0
0
Text · 20.5 KiB · dd75ccb Raw
822 lines · c
1// SPDX-License-Identifier: GPL-2.02 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 cases19 * specified in the test_cases array. Each test case specifies a program20 * type, context field offset, and disassembly patterns that correspond21 * to read and write instructions generated by22 * 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 pattern30 * 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) and48	 *             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#endif88	{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_ENDIAN149		.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#else156		.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#endif163	},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 N212 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 within381 * 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 an441		 * additional line with '^' marks, highlighting442		 * 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				else449					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 numerical463 *   value for the offset of <field> in <type>. The `text` is expected to464 *   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` is476 *   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 reference489 */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::cb582		 */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 the605		 * 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 level660	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}822