xref: /linux/tools/testing/selftests/bpf/prog_tests/bpf_insn_array.c (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
1218edd6dSAnton Protopopov // SPDX-License-Identifier: GPL-2.0
2218edd6dSAnton Protopopov 
3218edd6dSAnton Protopopov #include <bpf/bpf.h>
4218edd6dSAnton Protopopov #include <test_progs.h>
5218edd6dSAnton Protopopov 
6218edd6dSAnton Protopopov #ifdef __x86_64__
7218edd6dSAnton Protopopov static int map_create(__u32 map_type, __u32 max_entries)
8218edd6dSAnton Protopopov {
9218edd6dSAnton Protopopov 	const char *map_name = "insn_array";
10218edd6dSAnton Protopopov 	__u32 key_size = 4;
11218edd6dSAnton Protopopov 	__u32 value_size = sizeof(struct bpf_insn_array_value);
12218edd6dSAnton Protopopov 
13218edd6dSAnton Protopopov 	return bpf_map_create(map_type, map_name, key_size, value_size, max_entries, NULL);
14218edd6dSAnton Protopopov }
15218edd6dSAnton Protopopov 
16218edd6dSAnton Protopopov static int prog_load(struct bpf_insn *insns, __u32 insn_cnt, int *fd_array, __u32 fd_array_cnt)
17218edd6dSAnton Protopopov {
18218edd6dSAnton Protopopov 	LIBBPF_OPTS(bpf_prog_load_opts, opts);
19218edd6dSAnton Protopopov 
20218edd6dSAnton Protopopov 	opts.fd_array = fd_array;
21218edd6dSAnton Protopopov 	opts.fd_array_cnt = fd_array_cnt;
22218edd6dSAnton Protopopov 
23218edd6dSAnton Protopopov 	return bpf_prog_load(BPF_PROG_TYPE_XDP, NULL, "GPL", insns, insn_cnt, &opts);
24218edd6dSAnton Protopopov }
25218edd6dSAnton Protopopov 
26218edd6dSAnton Protopopov static void __check_success(struct bpf_insn *insns, __u32 insn_cnt, __u32 *map_in, __u32 *map_out)
27218edd6dSAnton Protopopov {
28218edd6dSAnton Protopopov 	struct bpf_insn_array_value val = {};
29218edd6dSAnton Protopopov 	int prog_fd = -1, map_fd, i;
30218edd6dSAnton Protopopov 
31218edd6dSAnton Protopopov 	map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, insn_cnt);
32218edd6dSAnton Protopopov 	if (!ASSERT_GE(map_fd, 0, "map_create"))
33218edd6dSAnton Protopopov 		return;
34218edd6dSAnton Protopopov 
35218edd6dSAnton Protopopov 	for (i = 0; i < insn_cnt; i++) {
36218edd6dSAnton Protopopov 		val.orig_off = map_in[i];
37218edd6dSAnton Protopopov 		if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &val, 0), 0, "bpf_map_update_elem"))
38218edd6dSAnton Protopopov 			goto cleanup;
39218edd6dSAnton Protopopov 	}
40218edd6dSAnton Protopopov 
41218edd6dSAnton Protopopov 	if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze"))
42218edd6dSAnton Protopopov 		goto cleanup;
43218edd6dSAnton Protopopov 
44218edd6dSAnton Protopopov 	prog_fd = prog_load(insns, insn_cnt, &map_fd, 1);
45218edd6dSAnton Protopopov 	if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)"))
46218edd6dSAnton Protopopov 		goto cleanup;
47218edd6dSAnton Protopopov 
48218edd6dSAnton Protopopov 	for (i = 0; i < insn_cnt; i++) {
49218edd6dSAnton Protopopov 		char buf[64];
50218edd6dSAnton Protopopov 
51218edd6dSAnton Protopopov 		if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem"))
52218edd6dSAnton Protopopov 			goto cleanup;
53218edd6dSAnton Protopopov 
54218edd6dSAnton Protopopov 		snprintf(buf, sizeof(buf), "val.xlated_off should be equal map_out[%d]", i);
55218edd6dSAnton Protopopov 		ASSERT_EQ(val.xlated_off, map_out[i], buf);
56218edd6dSAnton Protopopov 	}
57218edd6dSAnton Protopopov 
58218edd6dSAnton Protopopov cleanup:
59218edd6dSAnton Protopopov 	close(prog_fd);
60218edd6dSAnton Protopopov 	close(map_fd);
61218edd6dSAnton Protopopov }
62218edd6dSAnton Protopopov 
63218edd6dSAnton Protopopov /*
64218edd6dSAnton Protopopov  * Load a program, which will not be anyhow mangled by the verifier.  Add an
65218edd6dSAnton Protopopov  * insn_array map pointing to every instruction. Check that it hasn't changed
66218edd6dSAnton Protopopov  * after the program load.
67218edd6dSAnton Protopopov  */
68218edd6dSAnton Protopopov static void check_one_to_one_mapping(void)
69218edd6dSAnton Protopopov {
70218edd6dSAnton Protopopov 	struct bpf_insn insns[] = {
71218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 4),
72218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 3),
73218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 2),
74218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 1),
75218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 0),
76218edd6dSAnton Protopopov 		BPF_EXIT_INSN(),
77218edd6dSAnton Protopopov 	};
78218edd6dSAnton Protopopov 	__u32 map_in[] = {0, 1, 2, 3, 4, 5};
79218edd6dSAnton Protopopov 	__u32 map_out[] = {0, 1, 2, 3, 4, 5};
80218edd6dSAnton Protopopov 
81218edd6dSAnton Protopopov 	__check_success(insns, ARRAY_SIZE(insns), map_in, map_out);
82218edd6dSAnton Protopopov }
83218edd6dSAnton Protopopov 
84218edd6dSAnton Protopopov /*
85218edd6dSAnton Protopopov  * Load a program with two patches (get jiffies, for simplicity). Add an
86218edd6dSAnton Protopopov  * insn_array map pointing to every instruction. Check how it was changed
87218edd6dSAnton Protopopov  * after the program load.
88218edd6dSAnton Protopopov  */
89218edd6dSAnton Protopopov static void check_simple(void)
90218edd6dSAnton Protopopov {
91218edd6dSAnton Protopopov 	struct bpf_insn insns[] = {
92218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 2),
93218edd6dSAnton Protopopov 		BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64),
94218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 1),
95218edd6dSAnton Protopopov 		BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64),
96218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 0),
97218edd6dSAnton Protopopov 		BPF_EXIT_INSN(),
98218edd6dSAnton Protopopov 	};
99218edd6dSAnton Protopopov 	__u32 map_in[] = {0, 1, 2, 3, 4, 5};
100218edd6dSAnton Protopopov 	__u32 map_out[] = {0, 1, 4, 5, 8, 9};
101218edd6dSAnton Protopopov 
102218edd6dSAnton Protopopov 	__check_success(insns, ARRAY_SIZE(insns), map_in, map_out);
103218edd6dSAnton Protopopov }
104218edd6dSAnton Protopopov 
105218edd6dSAnton Protopopov /*
106218edd6dSAnton Protopopov  * Verifier can delete code in two cases: nops & dead code. From insn
107218edd6dSAnton Protopopov  * array's point of view, the two cases are the same, so test using
108218edd6dSAnton Protopopov  * the simplest method: by loading some nops
109218edd6dSAnton Protopopov  */
110218edd6dSAnton Protopopov static void check_deletions(void)
111218edd6dSAnton Protopopov {
112218edd6dSAnton Protopopov 	struct bpf_insn insns[] = {
113218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 2),
114218edd6dSAnton Protopopov 		BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */
115218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 1),
116218edd6dSAnton Protopopov 		BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */
117218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 0),
118218edd6dSAnton Protopopov 		BPF_EXIT_INSN(),
119218edd6dSAnton Protopopov 	};
120218edd6dSAnton Protopopov 	__u32 map_in[] = {0, 1, 2, 3, 4, 5};
121218edd6dSAnton Protopopov 	__u32 map_out[] = {0, -1, 1, -1, 2, 3};
122218edd6dSAnton Protopopov 
123218edd6dSAnton Protopopov 	__check_success(insns, ARRAY_SIZE(insns), map_in, map_out);
124218edd6dSAnton Protopopov }
125218edd6dSAnton Protopopov 
126218edd6dSAnton Protopopov /*
127218edd6dSAnton Protopopov  * Same test as check_deletions, but also add code which adds instructions
128218edd6dSAnton Protopopov  */
129218edd6dSAnton Protopopov static void check_deletions_with_functions(void)
130218edd6dSAnton Protopopov {
131218edd6dSAnton Protopopov 	struct bpf_insn insns[] = {
132218edd6dSAnton Protopopov 		BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */
133218edd6dSAnton Protopopov 		BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64),
134218edd6dSAnton Protopopov 		BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */
135218edd6dSAnton Protopopov 		BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 1, 0, 2),
136218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 1),
137218edd6dSAnton Protopopov 		BPF_EXIT_INSN(),
138218edd6dSAnton Protopopov 		BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */
139218edd6dSAnton Protopopov 		BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64),
140218edd6dSAnton Protopopov 		BPF_JMP_IMM(BPF_JA, 0, 0, 0), /* nop */
141218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 2),
142218edd6dSAnton Protopopov 		BPF_EXIT_INSN(),
143218edd6dSAnton Protopopov 	};
144218edd6dSAnton Protopopov 	__u32 map_in[] =  { 0, 1,  2, 3, 4, 5, /* func */  6, 7,  8, 9, 10};
145218edd6dSAnton Protopopov 	__u32 map_out[] = {-1, 0, -1, 3, 4, 5, /* func */ -1, 6, -1, 9, 10};
146218edd6dSAnton Protopopov 
147218edd6dSAnton Protopopov 	__check_success(insns, ARRAY_SIZE(insns), map_in, map_out);
148218edd6dSAnton Protopopov }
149218edd6dSAnton Protopopov 
150218edd6dSAnton Protopopov /*
151218edd6dSAnton Protopopov  * Try to load a program with a map which points to outside of the program
152218edd6dSAnton Protopopov  */
153218edd6dSAnton Protopopov static void check_out_of_bounds_index(void)
154218edd6dSAnton Protopopov {
155218edd6dSAnton Protopopov 	struct bpf_insn insns[] = {
156218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 4),
157218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 3),
158218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 2),
159218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 1),
160218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 0),
161218edd6dSAnton Protopopov 		BPF_EXIT_INSN(),
162218edd6dSAnton Protopopov 	};
163218edd6dSAnton Protopopov 	int prog_fd, map_fd;
164218edd6dSAnton Protopopov 	struct bpf_insn_array_value val = {};
165218edd6dSAnton Protopopov 	int key;
166218edd6dSAnton Protopopov 
167218edd6dSAnton Protopopov 	map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, 1);
168218edd6dSAnton Protopopov 	if (!ASSERT_GE(map_fd, 0, "map_create"))
169218edd6dSAnton Protopopov 		return;
170218edd6dSAnton Protopopov 
171218edd6dSAnton Protopopov 	key = 0;
172218edd6dSAnton Protopopov 	val.orig_off = ARRAY_SIZE(insns); /* too big */
173218edd6dSAnton Protopopov 	if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &key, &val, 0), 0, "bpf_map_update_elem"))
174218edd6dSAnton Protopopov 		goto cleanup;
175218edd6dSAnton Protopopov 
176218edd6dSAnton Protopopov 	if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze"))
177218edd6dSAnton Protopopov 		goto cleanup;
178218edd6dSAnton Protopopov 
179218edd6dSAnton Protopopov 	prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1);
180218edd6dSAnton Protopopov 	if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)")) {
181218edd6dSAnton Protopopov 		close(prog_fd);
182218edd6dSAnton Protopopov 		goto cleanup;
183218edd6dSAnton Protopopov 	}
184218edd6dSAnton Protopopov 
185218edd6dSAnton Protopopov cleanup:
186218edd6dSAnton Protopopov 	close(map_fd);
187218edd6dSAnton Protopopov }
188218edd6dSAnton Protopopov 
189218edd6dSAnton Protopopov /*
190218edd6dSAnton Protopopov  * Try to load a program with a map which points to the middle of 16-bit insn
191218edd6dSAnton Protopopov  */
192218edd6dSAnton Protopopov static void check_mid_insn_index(void)
193218edd6dSAnton Protopopov {
194218edd6dSAnton Protopopov 	struct bpf_insn insns[] = {
195218edd6dSAnton Protopopov 		BPF_LD_IMM64(BPF_REG_0, 0), /* 2 x 8 */
196218edd6dSAnton Protopopov 		BPF_EXIT_INSN(),
197218edd6dSAnton Protopopov 	};
198218edd6dSAnton Protopopov 	int prog_fd, map_fd;
199218edd6dSAnton Protopopov 	struct bpf_insn_array_value val = {};
200218edd6dSAnton Protopopov 	int key;
201218edd6dSAnton Protopopov 
202218edd6dSAnton Protopopov 	map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, 1);
203218edd6dSAnton Protopopov 	if (!ASSERT_GE(map_fd, 0, "map_create"))
204218edd6dSAnton Protopopov 		return;
205218edd6dSAnton Protopopov 
206218edd6dSAnton Protopopov 	key = 0;
207218edd6dSAnton Protopopov 	val.orig_off = 1; /* middle of 16-byte instruction */
208218edd6dSAnton Protopopov 	if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &key, &val, 0), 0, "bpf_map_update_elem"))
209218edd6dSAnton Protopopov 		goto cleanup;
210218edd6dSAnton Protopopov 
211218edd6dSAnton Protopopov 	if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze"))
212218edd6dSAnton Protopopov 		goto cleanup;
213218edd6dSAnton Protopopov 
214218edd6dSAnton Protopopov 	prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1);
215218edd6dSAnton Protopopov 	if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)")) {
216218edd6dSAnton Protopopov 		close(prog_fd);
217218edd6dSAnton Protopopov 		goto cleanup;
218218edd6dSAnton Protopopov 	}
219218edd6dSAnton Protopopov 
220218edd6dSAnton Protopopov cleanup:
221218edd6dSAnton Protopopov 	close(map_fd);
222218edd6dSAnton Protopopov }
223218edd6dSAnton Protopopov 
224218edd6dSAnton Protopopov static void check_incorrect_index(void)
225218edd6dSAnton Protopopov {
226218edd6dSAnton Protopopov 	check_out_of_bounds_index();
227218edd6dSAnton Protopopov 	check_mid_insn_index();
228218edd6dSAnton Protopopov }
229218edd6dSAnton Protopopov 
230*ae48162aSAnton Protopopov static int set_bpf_jit_harden(char *level)
231*ae48162aSAnton Protopopov {
232*ae48162aSAnton Protopopov 	char old_level;
233*ae48162aSAnton Protopopov 	int err = -1;
234*ae48162aSAnton Protopopov 	int fd = -1;
235*ae48162aSAnton Protopopov 
236*ae48162aSAnton Protopopov 	fd = open("/proc/sys/net/core/bpf_jit_harden", O_RDWR | O_NONBLOCK);
237*ae48162aSAnton Protopopov 	if (fd < 0) {
238*ae48162aSAnton Protopopov 		ASSERT_FAIL("open .../bpf_jit_harden returned %d (errno=%d)", fd, errno);
239*ae48162aSAnton Protopopov 		return -1;
240*ae48162aSAnton Protopopov 	}
241*ae48162aSAnton Protopopov 
242*ae48162aSAnton Protopopov 	err = read(fd, &old_level, 1);
243*ae48162aSAnton Protopopov 	if (err != 1) {
244*ae48162aSAnton Protopopov 		ASSERT_FAIL("read from .../bpf_jit_harden returned %d (errno=%d)", err, errno);
245*ae48162aSAnton Protopopov 		err = -1;
246*ae48162aSAnton Protopopov 		goto end;
247*ae48162aSAnton Protopopov 	}
248*ae48162aSAnton Protopopov 
249*ae48162aSAnton Protopopov 	lseek(fd, 0, SEEK_SET);
250*ae48162aSAnton Protopopov 
251*ae48162aSAnton Protopopov 	err = write(fd, level, 1);
252*ae48162aSAnton Protopopov 	if (err != 1) {
253*ae48162aSAnton Protopopov 		ASSERT_FAIL("write to .../bpf_jit_harden returned %d (errno=%d)", err, errno);
254*ae48162aSAnton Protopopov 		err = -1;
255*ae48162aSAnton Protopopov 		goto end;
256*ae48162aSAnton Protopopov 	}
257*ae48162aSAnton Protopopov 
258*ae48162aSAnton Protopopov 	err = 0;
259*ae48162aSAnton Protopopov 	*level = old_level;
260*ae48162aSAnton Protopopov end:
261*ae48162aSAnton Protopopov 	if (fd >= 0)
262*ae48162aSAnton Protopopov 		close(fd);
263*ae48162aSAnton Protopopov 	return err;
264*ae48162aSAnton Protopopov }
265*ae48162aSAnton Protopopov 
266*ae48162aSAnton Protopopov static void check_blindness(void)
267*ae48162aSAnton Protopopov {
268*ae48162aSAnton Protopopov 	struct bpf_insn insns[] = {
269*ae48162aSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 4),
270*ae48162aSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 3),
271*ae48162aSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 2),
272*ae48162aSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 1),
273*ae48162aSAnton Protopopov 		BPF_EXIT_INSN(),
274*ae48162aSAnton Protopopov 	};
275*ae48162aSAnton Protopopov 	int prog_fd = -1, map_fd;
276*ae48162aSAnton Protopopov 	struct bpf_insn_array_value val = {};
277*ae48162aSAnton Protopopov 	char bpf_jit_harden = '@'; /* non-exizsting value */
278*ae48162aSAnton Protopopov 	int i;
279*ae48162aSAnton Protopopov 
280*ae48162aSAnton Protopopov 	map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, ARRAY_SIZE(insns));
281*ae48162aSAnton Protopopov 	if (!ASSERT_GE(map_fd, 0, "map_create"))
282*ae48162aSAnton Protopopov 		return;
283*ae48162aSAnton Protopopov 
284*ae48162aSAnton Protopopov 	for (i = 0; i < ARRAY_SIZE(insns); i++) {
285*ae48162aSAnton Protopopov 		val.orig_off = i;
286*ae48162aSAnton Protopopov 		if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &val, 0), 0, "bpf_map_update_elem"))
287*ae48162aSAnton Protopopov 			goto cleanup;
288*ae48162aSAnton Protopopov 	}
289*ae48162aSAnton Protopopov 
290*ae48162aSAnton Protopopov 	if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze"))
291*ae48162aSAnton Protopopov 		goto cleanup;
292*ae48162aSAnton Protopopov 
293*ae48162aSAnton Protopopov 	bpf_jit_harden = '2';
294*ae48162aSAnton Protopopov 	if (set_bpf_jit_harden(&bpf_jit_harden)) {
295*ae48162aSAnton Protopopov 		bpf_jit_harden = '@'; /* open, read or write failed => no write was done */
296*ae48162aSAnton Protopopov 		goto cleanup;
297*ae48162aSAnton Protopopov 	}
298*ae48162aSAnton Protopopov 
299*ae48162aSAnton Protopopov 	prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1);
300*ae48162aSAnton Protopopov 	if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)"))
301*ae48162aSAnton Protopopov 		goto cleanup;
302*ae48162aSAnton Protopopov 
303*ae48162aSAnton Protopopov 	for (i = 0; i < ARRAY_SIZE(insns); i++) {
304*ae48162aSAnton Protopopov 		char fmt[32];
305*ae48162aSAnton Protopopov 
306*ae48162aSAnton Protopopov 		if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem"))
307*ae48162aSAnton Protopopov 			goto cleanup;
308*ae48162aSAnton Protopopov 
309*ae48162aSAnton Protopopov 		snprintf(fmt, sizeof(fmt), "val should be equal 3*%d", i);
310*ae48162aSAnton Protopopov 		ASSERT_EQ(val.xlated_off, i * 3, fmt);
311*ae48162aSAnton Protopopov 	}
312*ae48162aSAnton Protopopov 
313*ae48162aSAnton Protopopov cleanup:
314*ae48162aSAnton Protopopov 	/* restore the old one */
315*ae48162aSAnton Protopopov 	if (bpf_jit_harden != '@')
316*ae48162aSAnton Protopopov 		set_bpf_jit_harden(&bpf_jit_harden);
317*ae48162aSAnton Protopopov 
318*ae48162aSAnton Protopopov 	close(prog_fd);
319*ae48162aSAnton Protopopov 	close(map_fd);
320*ae48162aSAnton Protopopov }
321*ae48162aSAnton Protopopov 
322218edd6dSAnton Protopopov /* Once map was initialized, it should be frozen */
323218edd6dSAnton Protopopov static void check_load_unfrozen_map(void)
324218edd6dSAnton Protopopov {
325218edd6dSAnton Protopopov 	struct bpf_insn insns[] = {
326218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 0),
327218edd6dSAnton Protopopov 		BPF_EXIT_INSN(),
328218edd6dSAnton Protopopov 	};
329218edd6dSAnton Protopopov 	int prog_fd = -1, map_fd;
330218edd6dSAnton Protopopov 	struct bpf_insn_array_value val = {};
331218edd6dSAnton Protopopov 	int i;
332218edd6dSAnton Protopopov 
333218edd6dSAnton Protopopov 	map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, ARRAY_SIZE(insns));
334218edd6dSAnton Protopopov 	if (!ASSERT_GE(map_fd, 0, "map_create"))
335218edd6dSAnton Protopopov 		return;
336218edd6dSAnton Protopopov 
337218edd6dSAnton Protopopov 	for (i = 0; i < ARRAY_SIZE(insns); i++) {
338218edd6dSAnton Protopopov 		val.orig_off = i;
339218edd6dSAnton Protopopov 		if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &val, 0), 0, "bpf_map_update_elem"))
340218edd6dSAnton Protopopov 			goto cleanup;
341218edd6dSAnton Protopopov 	}
342218edd6dSAnton Protopopov 
343218edd6dSAnton Protopopov 	prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1);
344218edd6dSAnton Protopopov 	if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)"))
345218edd6dSAnton Protopopov 		goto cleanup;
346218edd6dSAnton Protopopov 
347218edd6dSAnton Protopopov 	/* correctness: now freeze the map, the program should load fine */
348218edd6dSAnton Protopopov 
349218edd6dSAnton Protopopov 	if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze"))
350218edd6dSAnton Protopopov 		goto cleanup;
351218edd6dSAnton Protopopov 
352218edd6dSAnton Protopopov 	prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1);
353218edd6dSAnton Protopopov 	if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)"))
354218edd6dSAnton Protopopov 		goto cleanup;
355218edd6dSAnton Protopopov 
356218edd6dSAnton Protopopov 	for (i = 0; i < ARRAY_SIZE(insns); i++) {
357218edd6dSAnton Protopopov 		if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem"))
358218edd6dSAnton Protopopov 			goto cleanup;
359218edd6dSAnton Protopopov 
360218edd6dSAnton Protopopov 		ASSERT_EQ(val.xlated_off, i, "val should be equal i");
361218edd6dSAnton Protopopov 	}
362218edd6dSAnton Protopopov 
363218edd6dSAnton Protopopov cleanup:
364218edd6dSAnton Protopopov 	close(prog_fd);
365218edd6dSAnton Protopopov 	close(map_fd);
366218edd6dSAnton Protopopov }
367218edd6dSAnton Protopopov 
368218edd6dSAnton Protopopov /* Map can be used only by one BPF program */
369218edd6dSAnton Protopopov static void check_no_map_reuse(void)
370218edd6dSAnton Protopopov {
371218edd6dSAnton Protopopov 	struct bpf_insn insns[] = {
372218edd6dSAnton Protopopov 		BPF_MOV64_IMM(BPF_REG_0, 0),
373218edd6dSAnton Protopopov 		BPF_EXIT_INSN(),
374218edd6dSAnton Protopopov 	};
375218edd6dSAnton Protopopov 	int prog_fd = -1, map_fd, extra_fd = -1;
376218edd6dSAnton Protopopov 	struct bpf_insn_array_value val = {};
377218edd6dSAnton Protopopov 	int i;
378218edd6dSAnton Protopopov 
379218edd6dSAnton Protopopov 	map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, ARRAY_SIZE(insns));
380218edd6dSAnton Protopopov 	if (!ASSERT_GE(map_fd, 0, "map_create"))
381218edd6dSAnton Protopopov 		return;
382218edd6dSAnton Protopopov 
383218edd6dSAnton Protopopov 	for (i = 0; i < ARRAY_SIZE(insns); i++) {
384218edd6dSAnton Protopopov 		val.orig_off = i;
385218edd6dSAnton Protopopov 		if (!ASSERT_EQ(bpf_map_update_elem(map_fd, &i, &val, 0), 0, "bpf_map_update_elem"))
386218edd6dSAnton Protopopov 			goto cleanup;
387218edd6dSAnton Protopopov 	}
388218edd6dSAnton Protopopov 
389218edd6dSAnton Protopopov 	if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze"))
390218edd6dSAnton Protopopov 		goto cleanup;
391218edd6dSAnton Protopopov 
392218edd6dSAnton Protopopov 	prog_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1);
393218edd6dSAnton Protopopov 	if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)"))
394218edd6dSAnton Protopopov 		goto cleanup;
395218edd6dSAnton Protopopov 
396218edd6dSAnton Protopopov 	for (i = 0; i < ARRAY_SIZE(insns); i++) {
397218edd6dSAnton Protopopov 		if (!ASSERT_EQ(bpf_map_lookup_elem(map_fd, &i, &val), 0, "bpf_map_lookup_elem"))
398218edd6dSAnton Protopopov 			goto cleanup;
399218edd6dSAnton Protopopov 
400218edd6dSAnton Protopopov 		ASSERT_EQ(val.xlated_off, i, "val should be equal i");
401218edd6dSAnton Protopopov 	}
402218edd6dSAnton Protopopov 
403218edd6dSAnton Protopopov 	extra_fd = prog_load(insns, ARRAY_SIZE(insns), &map_fd, 1);
404218edd6dSAnton Protopopov 	if (!ASSERT_EQ(extra_fd, -EBUSY, "program should have been rejected (extra_fd != -EBUSY)"))
405218edd6dSAnton Protopopov 		goto cleanup;
406218edd6dSAnton Protopopov 
407218edd6dSAnton Protopopov 	/* correctness: check that prog is still loadable without fd_array */
408218edd6dSAnton Protopopov 	extra_fd = prog_load(insns, ARRAY_SIZE(insns), NULL, 0);
409218edd6dSAnton Protopopov 	if (!ASSERT_GE(extra_fd, 0, "bpf(BPF_PROG_LOAD): expected no error"))
410218edd6dSAnton Protopopov 		goto cleanup;
411218edd6dSAnton Protopopov 
412218edd6dSAnton Protopopov cleanup:
413218edd6dSAnton Protopopov 	close(extra_fd);
414218edd6dSAnton Protopopov 	close(prog_fd);
415218edd6dSAnton Protopopov 	close(map_fd);
416218edd6dSAnton Protopopov }
417218edd6dSAnton Protopopov 
418218edd6dSAnton Protopopov static void check_bpf_no_lookup(void)
419218edd6dSAnton Protopopov {
420218edd6dSAnton Protopopov 	struct bpf_insn insns[] = {
421218edd6dSAnton Protopopov 		BPF_LD_MAP_FD(BPF_REG_1, 0),
422218edd6dSAnton Protopopov 		BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
423218edd6dSAnton Protopopov 		BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
424218edd6dSAnton Protopopov 		BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
425218edd6dSAnton Protopopov 		BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
426218edd6dSAnton Protopopov 		BPF_EXIT_INSN(),
427218edd6dSAnton Protopopov 	};
428218edd6dSAnton Protopopov 	int prog_fd = -1, map_fd;
429218edd6dSAnton Protopopov 
430218edd6dSAnton Protopopov 	map_fd = map_create(BPF_MAP_TYPE_INSN_ARRAY, 1);
431218edd6dSAnton Protopopov 	if (!ASSERT_GE(map_fd, 0, "map_create"))
432218edd6dSAnton Protopopov 		return;
433218edd6dSAnton Protopopov 
434218edd6dSAnton Protopopov 	insns[0].imm = map_fd;
435218edd6dSAnton Protopopov 
436218edd6dSAnton Protopopov 	if (!ASSERT_EQ(bpf_map_freeze(map_fd), 0, "bpf_map_freeze"))
437218edd6dSAnton Protopopov 		goto cleanup;
438218edd6dSAnton Protopopov 
439218edd6dSAnton Protopopov 	prog_fd = prog_load(insns, ARRAY_SIZE(insns), NULL, 0);
440218edd6dSAnton Protopopov 	if (!ASSERT_EQ(prog_fd, -EINVAL, "program should have been rejected (prog_fd != -EINVAL)"))
441218edd6dSAnton Protopopov 		goto cleanup;
442218edd6dSAnton Protopopov 
443218edd6dSAnton Protopopov 	/* correctness: check that prog is still loadable with normal map */
444218edd6dSAnton Protopopov 	close(map_fd);
445218edd6dSAnton Protopopov 	map_fd = map_create(BPF_MAP_TYPE_ARRAY, 1);
446218edd6dSAnton Protopopov 	insns[0].imm = map_fd;
447218edd6dSAnton Protopopov 	prog_fd = prog_load(insns, ARRAY_SIZE(insns), NULL, 0);
448218edd6dSAnton Protopopov 	if (!ASSERT_GE(prog_fd, 0, "bpf(BPF_PROG_LOAD)"))
449218edd6dSAnton Protopopov 		goto cleanup;
450218edd6dSAnton Protopopov 
451218edd6dSAnton Protopopov cleanup:
452218edd6dSAnton Protopopov 	close(prog_fd);
453218edd6dSAnton Protopopov 	close(map_fd);
454218edd6dSAnton Protopopov }
455218edd6dSAnton Protopopov 
456218edd6dSAnton Protopopov static void check_bpf_side(void)
457218edd6dSAnton Protopopov {
458218edd6dSAnton Protopopov 	check_bpf_no_lookup();
459218edd6dSAnton Protopopov }
460218edd6dSAnton Protopopov 
461218edd6dSAnton Protopopov static void __test_bpf_insn_array(void)
462218edd6dSAnton Protopopov {
463218edd6dSAnton Protopopov 	/* Test if offsets are adjusted properly */
464218edd6dSAnton Protopopov 
465218edd6dSAnton Protopopov 	if (test__start_subtest("one2one"))
466218edd6dSAnton Protopopov 		check_one_to_one_mapping();
467218edd6dSAnton Protopopov 
468218edd6dSAnton Protopopov 	if (test__start_subtest("simple"))
469218edd6dSAnton Protopopov 		check_simple();
470218edd6dSAnton Protopopov 
471218edd6dSAnton Protopopov 	if (test__start_subtest("deletions"))
472218edd6dSAnton Protopopov 		check_deletions();
473218edd6dSAnton Protopopov 
474218edd6dSAnton Protopopov 	if (test__start_subtest("deletions-with-functions"))
475218edd6dSAnton Protopopov 		check_deletions_with_functions();
476218edd6dSAnton Protopopov 
477*ae48162aSAnton Protopopov 	if (test__start_subtest("blindness"))
478*ae48162aSAnton Protopopov 		check_blindness();
479*ae48162aSAnton Protopopov 
480218edd6dSAnton Protopopov 	/* Check all kinds of operations and related restrictions */
481218edd6dSAnton Protopopov 
482218edd6dSAnton Protopopov 	if (test__start_subtest("incorrect-index"))
483218edd6dSAnton Protopopov 		check_incorrect_index();
484218edd6dSAnton Protopopov 
485218edd6dSAnton Protopopov 	if (test__start_subtest("load-unfrozen-map"))
486218edd6dSAnton Protopopov 		check_load_unfrozen_map();
487218edd6dSAnton Protopopov 
488218edd6dSAnton Protopopov 	if (test__start_subtest("no-map-reuse"))
489218edd6dSAnton Protopopov 		check_no_map_reuse();
490218edd6dSAnton Protopopov 
491218edd6dSAnton Protopopov 	if (test__start_subtest("bpf-side-ops"))
492218edd6dSAnton Protopopov 		check_bpf_side();
493218edd6dSAnton Protopopov }
494218edd6dSAnton Protopopov #else
495218edd6dSAnton Protopopov static void __test_bpf_insn_array(void)
496218edd6dSAnton Protopopov {
497218edd6dSAnton Protopopov 	test__skip();
498218edd6dSAnton Protopopov }
499218edd6dSAnton Protopopov #endif
500218edd6dSAnton Protopopov 
501218edd6dSAnton Protopopov void test_bpf_insn_array(void)
502218edd6dSAnton Protopopov {
503218edd6dSAnton Protopopov 	__test_bpf_insn_array();
504218edd6dSAnton Protopopov }
505