xref: /openbmc/linux/tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
171cf4d02SEduard Zingerman // SPDX-License-Identifier: GPL-2.0
271cf4d02SEduard Zingerman 
371cf4d02SEduard Zingerman #include <limits.h>
471cf4d02SEduard Zingerman #include <stdio.h>
571cf4d02SEduard Zingerman #include <string.h>
671cf4d02SEduard Zingerman #include <ctype.h>
771cf4d02SEduard Zingerman #include <regex.h>
871cf4d02SEduard Zingerman #include <test_progs.h>
971cf4d02SEduard Zingerman 
1071cf4d02SEduard Zingerman #include "bpf/btf.h"
1171cf4d02SEduard Zingerman #include "bpf_util.h"
1271cf4d02SEduard Zingerman #include "linux/filter.h"
1371cf4d02SEduard Zingerman #include "disasm.h"
1471cf4d02SEduard Zingerman 
1571cf4d02SEduard Zingerman #define MAX_PROG_TEXT_SZ (32 * 1024)
1671cf4d02SEduard Zingerman 
1771cf4d02SEduard Zingerman /* The code in this file serves the sole purpose of executing test cases
1871cf4d02SEduard Zingerman  * specified in the test_cases array. Each test case specifies a program
1971cf4d02SEduard Zingerman  * type, context field offset, and disassembly patterns that correspond
2071cf4d02SEduard Zingerman  * to read and write instructions generated by
2171cf4d02SEduard Zingerman  * verifier.c:convert_ctx_access() for accessing that field.
2271cf4d02SEduard Zingerman  *
2371cf4d02SEduard Zingerman  * For each test case, up to three programs are created:
2471cf4d02SEduard Zingerman  * - One that uses BPF_LDX_MEM to read the context field.
2571cf4d02SEduard Zingerman  * - One that uses BPF_STX_MEM to write to the context field.
2671cf4d02SEduard Zingerman  * - One that uses BPF_ST_MEM to write to the context field.
2771cf4d02SEduard Zingerman  *
2871cf4d02SEduard Zingerman  * The disassembly of each program is then compared with the pattern
2971cf4d02SEduard Zingerman  * specified in the test case.
3071cf4d02SEduard Zingerman  */
3171cf4d02SEduard Zingerman struct test_case {
3271cf4d02SEduard Zingerman 	char *name;
3371cf4d02SEduard Zingerman 	enum bpf_prog_type prog_type;
3471cf4d02SEduard Zingerman 	enum bpf_attach_type expected_attach_type;
3571cf4d02SEduard Zingerman 	int field_offset;
3671cf4d02SEduard Zingerman 	int field_sz;
3771cf4d02SEduard Zingerman 	/* Program generated for BPF_ST_MEM uses value 42 by default,
3871cf4d02SEduard Zingerman 	 * this field allows to specify custom value.
3971cf4d02SEduard Zingerman 	 */
4071cf4d02SEduard Zingerman 	struct {
4171cf4d02SEduard Zingerman 		bool use;
4271cf4d02SEduard Zingerman 		int value;
4371cf4d02SEduard Zingerman 	} st_value;
4471cf4d02SEduard Zingerman 	/* Pattern for BPF_LDX_MEM(field_sz, dst, ctx, field_offset) */
4571cf4d02SEduard Zingerman 	char *read;
4671cf4d02SEduard Zingerman 	/* Pattern for BPF_STX_MEM(field_sz, ctx, src, field_offset) and
4771cf4d02SEduard Zingerman 	 *             BPF_ST_MEM (field_sz, ctx, src, field_offset)
4871cf4d02SEduard Zingerman 	 */
4971cf4d02SEduard Zingerman 	char *write;
5071cf4d02SEduard Zingerman 	/* Pattern for BPF_ST_MEM(field_sz, ctx, src, field_offset),
5171cf4d02SEduard Zingerman 	 * takes priority over `write`.
5271cf4d02SEduard Zingerman 	 */
5371cf4d02SEduard Zingerman 	char *write_st;
5471cf4d02SEduard Zingerman 	/* Pattern for BPF_STX_MEM (field_sz, ctx, src, field_offset),
5571cf4d02SEduard Zingerman 	 * takes priority over `write`.
5671cf4d02SEduard Zingerman 	 */
5771cf4d02SEduard Zingerman 	char *write_stx;
5871cf4d02SEduard Zingerman };
5971cf4d02SEduard Zingerman 
6071cf4d02SEduard Zingerman #define N(_prog_type, type, field, name_extra...)	\
6171cf4d02SEduard Zingerman 	.name = #_prog_type "." #field name_extra,	\
6271cf4d02SEduard Zingerman 	.prog_type = BPF_PROG_TYPE_##_prog_type,	\
6371cf4d02SEduard Zingerman 	.field_offset = offsetof(type, field),		\
6471cf4d02SEduard Zingerman 	.field_sz = sizeof(typeof(((type *)NULL)->field))
6571cf4d02SEduard Zingerman 
6671cf4d02SEduard Zingerman static struct test_case test_cases[] = {
6771cf4d02SEduard Zingerman /* Sign extension on s390 changes the pattern */
6871cf4d02SEduard Zingerman #if defined(__x86_64__) || defined(__aarch64__)
6971cf4d02SEduard Zingerman 	{
7071cf4d02SEduard Zingerman 		N(SCHED_CLS, struct __sk_buff, tstamp),
7104aae213SJakub Kicinski 		.read  = "r11 = *(u8 *)($ctx + sk_buff::__mono_tc_offset);"
72*c0ba8611SJakub Kicinski 			 "w11 &= 3;"
73*c0ba8611SJakub Kicinski 			 "if w11 != 0x3 goto pc+2;"
7471cf4d02SEduard Zingerman 			 "$dst = 0;"
7571cf4d02SEduard Zingerman 			 "goto pc+1;"
7671cf4d02SEduard Zingerman 			 "$dst = *(u64 *)($ctx + sk_buff::tstamp);",
7704aae213SJakub Kicinski 		.write = "r11 = *(u8 *)($ctx + sk_buff::__mono_tc_offset);"
78*c0ba8611SJakub Kicinski 			 "if w11 & 0x2 goto pc+1;"
7971cf4d02SEduard Zingerman 			 "goto pc+2;"
80*c0ba8611SJakub Kicinski 			 "w11 &= -2;"
8104aae213SJakub Kicinski 			 "*(u8 *)($ctx + sk_buff::__mono_tc_offset) = r11;"
8271cf4d02SEduard Zingerman 			 "*(u64 *)($ctx + sk_buff::tstamp) = $src;",
8371cf4d02SEduard Zingerman 	},
8471cf4d02SEduard Zingerman #endif
8571cf4d02SEduard Zingerman 	{
8671cf4d02SEduard Zingerman 		N(SCHED_CLS, struct __sk_buff, priority),
8771cf4d02SEduard Zingerman 		.read  = "$dst = *(u32 *)($ctx + sk_buff::priority);",
8871cf4d02SEduard Zingerman 		.write = "*(u32 *)($ctx + sk_buff::priority) = $src;",
8971cf4d02SEduard Zingerman 	},
9071cf4d02SEduard Zingerman 	{
9171cf4d02SEduard Zingerman 		N(SCHED_CLS, struct __sk_buff, mark),
9271cf4d02SEduard Zingerman 		.read  = "$dst = *(u32 *)($ctx + sk_buff::mark);",
9371cf4d02SEduard Zingerman 		.write = "*(u32 *)($ctx + sk_buff::mark) = $src;",
9471cf4d02SEduard Zingerman 	},
9571cf4d02SEduard Zingerman 	{
9671cf4d02SEduard Zingerman 		N(SCHED_CLS, struct __sk_buff, cb[0]),
9771cf4d02SEduard Zingerman 		.read  = "$dst = *(u32 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::data));",
9871cf4d02SEduard Zingerman 		.write = "*(u32 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::data)) = $src;",
9971cf4d02SEduard Zingerman 	},
10071cf4d02SEduard Zingerman 	{
10171cf4d02SEduard Zingerman 		N(SCHED_CLS, struct __sk_buff, tc_classid),
10271cf4d02SEduard Zingerman 		.read  = "$dst = *(u16 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::tc_classid));",
10371cf4d02SEduard Zingerman 		.write = "*(u16 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::tc_classid)) = $src;",
10471cf4d02SEduard Zingerman 	},
10571cf4d02SEduard Zingerman 	{
10671cf4d02SEduard Zingerman 		N(SCHED_CLS, struct __sk_buff, tc_index),
10771cf4d02SEduard Zingerman 		.read  = "$dst = *(u16 *)($ctx + sk_buff::tc_index);",
10871cf4d02SEduard Zingerman 		.write = "*(u16 *)($ctx + sk_buff::tc_index) = $src;",
10971cf4d02SEduard Zingerman 	},
11071cf4d02SEduard Zingerman 	{
11171cf4d02SEduard Zingerman 		N(SCHED_CLS, struct __sk_buff, queue_mapping),
11271cf4d02SEduard Zingerman 		.read      = "$dst = *(u16 *)($ctx + sk_buff::queue_mapping);",
11371cf4d02SEduard Zingerman 		.write_stx = "if $src >= 0xffff goto pc+1;"
11471cf4d02SEduard Zingerman 			     "*(u16 *)($ctx + sk_buff::queue_mapping) = $src;",
11571cf4d02SEduard Zingerman 		.write_st  = "*(u16 *)($ctx + sk_buff::queue_mapping) = $src;",
11671cf4d02SEduard Zingerman 	},
11771cf4d02SEduard Zingerman 	{
11871cf4d02SEduard Zingerman 		/* This is a corner case in filter.c:bpf_convert_ctx_access() */
11971cf4d02SEduard Zingerman 		N(SCHED_CLS, struct __sk_buff, queue_mapping, ".ushrt_max"),
12071cf4d02SEduard Zingerman 		.st_value = { true, USHRT_MAX },
12171cf4d02SEduard Zingerman 		.write_st = "goto pc+0;",
12271cf4d02SEduard Zingerman 	},
12371cf4d02SEduard Zingerman 	{
12471cf4d02SEduard Zingerman 		N(CGROUP_SOCK, struct bpf_sock, bound_dev_if),
12571cf4d02SEduard Zingerman 		.read  = "$dst = *(u32 *)($ctx + sock_common::skc_bound_dev_if);",
12671cf4d02SEduard Zingerman 		.write = "*(u32 *)($ctx + sock_common::skc_bound_dev_if) = $src;",
12771cf4d02SEduard Zingerman 	},
12871cf4d02SEduard Zingerman 	{
12971cf4d02SEduard Zingerman 		N(CGROUP_SOCK, struct bpf_sock, mark),
13071cf4d02SEduard Zingerman 		.read  = "$dst = *(u32 *)($ctx + sock::sk_mark);",
13171cf4d02SEduard Zingerman 		.write = "*(u32 *)($ctx + sock::sk_mark) = $src;",
13271cf4d02SEduard Zingerman 	},
13371cf4d02SEduard Zingerman 	{
13471cf4d02SEduard Zingerman 		N(CGROUP_SOCK, struct bpf_sock, priority),
13571cf4d02SEduard Zingerman 		.read  = "$dst = *(u32 *)($ctx + sock::sk_priority);",
13671cf4d02SEduard Zingerman 		.write = "*(u32 *)($ctx + sock::sk_priority) = $src;",
13771cf4d02SEduard Zingerman 	},
13871cf4d02SEduard Zingerman 	{
13971cf4d02SEduard Zingerman 		N(SOCK_OPS, struct bpf_sock_ops, replylong[0]),
14071cf4d02SEduard Zingerman 		.read  = "$dst = *(u32 *)($ctx + bpf_sock_ops_kern::replylong);",
14171cf4d02SEduard Zingerman 		.write = "*(u32 *)($ctx + bpf_sock_ops_kern::replylong) = $src;",
14271cf4d02SEduard Zingerman 	},
14371cf4d02SEduard Zingerman 	{
14471cf4d02SEduard Zingerman 		N(CGROUP_SYSCTL, struct bpf_sysctl, file_pos),
14571cf4d02SEduard Zingerman #if __BYTE_ORDER == __LITTLE_ENDIAN
14671cf4d02SEduard Zingerman 		.read  = "$dst = *(u64 *)($ctx + bpf_sysctl_kern::ppos);"
14771cf4d02SEduard Zingerman 			 "$dst = *(u32 *)($dst +0);",
14871cf4d02SEduard Zingerman 		.write = "*(u64 *)($ctx + bpf_sysctl_kern::tmp_reg) = r9;"
14971cf4d02SEduard Zingerman 			 "r9 = *(u64 *)($ctx + bpf_sysctl_kern::ppos);"
15071cf4d02SEduard Zingerman 			 "*(u32 *)(r9 +0) = $src;"
15171cf4d02SEduard Zingerman 			 "r9 = *(u64 *)($ctx + bpf_sysctl_kern::tmp_reg);",
15271cf4d02SEduard Zingerman #else
15371cf4d02SEduard Zingerman 		.read  = "$dst = *(u64 *)($ctx + bpf_sysctl_kern::ppos);"
15471cf4d02SEduard Zingerman 			 "$dst = *(u32 *)($dst +4);",
15571cf4d02SEduard Zingerman 		.write = "*(u64 *)($ctx + bpf_sysctl_kern::tmp_reg) = r9;"
15671cf4d02SEduard Zingerman 			 "r9 = *(u64 *)($ctx + bpf_sysctl_kern::ppos);"
15771cf4d02SEduard Zingerman 			 "*(u32 *)(r9 +4) = $src;"
15871cf4d02SEduard Zingerman 			 "r9 = *(u64 *)($ctx + bpf_sysctl_kern::tmp_reg);",
15971cf4d02SEduard Zingerman #endif
16071cf4d02SEduard Zingerman 	},
16171cf4d02SEduard Zingerman 	{
16271cf4d02SEduard Zingerman 		N(CGROUP_SOCKOPT, struct bpf_sockopt, sk),
16371cf4d02SEduard Zingerman 		.read  = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::sk);",
16471cf4d02SEduard Zingerman 		.expected_attach_type = BPF_CGROUP_GETSOCKOPT,
16571cf4d02SEduard Zingerman 	},
16671cf4d02SEduard Zingerman 	{
16771cf4d02SEduard Zingerman 		N(CGROUP_SOCKOPT, struct bpf_sockopt, level),
16871cf4d02SEduard Zingerman 		.read  = "$dst = *(u32 *)($ctx + bpf_sockopt_kern::level);",
16971cf4d02SEduard Zingerman 		.write = "*(u32 *)($ctx + bpf_sockopt_kern::level) = $src;",
17071cf4d02SEduard Zingerman 		.expected_attach_type = BPF_CGROUP_SETSOCKOPT,
17171cf4d02SEduard Zingerman 	},
17271cf4d02SEduard Zingerman 	{
17371cf4d02SEduard Zingerman 		N(CGROUP_SOCKOPT, struct bpf_sockopt, optname),
17471cf4d02SEduard Zingerman 		.read  = "$dst = *(u32 *)($ctx + bpf_sockopt_kern::optname);",
17571cf4d02SEduard Zingerman 		.write = "*(u32 *)($ctx + bpf_sockopt_kern::optname) = $src;",
17671cf4d02SEduard Zingerman 		.expected_attach_type = BPF_CGROUP_SETSOCKOPT,
17771cf4d02SEduard Zingerman 	},
17871cf4d02SEduard Zingerman 	{
17971cf4d02SEduard Zingerman 		N(CGROUP_SOCKOPT, struct bpf_sockopt, optlen),
18071cf4d02SEduard Zingerman 		.read  = "$dst = *(u32 *)($ctx + bpf_sockopt_kern::optlen);",
18171cf4d02SEduard Zingerman 		.write = "*(u32 *)($ctx + bpf_sockopt_kern::optlen) = $src;",
18271cf4d02SEduard Zingerman 		.expected_attach_type = BPF_CGROUP_SETSOCKOPT,
18371cf4d02SEduard Zingerman 	},
18471cf4d02SEduard Zingerman 	{
18571cf4d02SEduard Zingerman 		N(CGROUP_SOCKOPT, struct bpf_sockopt, retval),
18671cf4d02SEduard Zingerman 		.read  = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::current_task);"
18771cf4d02SEduard Zingerman 			 "$dst = *(u64 *)($dst + task_struct::bpf_ctx);"
18871cf4d02SEduard Zingerman 			 "$dst = *(u32 *)($dst + bpf_cg_run_ctx::retval);",
18971cf4d02SEduard Zingerman 		.write = "*(u64 *)($ctx + bpf_sockopt_kern::tmp_reg) = r9;"
19071cf4d02SEduard Zingerman 			 "r9 = *(u64 *)($ctx + bpf_sockopt_kern::current_task);"
19171cf4d02SEduard Zingerman 			 "r9 = *(u64 *)(r9 + task_struct::bpf_ctx);"
19271cf4d02SEduard Zingerman 			 "*(u32 *)(r9 + bpf_cg_run_ctx::retval) = $src;"
19371cf4d02SEduard Zingerman 			 "r9 = *(u64 *)($ctx + bpf_sockopt_kern::tmp_reg);",
19471cf4d02SEduard Zingerman 		.expected_attach_type = BPF_CGROUP_GETSOCKOPT,
19571cf4d02SEduard Zingerman 	},
19671cf4d02SEduard Zingerman 	{
19771cf4d02SEduard Zingerman 		N(CGROUP_SOCKOPT, struct bpf_sockopt, optval),
19871cf4d02SEduard Zingerman 		.read  = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::optval);",
19971cf4d02SEduard Zingerman 		.expected_attach_type = BPF_CGROUP_GETSOCKOPT,
20071cf4d02SEduard Zingerman 	},
20171cf4d02SEduard Zingerman 	{
20271cf4d02SEduard Zingerman 		N(CGROUP_SOCKOPT, struct bpf_sockopt, optval_end),
20371cf4d02SEduard Zingerman 		.read  = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::optval_end);",
20471cf4d02SEduard Zingerman 		.expected_attach_type = BPF_CGROUP_GETSOCKOPT,
20571cf4d02SEduard Zingerman 	},
20671cf4d02SEduard Zingerman };
20771cf4d02SEduard Zingerman 
20871cf4d02SEduard Zingerman #undef N
20971cf4d02SEduard Zingerman 
21071cf4d02SEduard Zingerman static regex_t *ident_regex;
21171cf4d02SEduard Zingerman static regex_t *field_regex;
21271cf4d02SEduard Zingerman 
skip_space(char * str)21371cf4d02SEduard Zingerman static char *skip_space(char *str)
21471cf4d02SEduard Zingerman {
21571cf4d02SEduard Zingerman 	while (*str && isspace(*str))
21671cf4d02SEduard Zingerman 		++str;
21771cf4d02SEduard Zingerman 	return str;
21871cf4d02SEduard Zingerman }
21971cf4d02SEduard Zingerman 
skip_space_and_semi(char * str)22071cf4d02SEduard Zingerman static char *skip_space_and_semi(char *str)
22171cf4d02SEduard Zingerman {
22271cf4d02SEduard Zingerman 	while (*str && (isspace(*str) || *str == ';'))
22371cf4d02SEduard Zingerman 		++str;
22471cf4d02SEduard Zingerman 	return str;
22571cf4d02SEduard Zingerman }
22671cf4d02SEduard Zingerman 
match_str(char * str,char * prefix)22771cf4d02SEduard Zingerman static char *match_str(char *str, char *prefix)
22871cf4d02SEduard Zingerman {
22971cf4d02SEduard Zingerman 	while (*str && *prefix && *str == *prefix) {
23071cf4d02SEduard Zingerman 		++str;
23171cf4d02SEduard Zingerman 		++prefix;
23271cf4d02SEduard Zingerman 	}
23371cf4d02SEduard Zingerman 	if (*prefix)
23471cf4d02SEduard Zingerman 		return NULL;
23571cf4d02SEduard Zingerman 	return str;
23671cf4d02SEduard Zingerman }
23771cf4d02SEduard Zingerman 
match_number(char * str,int num)23871cf4d02SEduard Zingerman static char *match_number(char *str, int num)
23971cf4d02SEduard Zingerman {
24071cf4d02SEduard Zingerman 	char *next;
24171cf4d02SEduard Zingerman 	int snum = strtol(str, &next, 10);
24271cf4d02SEduard Zingerman 
24371cf4d02SEduard Zingerman 	if (next - str == 0 || num != snum)
24471cf4d02SEduard Zingerman 		return NULL;
24571cf4d02SEduard Zingerman 
24671cf4d02SEduard Zingerman 	return next;
24771cf4d02SEduard Zingerman }
24871cf4d02SEduard Zingerman 
find_field_offset_aux(struct btf * btf,int btf_id,char * field_name,int off)24971cf4d02SEduard Zingerman static int find_field_offset_aux(struct btf *btf, int btf_id, char *field_name, int off)
25071cf4d02SEduard Zingerman {
25171cf4d02SEduard Zingerman 	const struct btf_type *type = btf__type_by_id(btf, btf_id);
25271cf4d02SEduard Zingerman 	const struct btf_member *m;
25371cf4d02SEduard Zingerman 	__u16 mnum;
25471cf4d02SEduard Zingerman 	int i;
25571cf4d02SEduard Zingerman 
25671cf4d02SEduard Zingerman 	if (!type) {
25771cf4d02SEduard Zingerman 		PRINT_FAIL("Can't find btf_type for id %d\n", btf_id);
25871cf4d02SEduard Zingerman 		return -1;
25971cf4d02SEduard Zingerman 	}
26071cf4d02SEduard Zingerman 
26171cf4d02SEduard Zingerman 	if (!btf_is_struct(type) && !btf_is_union(type)) {
26271cf4d02SEduard Zingerman 		PRINT_FAIL("BTF id %d is not struct or union\n", btf_id);
26371cf4d02SEduard Zingerman 		return -1;
26471cf4d02SEduard Zingerman 	}
26571cf4d02SEduard Zingerman 
26671cf4d02SEduard Zingerman 	m = btf_members(type);
26771cf4d02SEduard Zingerman 	mnum = btf_vlen(type);
26871cf4d02SEduard Zingerman 
26971cf4d02SEduard Zingerman 	for (i = 0; i < mnum; ++i, ++m) {
27071cf4d02SEduard Zingerman 		const char *mname = btf__name_by_offset(btf, m->name_off);
27171cf4d02SEduard Zingerman 
27271cf4d02SEduard Zingerman 		if (strcmp(mname, "") == 0) {
27371cf4d02SEduard Zingerman 			int msize = find_field_offset_aux(btf, m->type, field_name,
27471cf4d02SEduard Zingerman 							  off + m->offset);
27571cf4d02SEduard Zingerman 			if (msize >= 0)
27671cf4d02SEduard Zingerman 				return msize;
27771cf4d02SEduard Zingerman 		}
27871cf4d02SEduard Zingerman 
27971cf4d02SEduard Zingerman 		if (strcmp(mname, field_name))
28071cf4d02SEduard Zingerman 			continue;
28171cf4d02SEduard Zingerman 
28271cf4d02SEduard Zingerman 		return (off + m->offset) / 8;
28371cf4d02SEduard Zingerman 	}
28471cf4d02SEduard Zingerman 
28571cf4d02SEduard Zingerman 	return -1;
28671cf4d02SEduard Zingerman }
28771cf4d02SEduard Zingerman 
find_field_offset(struct btf * btf,char * pattern,regmatch_t * matches)28871cf4d02SEduard Zingerman static int find_field_offset(struct btf *btf, char *pattern, regmatch_t *matches)
28971cf4d02SEduard Zingerman {
29071cf4d02SEduard Zingerman 	int type_sz  = matches[1].rm_eo - matches[1].rm_so;
29171cf4d02SEduard Zingerman 	int field_sz = matches[2].rm_eo - matches[2].rm_so;
29271cf4d02SEduard Zingerman 	char *type   = pattern + matches[1].rm_so;
29371cf4d02SEduard Zingerman 	char *field  = pattern + matches[2].rm_so;
29471cf4d02SEduard Zingerman 	char field_str[128] = {};
29571cf4d02SEduard Zingerman 	char type_str[128] = {};
29671cf4d02SEduard Zingerman 	int btf_id, field_offset;
29771cf4d02SEduard Zingerman 
29871cf4d02SEduard Zingerman 	if (type_sz >= sizeof(type_str)) {
29971cf4d02SEduard Zingerman 		PRINT_FAIL("Malformed pattern: type ident is too long: %d\n", type_sz);
30071cf4d02SEduard Zingerman 		return -1;
30171cf4d02SEduard Zingerman 	}
30271cf4d02SEduard Zingerman 
30371cf4d02SEduard Zingerman 	if (field_sz >= sizeof(field_str)) {
30471cf4d02SEduard Zingerman 		PRINT_FAIL("Malformed pattern: field ident is too long: %d\n", field_sz);
30571cf4d02SEduard Zingerman 		return -1;
30671cf4d02SEduard Zingerman 	}
30771cf4d02SEduard Zingerman 
30871cf4d02SEduard Zingerman 	strncpy(type_str, type, type_sz);
30971cf4d02SEduard Zingerman 	strncpy(field_str, field, field_sz);
31071cf4d02SEduard Zingerman 	btf_id = btf__find_by_name(btf, type_str);
31171cf4d02SEduard Zingerman 	if (btf_id < 0) {
31271cf4d02SEduard Zingerman 		PRINT_FAIL("No BTF info for type %s\n", type_str);
31371cf4d02SEduard Zingerman 		return -1;
31471cf4d02SEduard Zingerman 	}
31571cf4d02SEduard Zingerman 
31671cf4d02SEduard Zingerman 	field_offset = find_field_offset_aux(btf, btf_id, field_str, 0);
31771cf4d02SEduard Zingerman 	if (field_offset < 0) {
31871cf4d02SEduard Zingerman 		PRINT_FAIL("No BTF info for field %s::%s\n", type_str, field_str);
31971cf4d02SEduard Zingerman 		return -1;
32071cf4d02SEduard Zingerman 	}
32171cf4d02SEduard Zingerman 
32271cf4d02SEduard Zingerman 	return field_offset;
32371cf4d02SEduard Zingerman }
32471cf4d02SEduard Zingerman 
compile_regex(char * pat)32571cf4d02SEduard Zingerman static regex_t *compile_regex(char *pat)
32671cf4d02SEduard Zingerman {
32771cf4d02SEduard Zingerman 	regex_t *re;
32871cf4d02SEduard Zingerman 	int err;
32971cf4d02SEduard Zingerman 
33071cf4d02SEduard Zingerman 	re = malloc(sizeof(regex_t));
33171cf4d02SEduard Zingerman 	if (!re) {
33271cf4d02SEduard Zingerman 		PRINT_FAIL("Can't alloc regex\n");
33371cf4d02SEduard Zingerman 		return NULL;
33471cf4d02SEduard Zingerman 	}
33571cf4d02SEduard Zingerman 
33671cf4d02SEduard Zingerman 	err = regcomp(re, pat, REG_EXTENDED);
33771cf4d02SEduard Zingerman 	if (err) {
33871cf4d02SEduard Zingerman 		char errbuf[512];
33971cf4d02SEduard Zingerman 
34071cf4d02SEduard Zingerman 		regerror(err, re, errbuf, sizeof(errbuf));
34171cf4d02SEduard Zingerman 		PRINT_FAIL("Can't compile regex: %s\n", errbuf);
34271cf4d02SEduard Zingerman 		free(re);
34371cf4d02SEduard Zingerman 		return NULL;
34471cf4d02SEduard Zingerman 	}
34571cf4d02SEduard Zingerman 
34671cf4d02SEduard Zingerman 	return re;
34771cf4d02SEduard Zingerman }
34871cf4d02SEduard Zingerman 
free_regex(regex_t * re)34971cf4d02SEduard Zingerman static void free_regex(regex_t *re)
35071cf4d02SEduard Zingerman {
35171cf4d02SEduard Zingerman 	if (!re)
35271cf4d02SEduard Zingerman 		return;
35371cf4d02SEduard Zingerman 
35471cf4d02SEduard Zingerman 	regfree(re);
35571cf4d02SEduard Zingerman 	free(re);
35671cf4d02SEduard Zingerman }
35771cf4d02SEduard Zingerman 
max_line_len(char * str)35871cf4d02SEduard Zingerman static u32 max_line_len(char *str)
35971cf4d02SEduard Zingerman {
36071cf4d02SEduard Zingerman 	u32 max_line = 0;
36171cf4d02SEduard Zingerman 	char *next = str;
36271cf4d02SEduard Zingerman 
36371cf4d02SEduard Zingerman 	while (next) {
36471cf4d02SEduard Zingerman 		next = strchr(str, '\n');
36571cf4d02SEduard Zingerman 		if (next) {
36671cf4d02SEduard Zingerman 			max_line = max_t(u32, max_line, (next - str));
36771cf4d02SEduard Zingerman 			str = next + 1;
36871cf4d02SEduard Zingerman 		} else {
36971cf4d02SEduard Zingerman 			max_line = max_t(u32, max_line, strlen(str));
37071cf4d02SEduard Zingerman 		}
37171cf4d02SEduard Zingerman 	}
37271cf4d02SEduard Zingerman 
37371cf4d02SEduard Zingerman 	return min(max_line, 60u);
37471cf4d02SEduard Zingerman }
37571cf4d02SEduard Zingerman 
37671cf4d02SEduard Zingerman /* Print strings `pattern_origin` and `text_origin` side by side,
37771cf4d02SEduard Zingerman  * assume `pattern_pos` and `text_pos` designate location within
37871cf4d02SEduard Zingerman  * corresponding origin string where match diverges.
37971cf4d02SEduard Zingerman  * The output should look like:
38071cf4d02SEduard Zingerman  *
38171cf4d02SEduard Zingerman  *   Can't match disassembly(left) with pattern(right):
38271cf4d02SEduard Zingerman  *   r2 = *(u64 *)(r1 +0)  ;  $dst = *(u64 *)($ctx + bpf_sockopt_kern::sk1)
38371cf4d02SEduard Zingerman  *                     ^                             ^
38471cf4d02SEduard Zingerman  *   r0 = 0                ;
38571cf4d02SEduard Zingerman  *   exit                  ;
38671cf4d02SEduard Zingerman  */
print_match_error(FILE * out,char * pattern_origin,char * text_origin,char * pattern_pos,char * text_pos)38771cf4d02SEduard Zingerman static void print_match_error(FILE *out,
38871cf4d02SEduard Zingerman 			      char *pattern_origin, char *text_origin,
38971cf4d02SEduard Zingerman 			      char *pattern_pos, char *text_pos)
39071cf4d02SEduard Zingerman {
39171cf4d02SEduard Zingerman 	char *pattern = pattern_origin;
39271cf4d02SEduard Zingerman 	char *text = text_origin;
39371cf4d02SEduard Zingerman 	int middle = max_line_len(text) + 2;
39471cf4d02SEduard Zingerman 
39571cf4d02SEduard Zingerman 	fprintf(out, "Can't match disassembly(left) with pattern(right):\n");
39671cf4d02SEduard Zingerman 	while (*pattern || *text) {
39771cf4d02SEduard Zingerman 		int column = 0;
39871cf4d02SEduard Zingerman 		int mark1 = -1;
39971cf4d02SEduard Zingerman 		int mark2 = -1;
40071cf4d02SEduard Zingerman 
40171cf4d02SEduard Zingerman 		/* Print one line from text */
40271cf4d02SEduard Zingerman 		while (*text && *text != '\n') {
40371cf4d02SEduard Zingerman 			if (text == text_pos)
40471cf4d02SEduard Zingerman 				mark1 = column;
40571cf4d02SEduard Zingerman 			fputc(*text, out);
40671cf4d02SEduard Zingerman 			++text;
40771cf4d02SEduard Zingerman 			++column;
40871cf4d02SEduard Zingerman 		}
40971cf4d02SEduard Zingerman 		if (text == text_pos)
41071cf4d02SEduard Zingerman 			mark1 = column;
41171cf4d02SEduard Zingerman 
41271cf4d02SEduard Zingerman 		/* Pad to the middle */
41371cf4d02SEduard Zingerman 		while (column < middle) {
41471cf4d02SEduard Zingerman 			fputc(' ', out);
41571cf4d02SEduard Zingerman 			++column;
41671cf4d02SEduard Zingerman 		}
41771cf4d02SEduard Zingerman 		fputs(";  ", out);
41871cf4d02SEduard Zingerman 		column += 3;
41971cf4d02SEduard Zingerman 
42071cf4d02SEduard Zingerman 		/* Print one line from pattern, pattern lines are terminated by ';' */
42171cf4d02SEduard Zingerman 		while (*pattern && *pattern != ';') {
42271cf4d02SEduard Zingerman 			if (pattern == pattern_pos)
42371cf4d02SEduard Zingerman 				mark2 = column;
42471cf4d02SEduard Zingerman 			fputc(*pattern, out);
42571cf4d02SEduard Zingerman 			++pattern;
42671cf4d02SEduard Zingerman 			++column;
42771cf4d02SEduard Zingerman 		}
42871cf4d02SEduard Zingerman 		if (pattern == pattern_pos)
42971cf4d02SEduard Zingerman 			mark2 = column;
43071cf4d02SEduard Zingerman 
43171cf4d02SEduard Zingerman 		fputc('\n', out);
43271cf4d02SEduard Zingerman 		if (*pattern)
43371cf4d02SEduard Zingerman 			++pattern;
43471cf4d02SEduard Zingerman 		if (*text)
43571cf4d02SEduard Zingerman 			++text;
43671cf4d02SEduard Zingerman 
43771cf4d02SEduard Zingerman 		/* If pattern and text diverge at this line, print an
43871cf4d02SEduard Zingerman 		 * additional line with '^' marks, highlighting
43971cf4d02SEduard Zingerman 		 * positions where match fails.
44071cf4d02SEduard Zingerman 		 */
44171cf4d02SEduard Zingerman 		if (mark1 > 0 || mark2 > 0) {
44271cf4d02SEduard Zingerman 			for (column = 0; column <= max(mark1, mark2); ++column) {
44371cf4d02SEduard Zingerman 				if (column == mark1 || column == mark2)
44471cf4d02SEduard Zingerman 					fputc('^', out);
44571cf4d02SEduard Zingerman 				else
44671cf4d02SEduard Zingerman 					fputc(' ', out);
44771cf4d02SEduard Zingerman 			}
44871cf4d02SEduard Zingerman 			fputc('\n', out);
44971cf4d02SEduard Zingerman 		}
45071cf4d02SEduard Zingerman 	}
45171cf4d02SEduard Zingerman }
45271cf4d02SEduard Zingerman 
45371cf4d02SEduard Zingerman /* Test if `text` matches `pattern`. Pattern consists of the following elements:
45471cf4d02SEduard Zingerman  *
45571cf4d02SEduard Zingerman  * - Field offset references:
45671cf4d02SEduard Zingerman  *
45771cf4d02SEduard Zingerman  *     <type>::<field>
45871cf4d02SEduard Zingerman  *
45971cf4d02SEduard Zingerman  *   When such reference is encountered BTF is used to compute numerical
46071cf4d02SEduard Zingerman  *   value for the offset of <field> in <type>. The `text` is expected to
46171cf4d02SEduard Zingerman  *   contain matching numerical value.
46271cf4d02SEduard Zingerman  *
46371cf4d02SEduard Zingerman  * - Field groups:
46471cf4d02SEduard Zingerman  *
46571cf4d02SEduard Zingerman  *     $(<type>::<field> [+ <type>::<field>]*)
46671cf4d02SEduard Zingerman  *
46771cf4d02SEduard Zingerman  *   Allows to specify an offset that is a sum of multiple field offsets.
46871cf4d02SEduard Zingerman  *   The `text` is expected to contain matching numerical value.
46971cf4d02SEduard Zingerman  *
47071cf4d02SEduard Zingerman  * - Variable references, e.g. `$src`, `$dst`, `$ctx`.
47171cf4d02SEduard Zingerman  *   These are substitutions specified in `reg_map` array.
47271cf4d02SEduard Zingerman  *   If a substring of pattern is equal to `reg_map[i][0]` the `text` is
47371cf4d02SEduard Zingerman  *   expected to contain `reg_map[i][1]` in the matching position.
47471cf4d02SEduard Zingerman  *
47571cf4d02SEduard Zingerman  * - Whitespace is ignored, ';' counts as whitespace for `pattern`.
47671cf4d02SEduard Zingerman  *
47771cf4d02SEduard Zingerman  * - Any other characters, `pattern` and `text` should match one-to-one.
47871cf4d02SEduard Zingerman  *
47971cf4d02SEduard Zingerman  * Example of a pattern:
48071cf4d02SEduard Zingerman  *
48171cf4d02SEduard Zingerman  *                    __________ fields group ________________
48271cf4d02SEduard Zingerman  *                   '                                        '
48371cf4d02SEduard Zingerman  *   *(u16 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::tc_classid)) = $src;
48471cf4d02SEduard Zingerman  *            ^^^^                   '______________________'
48571cf4d02SEduard Zingerman  *     variable reference             field offset reference
48671cf4d02SEduard Zingerman  */
match_pattern(struct btf * btf,char * pattern,char * text,char * reg_map[][2])48771cf4d02SEduard Zingerman static bool match_pattern(struct btf *btf, char *pattern, char *text, char *reg_map[][2])
48871cf4d02SEduard Zingerman {
48971cf4d02SEduard Zingerman 	char *pattern_origin = pattern;
49071cf4d02SEduard Zingerman 	char *text_origin = text;
49171cf4d02SEduard Zingerman 	regmatch_t matches[3];
49271cf4d02SEduard Zingerman 
49371cf4d02SEduard Zingerman _continue:
49471cf4d02SEduard Zingerman 	while (*pattern) {
49571cf4d02SEduard Zingerman 		if (!*text)
49671cf4d02SEduard Zingerman 			goto err;
49771cf4d02SEduard Zingerman 
49871cf4d02SEduard Zingerman 		/* Skip whitespace */
49971cf4d02SEduard Zingerman 		if (isspace(*pattern) || *pattern == ';') {
50071cf4d02SEduard Zingerman 			if (!isspace(*text) && text != text_origin && isalnum(text[-1]))
50171cf4d02SEduard Zingerman 				goto err;
50271cf4d02SEduard Zingerman 			pattern = skip_space_and_semi(pattern);
50371cf4d02SEduard Zingerman 			text = skip_space(text);
50471cf4d02SEduard Zingerman 			continue;
50571cf4d02SEduard Zingerman 		}
50671cf4d02SEduard Zingerman 
50771cf4d02SEduard Zingerman 		/* Check for variable references */
50871cf4d02SEduard Zingerman 		for (int i = 0; reg_map[i][0]; ++i) {
50971cf4d02SEduard Zingerman 			char *pattern_next, *text_next;
51071cf4d02SEduard Zingerman 
51171cf4d02SEduard Zingerman 			pattern_next = match_str(pattern, reg_map[i][0]);
51271cf4d02SEduard Zingerman 			if (!pattern_next)
51371cf4d02SEduard Zingerman 				continue;
51471cf4d02SEduard Zingerman 
51571cf4d02SEduard Zingerman 			text_next = match_str(text, reg_map[i][1]);
51671cf4d02SEduard Zingerman 			if (!text_next)
51771cf4d02SEduard Zingerman 				goto err;
51871cf4d02SEduard Zingerman 
51971cf4d02SEduard Zingerman 			pattern = pattern_next;
52071cf4d02SEduard Zingerman 			text = text_next;
52171cf4d02SEduard Zingerman 			goto _continue;
52271cf4d02SEduard Zingerman 		}
52371cf4d02SEduard Zingerman 
52471cf4d02SEduard Zingerman 		/* Match field group:
52571cf4d02SEduard Zingerman 		 *   $(sk_buff::cb + qdisc_skb_cb::tc_classid)
52671cf4d02SEduard Zingerman 		 */
52771cf4d02SEduard Zingerman 		if (strncmp(pattern, "$(", 2) == 0) {
52871cf4d02SEduard Zingerman 			char *group_start = pattern, *text_next;
52971cf4d02SEduard Zingerman 			int acc_offset = 0;
53071cf4d02SEduard Zingerman 
53171cf4d02SEduard Zingerman 			pattern += 2;
53271cf4d02SEduard Zingerman 
53371cf4d02SEduard Zingerman 			for (;;) {
53471cf4d02SEduard Zingerman 				int field_offset;
53571cf4d02SEduard Zingerman 
53671cf4d02SEduard Zingerman 				pattern = skip_space(pattern);
53771cf4d02SEduard Zingerman 				if (!*pattern) {
53871cf4d02SEduard Zingerman 					PRINT_FAIL("Unexpected end of pattern\n");
53971cf4d02SEduard Zingerman 					goto err;
54071cf4d02SEduard Zingerman 				}
54171cf4d02SEduard Zingerman 
54271cf4d02SEduard Zingerman 				if (*pattern == ')') {
54371cf4d02SEduard Zingerman 					++pattern;
54471cf4d02SEduard Zingerman 					break;
54571cf4d02SEduard Zingerman 				}
54671cf4d02SEduard Zingerman 
54771cf4d02SEduard Zingerman 				if (*pattern == '+') {
54871cf4d02SEduard Zingerman 					++pattern;
54971cf4d02SEduard Zingerman 					continue;
55071cf4d02SEduard Zingerman 				}
55171cf4d02SEduard Zingerman 
55271cf4d02SEduard Zingerman 				printf("pattern: %s\n", pattern);
55371cf4d02SEduard Zingerman 				if (regexec(field_regex, pattern, 3, matches, 0) != 0) {
55471cf4d02SEduard Zingerman 					PRINT_FAIL("Field reference expected\n");
55571cf4d02SEduard Zingerman 					goto err;
55671cf4d02SEduard Zingerman 				}
55771cf4d02SEduard Zingerman 
55871cf4d02SEduard Zingerman 				field_offset = find_field_offset(btf, pattern, matches);
55971cf4d02SEduard Zingerman 				if (field_offset < 0)
56071cf4d02SEduard Zingerman 					goto err;
56171cf4d02SEduard Zingerman 
56271cf4d02SEduard Zingerman 				pattern += matches[0].rm_eo;
56371cf4d02SEduard Zingerman 				acc_offset += field_offset;
56471cf4d02SEduard Zingerman 			}
56571cf4d02SEduard Zingerman 
56671cf4d02SEduard Zingerman 			text_next = match_number(text, acc_offset);
56771cf4d02SEduard Zingerman 			if (!text_next) {
56871cf4d02SEduard Zingerman 				PRINT_FAIL("No match for group offset %.*s (%d)\n",
56971cf4d02SEduard Zingerman 					   (int)(pattern - group_start),
57071cf4d02SEduard Zingerman 					   group_start,
57171cf4d02SEduard Zingerman 					   acc_offset);
57271cf4d02SEduard Zingerman 				goto err;
57371cf4d02SEduard Zingerman 			}
57471cf4d02SEduard Zingerman 			text = text_next;
57571cf4d02SEduard Zingerman 		}
57671cf4d02SEduard Zingerman 
57771cf4d02SEduard Zingerman 		/* Match field reference:
57871cf4d02SEduard Zingerman 		 *   sk_buff::cb
57971cf4d02SEduard Zingerman 		 */
58071cf4d02SEduard Zingerman 		if (regexec(field_regex, pattern, 3, matches, 0) == 0) {
58171cf4d02SEduard Zingerman 			int field_offset;
58271cf4d02SEduard Zingerman 			char *text_next;
58371cf4d02SEduard Zingerman 
58471cf4d02SEduard Zingerman 			field_offset = find_field_offset(btf, pattern, matches);
58571cf4d02SEduard Zingerman 			if (field_offset < 0)
58671cf4d02SEduard Zingerman 				goto err;
58771cf4d02SEduard Zingerman 
58871cf4d02SEduard Zingerman 			text_next = match_number(text, field_offset);
58971cf4d02SEduard Zingerman 			if (!text_next) {
59071cf4d02SEduard Zingerman 				PRINT_FAIL("No match for field offset %.*s (%d)\n",
59171cf4d02SEduard Zingerman 					   (int)matches[0].rm_eo, pattern, field_offset);
59271cf4d02SEduard Zingerman 				goto err;
59371cf4d02SEduard Zingerman 			}
59471cf4d02SEduard Zingerman 
59571cf4d02SEduard Zingerman 			pattern += matches[0].rm_eo;
59671cf4d02SEduard Zingerman 			text = text_next;
59771cf4d02SEduard Zingerman 			continue;
59871cf4d02SEduard Zingerman 		}
59971cf4d02SEduard Zingerman 
60071cf4d02SEduard Zingerman 		/* If pattern points to identifier not followed by '::'
60171cf4d02SEduard Zingerman 		 * skip the identifier to avoid n^2 application of the
60271cf4d02SEduard Zingerman 		 * field reference rule.
60371cf4d02SEduard Zingerman 		 */
60471cf4d02SEduard Zingerman 		if (regexec(ident_regex, pattern, 1, matches, 0) == 0) {
60571cf4d02SEduard Zingerman 			if (strncmp(pattern, text, matches[0].rm_eo) != 0)
60671cf4d02SEduard Zingerman 				goto err;
60771cf4d02SEduard Zingerman 
60871cf4d02SEduard Zingerman 			pattern += matches[0].rm_eo;
60971cf4d02SEduard Zingerman 			text += matches[0].rm_eo;
61071cf4d02SEduard Zingerman 			continue;
61171cf4d02SEduard Zingerman 		}
61271cf4d02SEduard Zingerman 
61371cf4d02SEduard Zingerman 		/* Match literally */
61471cf4d02SEduard Zingerman 		if (*pattern != *text)
61571cf4d02SEduard Zingerman 			goto err;
61671cf4d02SEduard Zingerman 
61771cf4d02SEduard Zingerman 		++pattern;
61871cf4d02SEduard Zingerman 		++text;
61971cf4d02SEduard Zingerman 	}
62071cf4d02SEduard Zingerman 
62171cf4d02SEduard Zingerman 	return true;
62271cf4d02SEduard Zingerman 
62371cf4d02SEduard Zingerman err:
62471cf4d02SEduard Zingerman 	test__fail();
62571cf4d02SEduard Zingerman 	print_match_error(stdout, pattern_origin, text_origin, pattern, text);
62671cf4d02SEduard Zingerman 	return false;
62771cf4d02SEduard Zingerman }
62871cf4d02SEduard Zingerman 
62971cf4d02SEduard Zingerman /* Request BPF program instructions after all rewrites are applied,
63071cf4d02SEduard Zingerman  * e.g. verifier.c:convert_ctx_access() is done.
63171cf4d02SEduard Zingerman  */
get_xlated_program(int fd_prog,struct bpf_insn ** buf,__u32 * cnt)63271cf4d02SEduard Zingerman static int get_xlated_program(int fd_prog, struct bpf_insn **buf, __u32 *cnt)
63371cf4d02SEduard Zingerman {
63471cf4d02SEduard Zingerman 	struct bpf_prog_info info = {};
63571cf4d02SEduard Zingerman 	__u32 info_len = sizeof(info);
63671cf4d02SEduard Zingerman 	__u32 xlated_prog_len;
63771cf4d02SEduard Zingerman 	__u32 buf_element_size = sizeof(struct bpf_insn);
63871cf4d02SEduard Zingerman 
63971cf4d02SEduard Zingerman 	if (bpf_prog_get_info_by_fd(fd_prog, &info, &info_len)) {
64071cf4d02SEduard Zingerman 		perror("bpf_prog_get_info_by_fd failed");
64171cf4d02SEduard Zingerman 		return -1;
64271cf4d02SEduard Zingerman 	}
64371cf4d02SEduard Zingerman 
64471cf4d02SEduard Zingerman 	xlated_prog_len = info.xlated_prog_len;
64571cf4d02SEduard Zingerman 	if (xlated_prog_len % buf_element_size) {
64671cf4d02SEduard Zingerman 		printf("Program length %d is not multiple of %d\n",
64771cf4d02SEduard Zingerman 		       xlated_prog_len, buf_element_size);
64871cf4d02SEduard Zingerman 		return -1;
64971cf4d02SEduard Zingerman 	}
65071cf4d02SEduard Zingerman 
65171cf4d02SEduard Zingerman 	*cnt = xlated_prog_len / buf_element_size;
65271cf4d02SEduard Zingerman 	*buf = calloc(*cnt, buf_element_size);
65371cf4d02SEduard Zingerman 	if (!buf) {
65471cf4d02SEduard Zingerman 		perror("can't allocate xlated program buffer");
65571cf4d02SEduard Zingerman 		return -ENOMEM;
65671cf4d02SEduard Zingerman 	}
65771cf4d02SEduard Zingerman 
65871cf4d02SEduard Zingerman 	bzero(&info, sizeof(info));
65971cf4d02SEduard Zingerman 	info.xlated_prog_len = xlated_prog_len;
66071cf4d02SEduard Zingerman 	info.xlated_prog_insns = (__u64)(unsigned long)*buf;
66171cf4d02SEduard Zingerman 	if (bpf_prog_get_info_by_fd(fd_prog, &info, &info_len)) {
66271cf4d02SEduard Zingerman 		perror("second bpf_prog_get_info_by_fd failed");
66371cf4d02SEduard Zingerman 		goto out_free_buf;
66471cf4d02SEduard Zingerman 	}
66571cf4d02SEduard Zingerman 
66671cf4d02SEduard Zingerman 	return 0;
66771cf4d02SEduard Zingerman 
66871cf4d02SEduard Zingerman out_free_buf:
66971cf4d02SEduard Zingerman 	free(*buf);
67071cf4d02SEduard Zingerman 	return -1;
67171cf4d02SEduard Zingerman }
67271cf4d02SEduard Zingerman 
print_insn(void * private_data,const char * fmt,...)67371cf4d02SEduard Zingerman static void print_insn(void *private_data, const char *fmt, ...)
67471cf4d02SEduard Zingerman {
67571cf4d02SEduard Zingerman 	va_list args;
67671cf4d02SEduard Zingerman 
67771cf4d02SEduard Zingerman 	va_start(args, fmt);
67871cf4d02SEduard Zingerman 	vfprintf((FILE *)private_data, fmt, args);
67971cf4d02SEduard Zingerman 	va_end(args);
68071cf4d02SEduard Zingerman }
68171cf4d02SEduard Zingerman 
68271cf4d02SEduard Zingerman /* Disassemble instructions to a stream */
print_xlated(FILE * out,struct bpf_insn * insn,__u32 len)68371cf4d02SEduard Zingerman static void print_xlated(FILE *out, struct bpf_insn *insn, __u32 len)
68471cf4d02SEduard Zingerman {
68571cf4d02SEduard Zingerman 	const struct bpf_insn_cbs cbs = {
68671cf4d02SEduard Zingerman 		.cb_print	= print_insn,
68771cf4d02SEduard Zingerman 		.cb_call	= NULL,
68871cf4d02SEduard Zingerman 		.cb_imm		= NULL,
68971cf4d02SEduard Zingerman 		.private_data	= out,
69071cf4d02SEduard Zingerman 	};
69171cf4d02SEduard Zingerman 	bool double_insn = false;
69271cf4d02SEduard Zingerman 	int i;
69371cf4d02SEduard Zingerman 
69471cf4d02SEduard Zingerman 	for (i = 0; i < len; i++) {
69571cf4d02SEduard Zingerman 		if (double_insn) {
69671cf4d02SEduard Zingerman 			double_insn = false;
69771cf4d02SEduard Zingerman 			continue;
69871cf4d02SEduard Zingerman 		}
69971cf4d02SEduard Zingerman 
70071cf4d02SEduard Zingerman 		double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
70171cf4d02SEduard Zingerman 		print_bpf_insn(&cbs, insn + i, true);
70271cf4d02SEduard Zingerman 	}
70371cf4d02SEduard Zingerman }
70471cf4d02SEduard Zingerman 
70571cf4d02SEduard Zingerman /* We share code with kernel BPF disassembler, it adds '(FF) ' prefix
70671cf4d02SEduard Zingerman  * for each instruction (FF stands for instruction `code` byte).
70771cf4d02SEduard Zingerman  * This function removes the prefix inplace for each line in `str`.
70871cf4d02SEduard Zingerman  */
remove_insn_prefix(char * str,int size)70971cf4d02SEduard Zingerman static void remove_insn_prefix(char *str, int size)
71071cf4d02SEduard Zingerman {
71171cf4d02SEduard Zingerman 	const int prefix_size = 5;
71271cf4d02SEduard Zingerman 
71371cf4d02SEduard Zingerman 	int write_pos = 0, read_pos = prefix_size;
71471cf4d02SEduard Zingerman 	int len = strlen(str);
71571cf4d02SEduard Zingerman 	char c;
71671cf4d02SEduard Zingerman 
71771cf4d02SEduard Zingerman 	size = min(size, len);
71871cf4d02SEduard Zingerman 
71971cf4d02SEduard Zingerman 	while (read_pos < size) {
72071cf4d02SEduard Zingerman 		c = str[read_pos++];
72171cf4d02SEduard Zingerman 		if (c == 0)
72271cf4d02SEduard Zingerman 			break;
72371cf4d02SEduard Zingerman 		str[write_pos++] = c;
72471cf4d02SEduard Zingerman 		if (c == '\n')
72571cf4d02SEduard Zingerman 			read_pos += prefix_size;
72671cf4d02SEduard Zingerman 	}
72771cf4d02SEduard Zingerman 	str[write_pos] = 0;
72871cf4d02SEduard Zingerman }
72971cf4d02SEduard Zingerman 
73071cf4d02SEduard Zingerman struct prog_info {
73171cf4d02SEduard Zingerman 	char *prog_kind;
73271cf4d02SEduard Zingerman 	enum bpf_prog_type prog_type;
73371cf4d02SEduard Zingerman 	enum bpf_attach_type expected_attach_type;
73471cf4d02SEduard Zingerman 	struct bpf_insn *prog;
73571cf4d02SEduard Zingerman 	u32 prog_len;
73671cf4d02SEduard Zingerman };
73771cf4d02SEduard Zingerman 
match_program(struct btf * btf,struct prog_info * pinfo,char * pattern,char * reg_map[][2],bool skip_first_insn)73871cf4d02SEduard Zingerman static void match_program(struct btf *btf,
73971cf4d02SEduard Zingerman 			  struct prog_info *pinfo,
74071cf4d02SEduard Zingerman 			  char *pattern,
74171cf4d02SEduard Zingerman 			  char *reg_map[][2],
74271cf4d02SEduard Zingerman 			  bool skip_first_insn)
74371cf4d02SEduard Zingerman {
74471cf4d02SEduard Zingerman 	struct bpf_insn *buf = NULL;
74571cf4d02SEduard Zingerman 	int err = 0, prog_fd = 0;
74671cf4d02SEduard Zingerman 	FILE *prog_out = NULL;
74771cf4d02SEduard Zingerman 	char *text = NULL;
74871cf4d02SEduard Zingerman 	__u32 cnt = 0;
74971cf4d02SEduard Zingerman 
75071cf4d02SEduard Zingerman 	text = calloc(MAX_PROG_TEXT_SZ, 1);
75171cf4d02SEduard Zingerman 	if (!text) {
75271cf4d02SEduard Zingerman 		PRINT_FAIL("Can't allocate %d bytes\n", MAX_PROG_TEXT_SZ);
75371cf4d02SEduard Zingerman 		goto out;
75471cf4d02SEduard Zingerman 	}
75571cf4d02SEduard Zingerman 
75671cf4d02SEduard Zingerman 	// TODO: log level
75771cf4d02SEduard Zingerman 	LIBBPF_OPTS(bpf_prog_load_opts, opts);
75871cf4d02SEduard Zingerman 	opts.log_buf = text;
75971cf4d02SEduard Zingerman 	opts.log_size = MAX_PROG_TEXT_SZ;
76071cf4d02SEduard Zingerman 	opts.log_level = 1 | 2 | 4;
76171cf4d02SEduard Zingerman 	opts.expected_attach_type = pinfo->expected_attach_type;
76271cf4d02SEduard Zingerman 
76371cf4d02SEduard Zingerman 	prog_fd = bpf_prog_load(pinfo->prog_type, NULL, "GPL",
76471cf4d02SEduard Zingerman 				pinfo->prog, pinfo->prog_len, &opts);
76571cf4d02SEduard Zingerman 	if (prog_fd < 0) {
76671cf4d02SEduard Zingerman 		PRINT_FAIL("Can't load program, errno %d (%s), verifier log:\n%s\n",
76771cf4d02SEduard Zingerman 			   errno, strerror(errno), text);
76871cf4d02SEduard Zingerman 		goto out;
76971cf4d02SEduard Zingerman 	}
77071cf4d02SEduard Zingerman 
77171cf4d02SEduard Zingerman 	memset(text, 0, MAX_PROG_TEXT_SZ);
77271cf4d02SEduard Zingerman 
77371cf4d02SEduard Zingerman 	err = get_xlated_program(prog_fd, &buf, &cnt);
77471cf4d02SEduard Zingerman 	if (err) {
77571cf4d02SEduard Zingerman 		PRINT_FAIL("Can't load back BPF program\n");
77671cf4d02SEduard Zingerman 		goto out;
77771cf4d02SEduard Zingerman 	}
77871cf4d02SEduard Zingerman 
77971cf4d02SEduard Zingerman 	prog_out = fmemopen(text, MAX_PROG_TEXT_SZ - 1, "w");
78071cf4d02SEduard Zingerman 	if (!prog_out) {
78171cf4d02SEduard Zingerman 		PRINT_FAIL("Can't open memory stream\n");
78271cf4d02SEduard Zingerman 		goto out;
78371cf4d02SEduard Zingerman 	}
78471cf4d02SEduard Zingerman 	if (skip_first_insn)
78571cf4d02SEduard Zingerman 		print_xlated(prog_out, buf + 1, cnt - 1);
78671cf4d02SEduard Zingerman 	else
78771cf4d02SEduard Zingerman 		print_xlated(prog_out, buf, cnt);
78871cf4d02SEduard Zingerman 	fclose(prog_out);
78971cf4d02SEduard Zingerman 	remove_insn_prefix(text, MAX_PROG_TEXT_SZ);
79071cf4d02SEduard Zingerman 
79171cf4d02SEduard Zingerman 	ASSERT_TRUE(match_pattern(btf, pattern, text, reg_map),
79271cf4d02SEduard Zingerman 		    pinfo->prog_kind);
79371cf4d02SEduard Zingerman 
79471cf4d02SEduard Zingerman out:
79571cf4d02SEduard Zingerman 	if (prog_fd)
79671cf4d02SEduard Zingerman 		close(prog_fd);
79771cf4d02SEduard Zingerman 	free(buf);
79871cf4d02SEduard Zingerman 	free(text);
79971cf4d02SEduard Zingerman }
80071cf4d02SEduard Zingerman 
run_one_testcase(struct btf * btf,struct test_case * test)80171cf4d02SEduard Zingerman static void run_one_testcase(struct btf *btf, struct test_case *test)
80271cf4d02SEduard Zingerman {
80371cf4d02SEduard Zingerman 	struct prog_info pinfo = {};
80471cf4d02SEduard Zingerman 	int bpf_sz;
80571cf4d02SEduard Zingerman 
80671cf4d02SEduard Zingerman 	if (!test__start_subtest(test->name))
80771cf4d02SEduard Zingerman 		return;
80871cf4d02SEduard Zingerman 
80971cf4d02SEduard Zingerman 	switch (test->field_sz) {
81071cf4d02SEduard Zingerman 	case 8:
81171cf4d02SEduard Zingerman 		bpf_sz = BPF_DW;
81271cf4d02SEduard Zingerman 		break;
81371cf4d02SEduard Zingerman 	case 4:
81471cf4d02SEduard Zingerman 		bpf_sz = BPF_W;
81571cf4d02SEduard Zingerman 		break;
81671cf4d02SEduard Zingerman 	case 2:
81771cf4d02SEduard Zingerman 		bpf_sz = BPF_H;
81871cf4d02SEduard Zingerman 		break;
81971cf4d02SEduard Zingerman 	case 1:
82071cf4d02SEduard Zingerman 		bpf_sz = BPF_B;
82171cf4d02SEduard Zingerman 		break;
82271cf4d02SEduard Zingerman 	default:
82371cf4d02SEduard Zingerman 		PRINT_FAIL("Unexpected field size: %d, want 8,4,2 or 1\n", test->field_sz);
82471cf4d02SEduard Zingerman 		return;
82571cf4d02SEduard Zingerman 	}
82671cf4d02SEduard Zingerman 
82771cf4d02SEduard Zingerman 	pinfo.prog_type = test->prog_type;
82871cf4d02SEduard Zingerman 	pinfo.expected_attach_type = test->expected_attach_type;
82971cf4d02SEduard Zingerman 
83071cf4d02SEduard Zingerman 	if (test->read) {
83171cf4d02SEduard Zingerman 		struct bpf_insn ldx_prog[] = {
83271cf4d02SEduard Zingerman 			BPF_LDX_MEM(bpf_sz, BPF_REG_2, BPF_REG_1, test->field_offset),
83371cf4d02SEduard Zingerman 			BPF_MOV64_IMM(BPF_REG_0, 0),
83471cf4d02SEduard Zingerman 			BPF_EXIT_INSN(),
83571cf4d02SEduard Zingerman 		};
83671cf4d02SEduard Zingerman 		char *reg_map[][2] = {
83771cf4d02SEduard Zingerman 			{ "$ctx", "r1" },
83871cf4d02SEduard Zingerman 			{ "$dst", "r2" },
83971cf4d02SEduard Zingerman 			{}
84071cf4d02SEduard Zingerman 		};
84171cf4d02SEduard Zingerman 
84271cf4d02SEduard Zingerman 		pinfo.prog_kind = "LDX";
84371cf4d02SEduard Zingerman 		pinfo.prog = ldx_prog;
84471cf4d02SEduard Zingerman 		pinfo.prog_len = ARRAY_SIZE(ldx_prog);
84571cf4d02SEduard Zingerman 		match_program(btf, &pinfo, test->read, reg_map, false);
84671cf4d02SEduard Zingerman 	}
84771cf4d02SEduard Zingerman 
84871cf4d02SEduard Zingerman 	if (test->write || test->write_st || test->write_stx) {
84971cf4d02SEduard Zingerman 		struct bpf_insn stx_prog[] = {
85071cf4d02SEduard Zingerman 			BPF_MOV64_IMM(BPF_REG_2, 0),
85171cf4d02SEduard Zingerman 			BPF_STX_MEM(bpf_sz, BPF_REG_1, BPF_REG_2, test->field_offset),
85271cf4d02SEduard Zingerman 			BPF_MOV64_IMM(BPF_REG_0, 0),
85371cf4d02SEduard Zingerman 			BPF_EXIT_INSN(),
85471cf4d02SEduard Zingerman 		};
85571cf4d02SEduard Zingerman 		char *stx_reg_map[][2] = {
85671cf4d02SEduard Zingerman 			{ "$ctx", "r1" },
85771cf4d02SEduard Zingerman 			{ "$src", "r2" },
85871cf4d02SEduard Zingerman 			{}
85971cf4d02SEduard Zingerman 		};
86071cf4d02SEduard Zingerman 		struct bpf_insn st_prog[] = {
86171cf4d02SEduard Zingerman 			BPF_ST_MEM(bpf_sz, BPF_REG_1, test->field_offset,
86271cf4d02SEduard Zingerman 				   test->st_value.use ? test->st_value.value : 42),
86371cf4d02SEduard Zingerman 			BPF_MOV64_IMM(BPF_REG_0, 0),
86471cf4d02SEduard Zingerman 			BPF_EXIT_INSN(),
86571cf4d02SEduard Zingerman 		};
86671cf4d02SEduard Zingerman 		char *st_reg_map[][2] = {
86771cf4d02SEduard Zingerman 			{ "$ctx", "r1" },
86871cf4d02SEduard Zingerman 			{ "$src", "42" },
86971cf4d02SEduard Zingerman 			{}
87071cf4d02SEduard Zingerman 		};
87171cf4d02SEduard Zingerman 
87271cf4d02SEduard Zingerman 		if (test->write || test->write_stx) {
87371cf4d02SEduard Zingerman 			char *pattern = test->write_stx ? test->write_stx : test->write;
87471cf4d02SEduard Zingerman 
87571cf4d02SEduard Zingerman 			pinfo.prog_kind = "STX";
87671cf4d02SEduard Zingerman 			pinfo.prog = stx_prog;
87771cf4d02SEduard Zingerman 			pinfo.prog_len = ARRAY_SIZE(stx_prog);
87871cf4d02SEduard Zingerman 			match_program(btf, &pinfo, pattern, stx_reg_map, true);
87971cf4d02SEduard Zingerman 		}
88071cf4d02SEduard Zingerman 
88171cf4d02SEduard Zingerman 		if (test->write || test->write_st) {
88271cf4d02SEduard Zingerman 			char *pattern = test->write_st ? test->write_st : test->write;
88371cf4d02SEduard Zingerman 
88471cf4d02SEduard Zingerman 			pinfo.prog_kind = "ST";
88571cf4d02SEduard Zingerman 			pinfo.prog = st_prog;
88671cf4d02SEduard Zingerman 			pinfo.prog_len = ARRAY_SIZE(st_prog);
88771cf4d02SEduard Zingerman 			match_program(btf, &pinfo, pattern, st_reg_map, false);
88871cf4d02SEduard Zingerman 		}
88971cf4d02SEduard Zingerman 	}
89071cf4d02SEduard Zingerman 
89171cf4d02SEduard Zingerman 	test__end_subtest();
89271cf4d02SEduard Zingerman }
89371cf4d02SEduard Zingerman 
test_ctx_rewrite(void)89471cf4d02SEduard Zingerman void test_ctx_rewrite(void)
89571cf4d02SEduard Zingerman {
89671cf4d02SEduard Zingerman 	struct btf *btf;
89771cf4d02SEduard Zingerman 	int i;
89871cf4d02SEduard Zingerman 
89971cf4d02SEduard Zingerman 	field_regex = compile_regex("^([[:alpha:]_][[:alnum:]_]+)::([[:alpha:]_][[:alnum:]_]+)");
90071cf4d02SEduard Zingerman 	ident_regex = compile_regex("^[[:alpha:]_][[:alnum:]_]+");
90171cf4d02SEduard Zingerman 	if (!field_regex || !ident_regex)
90271cf4d02SEduard Zingerman 		return;
90371cf4d02SEduard Zingerman 
90471cf4d02SEduard Zingerman 	btf = btf__load_vmlinux_btf();
90571cf4d02SEduard Zingerman 	if (!btf) {
90671cf4d02SEduard Zingerman 		PRINT_FAIL("Can't load vmlinux BTF, errno %d (%s)\n", errno, strerror(errno));
90771cf4d02SEduard Zingerman 		goto out;
90871cf4d02SEduard Zingerman 	}
90971cf4d02SEduard Zingerman 
91071cf4d02SEduard Zingerman 	for (i = 0; i < ARRAY_SIZE(test_cases); ++i)
91171cf4d02SEduard Zingerman 		run_one_testcase(btf, &test_cases[i]);
91271cf4d02SEduard Zingerman 
91371cf4d02SEduard Zingerman out:
91471cf4d02SEduard Zingerman 	btf__free(btf);
91571cf4d02SEduard Zingerman 	free_regex(field_regex);
91671cf4d02SEduard Zingerman 	free_regex(ident_regex);
91771cf4d02SEduard Zingerman }
918