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