1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2019 Facebook
3 
4 #include <fcntl.h>
5 #include <stdint.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <unistd.h>
10 
11 #include <linux/filter.h>
12 
13 #include <bpf/bpf.h>
14 #include <bpf/libbpf.h>
15 
16 #include "bpf_endian.h"
17 #include "bpf_rlimit.h"
18 #include "bpf_util.h"
19 #include "cgroup_helpers.h"
20 
21 #define CG_PATH			"/foo"
22 #define MAX_INSNS		512
23 #define FIXUP_SYSCTL_VALUE	0
24 
25 char bpf_log_buf[BPF_LOG_BUF_SIZE];
26 
27 struct sysctl_test {
28 	const char *descr;
29 	size_t fixup_value_insn;
30 	struct bpf_insn	insns[MAX_INSNS];
31 	const char *prog_file;
32 	enum bpf_attach_type attach_type;
33 	const char *sysctl;
34 	int open_flags;
35 	int seek;
36 	const char *newval;
37 	const char *oldval;
38 	enum {
39 		LOAD_REJECT,
40 		ATTACH_REJECT,
41 		OP_EPERM,
42 		SUCCESS,
43 	} result;
44 };
45 
46 static struct sysctl_test tests[] = {
47 	{
48 		.descr = "sysctl wrong attach_type",
49 		.insns = {
50 			BPF_MOV64_IMM(BPF_REG_0, 1),
51 			BPF_EXIT_INSN(),
52 		},
53 		.attach_type = 0,
54 		.sysctl = "kernel/ostype",
55 		.open_flags = O_RDONLY,
56 		.result = ATTACH_REJECT,
57 	},
58 	{
59 		.descr = "sysctl:read allow all",
60 		.insns = {
61 			BPF_MOV64_IMM(BPF_REG_0, 1),
62 			BPF_EXIT_INSN(),
63 		},
64 		.attach_type = BPF_CGROUP_SYSCTL,
65 		.sysctl = "kernel/ostype",
66 		.open_flags = O_RDONLY,
67 		.result = SUCCESS,
68 	},
69 	{
70 		.descr = "sysctl:read deny all",
71 		.insns = {
72 			BPF_MOV64_IMM(BPF_REG_0, 0),
73 			BPF_EXIT_INSN(),
74 		},
75 		.attach_type = BPF_CGROUP_SYSCTL,
76 		.sysctl = "kernel/ostype",
77 		.open_flags = O_RDONLY,
78 		.result = OP_EPERM,
79 	},
80 	{
81 		.descr = "ctx:write sysctl:read read ok",
82 		.insns = {
83 			/* If (write) */
84 			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1,
85 				    offsetof(struct bpf_sysctl, write)),
86 			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 1, 2),
87 
88 			/* return DENY; */
89 			BPF_MOV64_IMM(BPF_REG_0, 0),
90 			BPF_JMP_A(1),
91 
92 			/* else return ALLOW; */
93 			BPF_MOV64_IMM(BPF_REG_0, 1),
94 			BPF_EXIT_INSN(),
95 		},
96 		.attach_type = BPF_CGROUP_SYSCTL,
97 		.sysctl = "kernel/ostype",
98 		.open_flags = O_RDONLY,
99 		.result = SUCCESS,
100 	},
101 	{
102 		.descr = "ctx:write sysctl:write read ok",
103 		.insns = {
104 			/* If (write) */
105 			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1,
106 				    offsetof(struct bpf_sysctl, write)),
107 			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 1, 2),
108 
109 			/* return DENY; */
110 			BPF_MOV64_IMM(BPF_REG_0, 0),
111 			BPF_JMP_A(1),
112 
113 			/* else return ALLOW; */
114 			BPF_MOV64_IMM(BPF_REG_0, 1),
115 			BPF_EXIT_INSN(),
116 		},
117 		.attach_type = BPF_CGROUP_SYSCTL,
118 		.sysctl = "kernel/domainname",
119 		.open_flags = O_WRONLY,
120 		.newval = "(none)", /* same as default, should fail anyway */
121 		.result = OP_EPERM,
122 	},
123 	{
124 		.descr = "ctx:write sysctl:read write reject",
125 		.insns = {
126 			/* write = X */
127 			BPF_MOV64_IMM(BPF_REG_0, 0),
128 			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
129 				    offsetof(struct bpf_sysctl, write)),
130 			BPF_MOV64_IMM(BPF_REG_0, 1),
131 			BPF_EXIT_INSN(),
132 		},
133 		.attach_type = BPF_CGROUP_SYSCTL,
134 		.sysctl = "kernel/ostype",
135 		.open_flags = O_RDONLY,
136 		.result = LOAD_REJECT,
137 	},
138 	{
139 		.descr = "ctx:file_pos sysctl:read read ok",
140 		.insns = {
141 			/* If (file_pos == X) */
142 			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1,
143 				    offsetof(struct bpf_sysctl, file_pos)),
144 			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 3, 2),
145 
146 			/* return ALLOW; */
147 			BPF_MOV64_IMM(BPF_REG_0, 1),
148 			BPF_JMP_A(1),
149 
150 			/* else return DENY; */
151 			BPF_MOV64_IMM(BPF_REG_0, 0),
152 			BPF_EXIT_INSN(),
153 		},
154 		.attach_type = BPF_CGROUP_SYSCTL,
155 		.sysctl = "kernel/ostype",
156 		.open_flags = O_RDONLY,
157 		.seek = 3,
158 		.result = SUCCESS,
159 	},
160 	{
161 		.descr = "ctx:file_pos sysctl:read read ok narrow",
162 		.insns = {
163 			/* If (file_pos == X) */
164 #if __BYTE_ORDER == __LITTLE_ENDIAN
165 			BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_1,
166 				    offsetof(struct bpf_sysctl, file_pos)),
167 #else
168 			BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_1,
169 				    offsetof(struct bpf_sysctl, file_pos) + 3),
170 #endif
171 			BPF_JMP_IMM(BPF_JNE, BPF_REG_7, 4, 2),
172 
173 			/* return ALLOW; */
174 			BPF_MOV64_IMM(BPF_REG_0, 1),
175 			BPF_JMP_A(1),
176 
177 			/* else return DENY; */
178 			BPF_MOV64_IMM(BPF_REG_0, 0),
179 			BPF_EXIT_INSN(),
180 		},
181 		.attach_type = BPF_CGROUP_SYSCTL,
182 		.sysctl = "kernel/ostype",
183 		.open_flags = O_RDONLY,
184 		.seek = 4,
185 		.result = SUCCESS,
186 	},
187 	{
188 		.descr = "ctx:file_pos sysctl:read write ok",
189 		.insns = {
190 			/* file_pos = X */
191 			BPF_MOV64_IMM(BPF_REG_0, 2),
192 			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
193 				    offsetof(struct bpf_sysctl, file_pos)),
194 			BPF_MOV64_IMM(BPF_REG_0, 1),
195 			BPF_EXIT_INSN(),
196 		},
197 		.attach_type = BPF_CGROUP_SYSCTL,
198 		.sysctl = "kernel/ostype",
199 		.open_flags = O_RDONLY,
200 		.oldval = "nux\n",
201 		.result = SUCCESS,
202 	},
203 	{
204 		.descr = "sysctl_get_name sysctl_value:base ok",
205 		.insns = {
206 			/* sysctl_get_name arg2 (buf) */
207 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
208 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
209 			BPF_MOV64_IMM(BPF_REG_0, 0),
210 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
211 
212 			BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
213 
214 			/* sysctl_get_name arg3 (buf_len) */
215 			BPF_MOV64_IMM(BPF_REG_3, 8),
216 
217 			/* sysctl_get_name arg4 (flags) */
218 			BPF_MOV64_IMM(BPF_REG_4, BPF_F_SYSCTL_BASE_NAME),
219 
220 			/* sysctl_get_name(ctx, buf, buf_len, flags) */
221 			BPF_EMIT_CALL(BPF_FUNC_sysctl_get_name),
222 
223 			/* if (ret == expected && */
224 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, sizeof("tcp_mem") - 1, 6),
225 			/*     buf == "tcp_mem\0") */
226 			BPF_LD_IMM64(BPF_REG_8,
227 				     bpf_be64_to_cpu(0x7463705f6d656d00ULL)),
228 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
229 			BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
230 
231 			/* return ALLOW; */
232 			BPF_MOV64_IMM(BPF_REG_0, 1),
233 			BPF_JMP_A(1),
234 
235 			/* else return DENY; */
236 			BPF_MOV64_IMM(BPF_REG_0, 0),
237 			BPF_EXIT_INSN(),
238 		},
239 		.attach_type = BPF_CGROUP_SYSCTL,
240 		.sysctl = "net/ipv4/tcp_mem",
241 		.open_flags = O_RDONLY,
242 		.result = SUCCESS,
243 	},
244 	{
245 		.descr = "sysctl_get_name sysctl_value:base E2BIG truncated",
246 		.insns = {
247 			/* sysctl_get_name arg2 (buf) */
248 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
249 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
250 			BPF_MOV64_IMM(BPF_REG_0, 0),
251 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
252 
253 			BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
254 
255 			/* sysctl_get_name arg3 (buf_len) too small */
256 			BPF_MOV64_IMM(BPF_REG_3, 7),
257 
258 			/* sysctl_get_name arg4 (flags) */
259 			BPF_MOV64_IMM(BPF_REG_4, BPF_F_SYSCTL_BASE_NAME),
260 
261 			/* sysctl_get_name(ctx, buf, buf_len, flags) */
262 			BPF_EMIT_CALL(BPF_FUNC_sysctl_get_name),
263 
264 			/* if (ret == expected && */
265 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -E2BIG, 6),
266 
267 			/*     buf[0:7] == "tcp_me\0") */
268 			BPF_LD_IMM64(BPF_REG_8,
269 				     bpf_be64_to_cpu(0x7463705f6d650000ULL)),
270 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
271 			BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
272 
273 			/* return ALLOW; */
274 			BPF_MOV64_IMM(BPF_REG_0, 1),
275 			BPF_JMP_A(1),
276 
277 			/* else return DENY; */
278 			BPF_MOV64_IMM(BPF_REG_0, 0),
279 			BPF_EXIT_INSN(),
280 		},
281 		.attach_type = BPF_CGROUP_SYSCTL,
282 		.sysctl = "net/ipv4/tcp_mem",
283 		.open_flags = O_RDONLY,
284 		.result = SUCCESS,
285 	},
286 	{
287 		.descr = "sysctl_get_name sysctl:full ok",
288 		.insns = {
289 			/* sysctl_get_name arg2 (buf) */
290 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
291 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -24),
292 			BPF_MOV64_IMM(BPF_REG_0, 0),
293 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
294 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 8),
295 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 16),
296 
297 			BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
298 
299 			/* sysctl_get_name arg3 (buf_len) */
300 			BPF_MOV64_IMM(BPF_REG_3, 17),
301 
302 			/* sysctl_get_name arg4 (flags) */
303 			BPF_MOV64_IMM(BPF_REG_4, 0),
304 
305 			/* sysctl_get_name(ctx, buf, buf_len, flags) */
306 			BPF_EMIT_CALL(BPF_FUNC_sysctl_get_name),
307 
308 			/* if (ret == expected && */
309 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 16, 14),
310 
311 			/*     buf[0:8] == "net/ipv4" && */
312 			BPF_LD_IMM64(BPF_REG_8,
313 				     bpf_be64_to_cpu(0x6e65742f69707634ULL)),
314 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
315 			BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 10),
316 
317 			/*     buf[8:16] == "/tcp_mem" && */
318 			BPF_LD_IMM64(BPF_REG_8,
319 				     bpf_be64_to_cpu(0x2f7463705f6d656dULL)),
320 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 8),
321 			BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 6),
322 
323 			/*     buf[16:24] == "\0") */
324 			BPF_LD_IMM64(BPF_REG_8, 0x0ULL),
325 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 16),
326 			BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
327 
328 			/* return ALLOW; */
329 			BPF_MOV64_IMM(BPF_REG_0, 1),
330 			BPF_JMP_A(1),
331 
332 			/* else return DENY; */
333 			BPF_MOV64_IMM(BPF_REG_0, 0),
334 			BPF_EXIT_INSN(),
335 		},
336 		.attach_type = BPF_CGROUP_SYSCTL,
337 		.sysctl = "net/ipv4/tcp_mem",
338 		.open_flags = O_RDONLY,
339 		.result = SUCCESS,
340 	},
341 	{
342 		.descr = "sysctl_get_name sysctl:full E2BIG truncated",
343 		.insns = {
344 			/* sysctl_get_name arg2 (buf) */
345 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
346 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -16),
347 			BPF_MOV64_IMM(BPF_REG_0, 0),
348 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
349 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 8),
350 
351 			BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
352 
353 			/* sysctl_get_name arg3 (buf_len) */
354 			BPF_MOV64_IMM(BPF_REG_3, 16),
355 
356 			/* sysctl_get_name arg4 (flags) */
357 			BPF_MOV64_IMM(BPF_REG_4, 0),
358 
359 			/* sysctl_get_name(ctx, buf, buf_len, flags) */
360 			BPF_EMIT_CALL(BPF_FUNC_sysctl_get_name),
361 
362 			/* if (ret == expected && */
363 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -E2BIG, 10),
364 
365 			/*     buf[0:8] == "net/ipv4" && */
366 			BPF_LD_IMM64(BPF_REG_8,
367 				     bpf_be64_to_cpu(0x6e65742f69707634ULL)),
368 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
369 			BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 6),
370 
371 			/*     buf[8:16] == "/tcp_me\0") */
372 			BPF_LD_IMM64(BPF_REG_8,
373 				     bpf_be64_to_cpu(0x2f7463705f6d6500ULL)),
374 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 8),
375 			BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
376 
377 			/* return ALLOW; */
378 			BPF_MOV64_IMM(BPF_REG_0, 1),
379 			BPF_JMP_A(1),
380 
381 			/* else return DENY; */
382 			BPF_MOV64_IMM(BPF_REG_0, 0),
383 			BPF_EXIT_INSN(),
384 		},
385 		.attach_type = BPF_CGROUP_SYSCTL,
386 		.sysctl = "net/ipv4/tcp_mem",
387 		.open_flags = O_RDONLY,
388 		.result = SUCCESS,
389 	},
390 	{
391 		.descr = "sysctl_get_name sysctl:full E2BIG truncated small",
392 		.insns = {
393 			/* sysctl_get_name arg2 (buf) */
394 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
395 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
396 			BPF_MOV64_IMM(BPF_REG_0, 0),
397 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
398 
399 			BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
400 
401 			/* sysctl_get_name arg3 (buf_len) */
402 			BPF_MOV64_IMM(BPF_REG_3, 7),
403 
404 			/* sysctl_get_name arg4 (flags) */
405 			BPF_MOV64_IMM(BPF_REG_4, 0),
406 
407 			/* sysctl_get_name(ctx, buf, buf_len, flags) */
408 			BPF_EMIT_CALL(BPF_FUNC_sysctl_get_name),
409 
410 			/* if (ret == expected && */
411 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -E2BIG, 6),
412 
413 			/*     buf[0:8] == "net/ip\0") */
414 			BPF_LD_IMM64(BPF_REG_8,
415 				     bpf_be64_to_cpu(0x6e65742f69700000ULL)),
416 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
417 			BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
418 
419 			/* return ALLOW; */
420 			BPF_MOV64_IMM(BPF_REG_0, 1),
421 			BPF_JMP_A(1),
422 
423 			/* else return DENY; */
424 			BPF_MOV64_IMM(BPF_REG_0, 0),
425 			BPF_EXIT_INSN(),
426 		},
427 		.attach_type = BPF_CGROUP_SYSCTL,
428 		.sysctl = "net/ipv4/tcp_mem",
429 		.open_flags = O_RDONLY,
430 		.result = SUCCESS,
431 	},
432 	{
433 		.descr = "sysctl_get_current_value sysctl:read ok, gt",
434 		.insns = {
435 			/* sysctl_get_current_value arg2 (buf) */
436 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
437 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
438 			BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
439 
440 			/* sysctl_get_current_value arg3 (buf_len) */
441 			BPF_MOV64_IMM(BPF_REG_3, 8),
442 
443 			/* sysctl_get_current_value(ctx, buf, buf_len) */
444 			BPF_EMIT_CALL(BPF_FUNC_sysctl_get_current_value),
445 
446 			/* if (ret == expected && */
447 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 6, 6),
448 
449 			/*     buf[0:6] == "Linux\n\0") */
450 			BPF_LD_IMM64(BPF_REG_8,
451 				     bpf_be64_to_cpu(0x4c696e75780a0000ULL)),
452 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
453 			BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
454 
455 			/* return ALLOW; */
456 			BPF_MOV64_IMM(BPF_REG_0, 1),
457 			BPF_JMP_A(1),
458 
459 			/* else return DENY; */
460 			BPF_MOV64_IMM(BPF_REG_0, 0),
461 			BPF_EXIT_INSN(),
462 		},
463 		.attach_type = BPF_CGROUP_SYSCTL,
464 		.sysctl = "kernel/ostype",
465 		.open_flags = O_RDONLY,
466 		.result = SUCCESS,
467 	},
468 	{
469 		.descr = "sysctl_get_current_value sysctl:read ok, eq",
470 		.insns = {
471 			/* sysctl_get_current_value arg2 (buf) */
472 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
473 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
474 			BPF_MOV64_IMM(BPF_REG_0, 0),
475 			BPF_STX_MEM(BPF_B, BPF_REG_7, BPF_REG_0, 7),
476 
477 			BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
478 
479 			/* sysctl_get_current_value arg3 (buf_len) */
480 			BPF_MOV64_IMM(BPF_REG_3, 7),
481 
482 			/* sysctl_get_current_value(ctx, buf, buf_len) */
483 			BPF_EMIT_CALL(BPF_FUNC_sysctl_get_current_value),
484 
485 			/* if (ret == expected && */
486 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 6, 6),
487 
488 			/*     buf[0:6] == "Linux\n\0") */
489 			BPF_LD_IMM64(BPF_REG_8,
490 				     bpf_be64_to_cpu(0x4c696e75780a0000ULL)),
491 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
492 			BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
493 
494 			/* return ALLOW; */
495 			BPF_MOV64_IMM(BPF_REG_0, 1),
496 			BPF_JMP_A(1),
497 
498 			/* else return DENY; */
499 			BPF_MOV64_IMM(BPF_REG_0, 0),
500 			BPF_EXIT_INSN(),
501 		},
502 		.attach_type = BPF_CGROUP_SYSCTL,
503 		.sysctl = "kernel/ostype",
504 		.open_flags = O_RDONLY,
505 		.result = SUCCESS,
506 	},
507 	{
508 		.descr = "sysctl_get_current_value sysctl:read E2BIG truncated",
509 		.insns = {
510 			/* sysctl_get_current_value arg2 (buf) */
511 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
512 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
513 			BPF_MOV64_IMM(BPF_REG_0, 0),
514 			BPF_STX_MEM(BPF_H, BPF_REG_7, BPF_REG_0, 6),
515 
516 			BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
517 
518 			/* sysctl_get_current_value arg3 (buf_len) */
519 			BPF_MOV64_IMM(BPF_REG_3, 6),
520 
521 			/* sysctl_get_current_value(ctx, buf, buf_len) */
522 			BPF_EMIT_CALL(BPF_FUNC_sysctl_get_current_value),
523 
524 			/* if (ret == expected && */
525 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -E2BIG, 6),
526 
527 			/*     buf[0:6] == "Linux\0") */
528 			BPF_LD_IMM64(BPF_REG_8,
529 				     bpf_be64_to_cpu(0x4c696e7578000000ULL)),
530 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
531 			BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
532 
533 			/* return ALLOW; */
534 			BPF_MOV64_IMM(BPF_REG_0, 1),
535 			BPF_JMP_A(1),
536 
537 			/* else return DENY; */
538 			BPF_MOV64_IMM(BPF_REG_0, 0),
539 			BPF_EXIT_INSN(),
540 		},
541 		.attach_type = BPF_CGROUP_SYSCTL,
542 		.sysctl = "kernel/ostype",
543 		.open_flags = O_RDONLY,
544 		.result = SUCCESS,
545 	},
546 	{
547 		.descr = "sysctl_get_current_value sysctl:read EINVAL",
548 		.insns = {
549 			/* sysctl_get_current_value arg2 (buf) */
550 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
551 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
552 
553 			BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
554 
555 			/* sysctl_get_current_value arg3 (buf_len) */
556 			BPF_MOV64_IMM(BPF_REG_3, 8),
557 
558 			/* sysctl_get_current_value(ctx, buf, buf_len) */
559 			BPF_EMIT_CALL(BPF_FUNC_sysctl_get_current_value),
560 
561 			/* if (ret == expected && */
562 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -EINVAL, 4),
563 
564 			/*     buf[0:8] is NUL-filled) */
565 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
566 			BPF_JMP_IMM(BPF_JNE, BPF_REG_9, 0, 2),
567 
568 			/* return DENY; */
569 			BPF_MOV64_IMM(BPF_REG_0, 0),
570 			BPF_JMP_A(1),
571 
572 			/* else return ALLOW; */
573 			BPF_MOV64_IMM(BPF_REG_0, 1),
574 			BPF_EXIT_INSN(),
575 		},
576 		.attach_type = BPF_CGROUP_SYSCTL,
577 		.sysctl = "net/ipv6/conf/lo/stable_secret", /* -EIO */
578 		.open_flags = O_RDONLY,
579 		.result = OP_EPERM,
580 	},
581 	{
582 		.descr = "sysctl_get_current_value sysctl:write ok",
583 		.fixup_value_insn = 6,
584 		.insns = {
585 			/* sysctl_get_current_value arg2 (buf) */
586 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
587 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
588 
589 			BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
590 
591 			/* sysctl_get_current_value arg3 (buf_len) */
592 			BPF_MOV64_IMM(BPF_REG_3, 8),
593 
594 			/* sysctl_get_current_value(ctx, buf, buf_len) */
595 			BPF_EMIT_CALL(BPF_FUNC_sysctl_get_current_value),
596 
597 			/* if (ret == expected && */
598 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 4, 6),
599 
600 			/*     buf[0:4] == expected) */
601 			BPF_LD_IMM64(BPF_REG_8, FIXUP_SYSCTL_VALUE),
602 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
603 			BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
604 
605 			/* return DENY; */
606 			BPF_MOV64_IMM(BPF_REG_0, 0),
607 			BPF_JMP_A(1),
608 
609 			/* else return ALLOW; */
610 			BPF_MOV64_IMM(BPF_REG_0, 1),
611 			BPF_EXIT_INSN(),
612 		},
613 		.attach_type = BPF_CGROUP_SYSCTL,
614 		.sysctl = "net/ipv4/route/mtu_expires",
615 		.open_flags = O_WRONLY,
616 		.newval = "600", /* same as default, should fail anyway */
617 		.result = OP_EPERM,
618 	},
619 	{
620 		.descr = "sysctl_get_new_value sysctl:read EINVAL",
621 		.insns = {
622 			/* sysctl_get_new_value arg2 (buf) */
623 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
624 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
625 			BPF_MOV64_IMM(BPF_REG_0, 0),
626 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
627 
628 			BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
629 
630 			/* sysctl_get_new_value arg3 (buf_len) */
631 			BPF_MOV64_IMM(BPF_REG_3, 8),
632 
633 			/* sysctl_get_new_value(ctx, buf, buf_len) */
634 			BPF_EMIT_CALL(BPF_FUNC_sysctl_get_new_value),
635 
636 			/* if (ret == expected) */
637 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -EINVAL, 2),
638 
639 			/* return ALLOW; */
640 			BPF_MOV64_IMM(BPF_REG_0, 1),
641 			BPF_JMP_A(1),
642 
643 			/* else return DENY; */
644 			BPF_MOV64_IMM(BPF_REG_0, 0),
645 			BPF_EXIT_INSN(),
646 		},
647 		.attach_type = BPF_CGROUP_SYSCTL,
648 		.sysctl = "net/ipv4/tcp_mem",
649 		.open_flags = O_RDONLY,
650 		.result = SUCCESS,
651 	},
652 	{
653 		.descr = "sysctl_get_new_value sysctl:write ok",
654 		.insns = {
655 			/* sysctl_get_new_value arg2 (buf) */
656 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
657 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
658 
659 			BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
660 
661 			/* sysctl_get_new_value arg3 (buf_len) */
662 			BPF_MOV64_IMM(BPF_REG_3, 4),
663 
664 			/* sysctl_get_new_value(ctx, buf, buf_len) */
665 			BPF_EMIT_CALL(BPF_FUNC_sysctl_get_new_value),
666 
667 			/* if (ret == expected && */
668 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 3, 4),
669 
670 			/*     buf[0:4] == "606\0") */
671 			BPF_LDX_MEM(BPF_W, BPF_REG_9, BPF_REG_7, 0),
672 			BPF_JMP_IMM(BPF_JNE, BPF_REG_9,
673 				    bpf_ntohl(0x36303600), 2),
674 
675 			/* return DENY; */
676 			BPF_MOV64_IMM(BPF_REG_0, 0),
677 			BPF_JMP_A(1),
678 
679 			/* else return ALLOW; */
680 			BPF_MOV64_IMM(BPF_REG_0, 1),
681 			BPF_EXIT_INSN(),
682 		},
683 		.attach_type = BPF_CGROUP_SYSCTL,
684 		.sysctl = "net/ipv4/route/mtu_expires",
685 		.open_flags = O_WRONLY,
686 		.newval = "606",
687 		.result = OP_EPERM,
688 	},
689 	{
690 		.descr = "sysctl_get_new_value sysctl:write ok long",
691 		.insns = {
692 			/* sysctl_get_new_value arg2 (buf) */
693 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
694 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -24),
695 
696 			BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
697 
698 			/* sysctl_get_new_value arg3 (buf_len) */
699 			BPF_MOV64_IMM(BPF_REG_3, 24),
700 
701 			/* sysctl_get_new_value(ctx, buf, buf_len) */
702 			BPF_EMIT_CALL(BPF_FUNC_sysctl_get_new_value),
703 
704 			/* if (ret == expected && */
705 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 23, 14),
706 
707 			/*     buf[0:8] == "3000000 " && */
708 			BPF_LD_IMM64(BPF_REG_8,
709 				     bpf_be64_to_cpu(0x3330303030303020ULL)),
710 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
711 			BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 10),
712 
713 			/*     buf[8:16] == "4000000 " && */
714 			BPF_LD_IMM64(BPF_REG_8,
715 				     bpf_be64_to_cpu(0x3430303030303020ULL)),
716 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 8),
717 			BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 6),
718 
719 			/*     buf[16:24] == "6000000\0") */
720 			BPF_LD_IMM64(BPF_REG_8,
721 				     bpf_be64_to_cpu(0x3630303030303000ULL)),
722 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 16),
723 			BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
724 
725 			/* return DENY; */
726 			BPF_MOV64_IMM(BPF_REG_0, 0),
727 			BPF_JMP_A(1),
728 
729 			/* else return ALLOW; */
730 			BPF_MOV64_IMM(BPF_REG_0, 1),
731 			BPF_EXIT_INSN(),
732 		},
733 		.attach_type = BPF_CGROUP_SYSCTL,
734 		.sysctl = "net/ipv4/tcp_mem",
735 		.open_flags = O_WRONLY,
736 		.newval = "3000000 4000000 6000000",
737 		.result = OP_EPERM,
738 	},
739 	{
740 		.descr = "sysctl_get_new_value sysctl:write E2BIG",
741 		.insns = {
742 			/* sysctl_get_new_value arg2 (buf) */
743 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
744 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
745 			BPF_MOV64_IMM(BPF_REG_0, 0),
746 			BPF_STX_MEM(BPF_B, BPF_REG_7, BPF_REG_0, 3),
747 
748 			BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
749 
750 			/* sysctl_get_new_value arg3 (buf_len) */
751 			BPF_MOV64_IMM(BPF_REG_3, 3),
752 
753 			/* sysctl_get_new_value(ctx, buf, buf_len) */
754 			BPF_EMIT_CALL(BPF_FUNC_sysctl_get_new_value),
755 
756 			/* if (ret == expected && */
757 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -E2BIG, 4),
758 
759 			/*     buf[0:3] == "60\0") */
760 			BPF_LDX_MEM(BPF_W, BPF_REG_9, BPF_REG_7, 0),
761 			BPF_JMP_IMM(BPF_JNE, BPF_REG_9,
762 				    bpf_ntohl(0x36300000), 2),
763 
764 			/* return DENY; */
765 			BPF_MOV64_IMM(BPF_REG_0, 0),
766 			BPF_JMP_A(1),
767 
768 			/* else return ALLOW; */
769 			BPF_MOV64_IMM(BPF_REG_0, 1),
770 			BPF_EXIT_INSN(),
771 		},
772 		.attach_type = BPF_CGROUP_SYSCTL,
773 		.sysctl = "net/ipv4/route/mtu_expires",
774 		.open_flags = O_WRONLY,
775 		.newval = "606",
776 		.result = OP_EPERM,
777 	},
778 	{
779 		.descr = "sysctl_set_new_value sysctl:read EINVAL",
780 		.insns = {
781 			/* sysctl_set_new_value arg2 (buf) */
782 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
783 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
784 			BPF_MOV64_IMM(BPF_REG_0,
785 				      bpf_ntohl(0x36303000)),
786 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
787 
788 			BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
789 
790 			/* sysctl_set_new_value arg3 (buf_len) */
791 			BPF_MOV64_IMM(BPF_REG_3, 3),
792 
793 			/* sysctl_set_new_value(ctx, buf, buf_len) */
794 			BPF_EMIT_CALL(BPF_FUNC_sysctl_set_new_value),
795 
796 			/* if (ret == expected) */
797 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -EINVAL, 2),
798 
799 			/* return ALLOW; */
800 			BPF_MOV64_IMM(BPF_REG_0, 1),
801 			BPF_JMP_A(1),
802 
803 			/* else return DENY; */
804 			BPF_MOV64_IMM(BPF_REG_0, 0),
805 			BPF_EXIT_INSN(),
806 		},
807 		.attach_type = BPF_CGROUP_SYSCTL,
808 		.sysctl = "net/ipv4/route/mtu_expires",
809 		.open_flags = O_RDONLY,
810 		.result = SUCCESS,
811 	},
812 	{
813 		.descr = "sysctl_set_new_value sysctl:write ok",
814 		.fixup_value_insn = 2,
815 		.insns = {
816 			/* sysctl_set_new_value arg2 (buf) */
817 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
818 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
819 			BPF_LD_IMM64(BPF_REG_0, FIXUP_SYSCTL_VALUE),
820 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
821 
822 			BPF_MOV64_REG(BPF_REG_2, BPF_REG_7),
823 
824 			/* sysctl_set_new_value arg3 (buf_len) */
825 			BPF_MOV64_IMM(BPF_REG_3, 3),
826 
827 			/* sysctl_set_new_value(ctx, buf, buf_len) */
828 			BPF_EMIT_CALL(BPF_FUNC_sysctl_set_new_value),
829 
830 			/* if (ret == expected) */
831 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 2),
832 
833 			/* return ALLOW; */
834 			BPF_MOV64_IMM(BPF_REG_0, 1),
835 			BPF_JMP_A(1),
836 
837 			/* else return DENY; */
838 			BPF_MOV64_IMM(BPF_REG_0, 0),
839 			BPF_EXIT_INSN(),
840 		},
841 		.attach_type = BPF_CGROUP_SYSCTL,
842 		.sysctl = "net/ipv4/route/mtu_expires",
843 		.open_flags = O_WRONLY,
844 		.newval = "606",
845 		.result = SUCCESS,
846 	},
847 	{
848 		"bpf_strtoul one number string",
849 		.insns = {
850 			/* arg1 (buf) */
851 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
852 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
853 			BPF_MOV64_IMM(BPF_REG_0,
854 				      bpf_ntohl(0x36303000)),
855 			BPF_STX_MEM(BPF_W, BPF_REG_7, BPF_REG_0, 0),
856 
857 			BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
858 
859 			/* arg2 (buf_len) */
860 			BPF_MOV64_IMM(BPF_REG_2, 4),
861 
862 			/* arg3 (flags) */
863 			BPF_MOV64_IMM(BPF_REG_3, 0),
864 
865 			/* arg4 (res) */
866 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
867 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
868 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_7),
869 
870 			BPF_EMIT_CALL(BPF_FUNC_strtoul),
871 
872 			/* if (ret == expected && */
873 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 3, 4),
874 			/*     res == expected) */
875 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
876 			BPF_JMP_IMM(BPF_JNE, BPF_REG_9, 600, 2),
877 
878 			/* return ALLOW; */
879 			BPF_MOV64_IMM(BPF_REG_0, 1),
880 			BPF_JMP_A(1),
881 
882 			/* else return DENY; */
883 			BPF_MOV64_IMM(BPF_REG_0, 0),
884 			BPF_EXIT_INSN(),
885 		},
886 		.attach_type = BPF_CGROUP_SYSCTL,
887 		.sysctl = "net/ipv4/route/mtu_expires",
888 		.open_flags = O_RDONLY,
889 		.result = SUCCESS,
890 	},
891 	{
892 		"bpf_strtoul multi number string",
893 		.insns = {
894 			/* arg1 (buf) */
895 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
896 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
897 			/* "600 602\0" */
898 			BPF_LD_IMM64(BPF_REG_0,
899 				     bpf_be64_to_cpu(0x3630302036303200ULL)),
900 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
901 			BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
902 
903 			/* arg2 (buf_len) */
904 			BPF_MOV64_IMM(BPF_REG_2, 8),
905 
906 			/* arg3 (flags) */
907 			BPF_MOV64_IMM(BPF_REG_3, 0),
908 
909 			/* arg4 (res) */
910 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
911 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
912 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_7),
913 
914 			BPF_EMIT_CALL(BPF_FUNC_strtoul),
915 
916 			/* if (ret == expected && */
917 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 3, 18),
918 			/*     res == expected) */
919 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
920 			BPF_JMP_IMM(BPF_JNE, BPF_REG_9, 600, 16),
921 
922 			/*     arg1 (buf) */
923 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
924 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
925 			BPF_ALU64_REG(BPF_ADD, BPF_REG_7, BPF_REG_0),
926 			BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
927 
928 			/*     arg2 (buf_len) */
929 			BPF_MOV64_IMM(BPF_REG_2, 8),
930 			BPF_ALU64_REG(BPF_SUB, BPF_REG_2, BPF_REG_0),
931 
932 			/*     arg3 (flags) */
933 			BPF_MOV64_IMM(BPF_REG_3, 0),
934 
935 			/*     arg4 (res) */
936 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
937 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -16),
938 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_7),
939 
940 			BPF_EMIT_CALL(BPF_FUNC_strtoul),
941 
942 			/*     if (ret == expected && */
943 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 4, 4),
944 			/*         res == expected) */
945 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
946 			BPF_JMP_IMM(BPF_JNE, BPF_REG_9, 602, 2),
947 
948 			/* return ALLOW; */
949 			BPF_MOV64_IMM(BPF_REG_0, 1),
950 			BPF_JMP_A(1),
951 
952 			/* else return DENY; */
953 			BPF_MOV64_IMM(BPF_REG_0, 0),
954 			BPF_EXIT_INSN(),
955 		},
956 		.attach_type = BPF_CGROUP_SYSCTL,
957 		.sysctl = "net/ipv4/tcp_mem",
958 		.open_flags = O_RDONLY,
959 		.result = SUCCESS,
960 	},
961 	{
962 		"bpf_strtoul buf_len = 0, reject",
963 		.insns = {
964 			/* arg1 (buf) */
965 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
966 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
967 			BPF_MOV64_IMM(BPF_REG_0,
968 				      bpf_ntohl(0x36303000)),
969 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
970 
971 			BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
972 
973 			/* arg2 (buf_len) */
974 			BPF_MOV64_IMM(BPF_REG_2, 0),
975 
976 			/* arg3 (flags) */
977 			BPF_MOV64_IMM(BPF_REG_3, 0),
978 
979 			/* arg4 (res) */
980 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
981 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
982 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_7),
983 
984 			BPF_EMIT_CALL(BPF_FUNC_strtoul),
985 
986 			BPF_MOV64_IMM(BPF_REG_0, 1),
987 			BPF_EXIT_INSN(),
988 		},
989 		.attach_type = BPF_CGROUP_SYSCTL,
990 		.sysctl = "net/ipv4/route/mtu_expires",
991 		.open_flags = O_RDONLY,
992 		.result = LOAD_REJECT,
993 	},
994 	{
995 		"bpf_strtoul supported base, ok",
996 		.insns = {
997 			/* arg1 (buf) */
998 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
999 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
1000 			BPF_MOV64_IMM(BPF_REG_0,
1001 				      bpf_ntohl(0x30373700)),
1002 			BPF_STX_MEM(BPF_W, BPF_REG_7, BPF_REG_0, 0),
1003 
1004 			BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
1005 
1006 			/* arg2 (buf_len) */
1007 			BPF_MOV64_IMM(BPF_REG_2, 4),
1008 
1009 			/* arg3 (flags) */
1010 			BPF_MOV64_IMM(BPF_REG_3, 8),
1011 
1012 			/* arg4 (res) */
1013 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
1014 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
1015 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_7),
1016 
1017 			BPF_EMIT_CALL(BPF_FUNC_strtoul),
1018 
1019 			/* if (ret == expected && */
1020 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 3, 4),
1021 			/*     res == expected) */
1022 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
1023 			BPF_JMP_IMM(BPF_JNE, BPF_REG_9, 63, 2),
1024 
1025 			/* return ALLOW; */
1026 			BPF_MOV64_IMM(BPF_REG_0, 1),
1027 			BPF_JMP_A(1),
1028 
1029 			/* else return DENY; */
1030 			BPF_MOV64_IMM(BPF_REG_0, 0),
1031 			BPF_EXIT_INSN(),
1032 		},
1033 		.attach_type = BPF_CGROUP_SYSCTL,
1034 		.sysctl = "net/ipv4/route/mtu_expires",
1035 		.open_flags = O_RDONLY,
1036 		.result = SUCCESS,
1037 	},
1038 	{
1039 		"bpf_strtoul unsupported base, EINVAL",
1040 		.insns = {
1041 			/* arg1 (buf) */
1042 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
1043 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
1044 			BPF_MOV64_IMM(BPF_REG_0,
1045 				      bpf_ntohl(0x36303000)),
1046 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
1047 
1048 			BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
1049 
1050 			/* arg2 (buf_len) */
1051 			BPF_MOV64_IMM(BPF_REG_2, 4),
1052 
1053 			/* arg3 (flags) */
1054 			BPF_MOV64_IMM(BPF_REG_3, 3),
1055 
1056 			/* arg4 (res) */
1057 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
1058 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
1059 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_7),
1060 
1061 			BPF_EMIT_CALL(BPF_FUNC_strtoul),
1062 
1063 			/* if (ret == expected) */
1064 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -EINVAL, 2),
1065 
1066 			/* return ALLOW; */
1067 			BPF_MOV64_IMM(BPF_REG_0, 1),
1068 			BPF_JMP_A(1),
1069 
1070 			/* else return DENY; */
1071 			BPF_MOV64_IMM(BPF_REG_0, 0),
1072 			BPF_EXIT_INSN(),
1073 		},
1074 		.attach_type = BPF_CGROUP_SYSCTL,
1075 		.sysctl = "net/ipv4/route/mtu_expires",
1076 		.open_flags = O_RDONLY,
1077 		.result = SUCCESS,
1078 	},
1079 	{
1080 		"bpf_strtoul buf with spaces only, EINVAL",
1081 		.insns = {
1082 			/* arg1 (buf) */
1083 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
1084 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
1085 			BPF_MOV64_IMM(BPF_REG_0,
1086 				      bpf_ntohl(0x0d0c0a09)),
1087 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
1088 
1089 			BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
1090 
1091 			/* arg2 (buf_len) */
1092 			BPF_MOV64_IMM(BPF_REG_2, 4),
1093 
1094 			/* arg3 (flags) */
1095 			BPF_MOV64_IMM(BPF_REG_3, 0),
1096 
1097 			/* arg4 (res) */
1098 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
1099 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
1100 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_7),
1101 
1102 			BPF_EMIT_CALL(BPF_FUNC_strtoul),
1103 
1104 			/* if (ret == expected) */
1105 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -EINVAL, 2),
1106 
1107 			/* return ALLOW; */
1108 			BPF_MOV64_IMM(BPF_REG_0, 1),
1109 			BPF_JMP_A(1),
1110 
1111 			/* else return DENY; */
1112 			BPF_MOV64_IMM(BPF_REG_0, 0),
1113 			BPF_EXIT_INSN(),
1114 		},
1115 		.attach_type = BPF_CGROUP_SYSCTL,
1116 		.sysctl = "net/ipv4/route/mtu_expires",
1117 		.open_flags = O_RDONLY,
1118 		.result = SUCCESS,
1119 	},
1120 	{
1121 		"bpf_strtoul negative number, EINVAL",
1122 		.insns = {
1123 			/* arg1 (buf) */
1124 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
1125 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
1126 			/* " -6\0" */
1127 			BPF_MOV64_IMM(BPF_REG_0,
1128 				      bpf_ntohl(0x0a2d3600)),
1129 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
1130 
1131 			BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
1132 
1133 			/* arg2 (buf_len) */
1134 			BPF_MOV64_IMM(BPF_REG_2, 4),
1135 
1136 			/* arg3 (flags) */
1137 			BPF_MOV64_IMM(BPF_REG_3, 0),
1138 
1139 			/* arg4 (res) */
1140 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
1141 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
1142 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_7),
1143 
1144 			BPF_EMIT_CALL(BPF_FUNC_strtoul),
1145 
1146 			/* if (ret == expected) */
1147 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -EINVAL, 2),
1148 
1149 			/* return ALLOW; */
1150 			BPF_MOV64_IMM(BPF_REG_0, 1),
1151 			BPF_JMP_A(1),
1152 
1153 			/* else return DENY; */
1154 			BPF_MOV64_IMM(BPF_REG_0, 0),
1155 			BPF_EXIT_INSN(),
1156 		},
1157 		.attach_type = BPF_CGROUP_SYSCTL,
1158 		.sysctl = "net/ipv4/route/mtu_expires",
1159 		.open_flags = O_RDONLY,
1160 		.result = SUCCESS,
1161 	},
1162 	{
1163 		"bpf_strtol negative number, ok",
1164 		.insns = {
1165 			/* arg1 (buf) */
1166 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
1167 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
1168 			/* " -6\0" */
1169 			BPF_MOV64_IMM(BPF_REG_0,
1170 				      bpf_ntohl(0x0a2d3600)),
1171 			BPF_STX_MEM(BPF_W, BPF_REG_7, BPF_REG_0, 0),
1172 
1173 			BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
1174 
1175 			/* arg2 (buf_len) */
1176 			BPF_MOV64_IMM(BPF_REG_2, 4),
1177 
1178 			/* arg3 (flags) */
1179 			BPF_MOV64_IMM(BPF_REG_3, 10),
1180 
1181 			/* arg4 (res) */
1182 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
1183 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
1184 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_7),
1185 
1186 			BPF_EMIT_CALL(BPF_FUNC_strtol),
1187 
1188 			/* if (ret == expected && */
1189 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 3, 4),
1190 			/*     res == expected) */
1191 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
1192 			BPF_JMP_IMM(BPF_JNE, BPF_REG_9, -6, 2),
1193 
1194 			/* return ALLOW; */
1195 			BPF_MOV64_IMM(BPF_REG_0, 1),
1196 			BPF_JMP_A(1),
1197 
1198 			/* else return DENY; */
1199 			BPF_MOV64_IMM(BPF_REG_0, 0),
1200 			BPF_EXIT_INSN(),
1201 		},
1202 		.attach_type = BPF_CGROUP_SYSCTL,
1203 		.sysctl = "net/ipv4/route/mtu_expires",
1204 		.open_flags = O_RDONLY,
1205 		.result = SUCCESS,
1206 	},
1207 	{
1208 		"bpf_strtol hex number, ok",
1209 		.insns = {
1210 			/* arg1 (buf) */
1211 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
1212 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
1213 			/* "0xfe" */
1214 			BPF_MOV64_IMM(BPF_REG_0,
1215 				      bpf_ntohl(0x30786665)),
1216 			BPF_STX_MEM(BPF_W, BPF_REG_7, BPF_REG_0, 0),
1217 
1218 			BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
1219 
1220 			/* arg2 (buf_len) */
1221 			BPF_MOV64_IMM(BPF_REG_2, 4),
1222 
1223 			/* arg3 (flags) */
1224 			BPF_MOV64_IMM(BPF_REG_3, 0),
1225 
1226 			/* arg4 (res) */
1227 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
1228 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
1229 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_7),
1230 
1231 			BPF_EMIT_CALL(BPF_FUNC_strtol),
1232 
1233 			/* if (ret == expected && */
1234 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 4, 4),
1235 			/*     res == expected) */
1236 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
1237 			BPF_JMP_IMM(BPF_JNE, BPF_REG_9, 254, 2),
1238 
1239 			/* return ALLOW; */
1240 			BPF_MOV64_IMM(BPF_REG_0, 1),
1241 			BPF_JMP_A(1),
1242 
1243 			/* else return DENY; */
1244 			BPF_MOV64_IMM(BPF_REG_0, 0),
1245 			BPF_EXIT_INSN(),
1246 		},
1247 		.attach_type = BPF_CGROUP_SYSCTL,
1248 		.sysctl = "net/ipv4/route/mtu_expires",
1249 		.open_flags = O_RDONLY,
1250 		.result = SUCCESS,
1251 	},
1252 	{
1253 		"bpf_strtol max long",
1254 		.insns = {
1255 			/* arg1 (buf) 9223372036854775807 */
1256 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
1257 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -24),
1258 			BPF_LD_IMM64(BPF_REG_0,
1259 				     bpf_be64_to_cpu(0x3932323333373230ULL)),
1260 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
1261 			BPF_LD_IMM64(BPF_REG_0,
1262 				     bpf_be64_to_cpu(0x3336383534373735ULL)),
1263 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 8),
1264 			BPF_LD_IMM64(BPF_REG_0,
1265 				     bpf_be64_to_cpu(0x3830370000000000ULL)),
1266 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 16),
1267 
1268 			BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
1269 
1270 			/* arg2 (buf_len) */
1271 			BPF_MOV64_IMM(BPF_REG_2, 19),
1272 
1273 			/* arg3 (flags) */
1274 			BPF_MOV64_IMM(BPF_REG_3, 0),
1275 
1276 			/* arg4 (res) */
1277 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
1278 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
1279 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_7),
1280 
1281 			BPF_EMIT_CALL(BPF_FUNC_strtol),
1282 
1283 			/* if (ret == expected && */
1284 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 19, 6),
1285 			/*     res == expected) */
1286 			BPF_LD_IMM64(BPF_REG_8, 0x7fffffffffffffffULL),
1287 			BPF_LDX_MEM(BPF_DW, BPF_REG_9, BPF_REG_7, 0),
1288 			BPF_JMP_REG(BPF_JNE, BPF_REG_8, BPF_REG_9, 2),
1289 
1290 			/* return ALLOW; */
1291 			BPF_MOV64_IMM(BPF_REG_0, 1),
1292 			BPF_JMP_A(1),
1293 
1294 			/* else return DENY; */
1295 			BPF_MOV64_IMM(BPF_REG_0, 0),
1296 			BPF_EXIT_INSN(),
1297 		},
1298 		.attach_type = BPF_CGROUP_SYSCTL,
1299 		.sysctl = "net/ipv4/route/mtu_expires",
1300 		.open_flags = O_RDONLY,
1301 		.result = SUCCESS,
1302 	},
1303 	{
1304 		"bpf_strtol overflow, ERANGE",
1305 		.insns = {
1306 			/* arg1 (buf) 9223372036854775808 */
1307 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_10),
1308 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -24),
1309 			BPF_LD_IMM64(BPF_REG_0,
1310 				     bpf_be64_to_cpu(0x3932323333373230ULL)),
1311 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
1312 			BPF_LD_IMM64(BPF_REG_0,
1313 				     bpf_be64_to_cpu(0x3336383534373735ULL)),
1314 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 8),
1315 			BPF_LD_IMM64(BPF_REG_0,
1316 				     bpf_be64_to_cpu(0x3830380000000000ULL)),
1317 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 16),
1318 
1319 			BPF_MOV64_REG(BPF_REG_1, BPF_REG_7),
1320 
1321 			/* arg2 (buf_len) */
1322 			BPF_MOV64_IMM(BPF_REG_2, 19),
1323 
1324 			/* arg3 (flags) */
1325 			BPF_MOV64_IMM(BPF_REG_3, 0),
1326 
1327 			/* arg4 (res) */
1328 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, -8),
1329 			BPF_STX_MEM(BPF_DW, BPF_REG_7, BPF_REG_0, 0),
1330 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_7),
1331 
1332 			BPF_EMIT_CALL(BPF_FUNC_strtol),
1333 
1334 			/* if (ret == expected) */
1335 			BPF_JMP_IMM(BPF_JNE, BPF_REG_0, -ERANGE, 2),
1336 
1337 			/* return ALLOW; */
1338 			BPF_MOV64_IMM(BPF_REG_0, 1),
1339 			BPF_JMP_A(1),
1340 
1341 			/* else return DENY; */
1342 			BPF_MOV64_IMM(BPF_REG_0, 0),
1343 			BPF_EXIT_INSN(),
1344 		},
1345 		.attach_type = BPF_CGROUP_SYSCTL,
1346 		.sysctl = "net/ipv4/route/mtu_expires",
1347 		.open_flags = O_RDONLY,
1348 		.result = SUCCESS,
1349 	},
1350 	{
1351 		"C prog: deny all writes",
1352 		.prog_file = "./test_sysctl_prog.o",
1353 		.attach_type = BPF_CGROUP_SYSCTL,
1354 		.sysctl = "net/ipv4/tcp_mem",
1355 		.open_flags = O_WRONLY,
1356 		.newval = "123 456 789",
1357 		.result = OP_EPERM,
1358 	},
1359 	{
1360 		"C prog: deny access by name",
1361 		.prog_file = "./test_sysctl_prog.o",
1362 		.attach_type = BPF_CGROUP_SYSCTL,
1363 		.sysctl = "net/ipv4/route/mtu_expires",
1364 		.open_flags = O_RDONLY,
1365 		.result = OP_EPERM,
1366 	},
1367 	{
1368 		"C prog: read tcp_mem",
1369 		.prog_file = "./test_sysctl_prog.o",
1370 		.attach_type = BPF_CGROUP_SYSCTL,
1371 		.sysctl = "net/ipv4/tcp_mem",
1372 		.open_flags = O_RDONLY,
1373 		.result = SUCCESS,
1374 	},
1375 };
1376 
1377 static size_t probe_prog_length(const struct bpf_insn *fp)
1378 {
1379 	size_t len;
1380 
1381 	for (len = MAX_INSNS - 1; len > 0; --len)
1382 		if (fp[len].code != 0 || fp[len].imm != 0)
1383 			break;
1384 	return len + 1;
1385 }
1386 
1387 static int fixup_sysctl_value(const char *buf, size_t buf_len,
1388 			      struct bpf_insn *prog, size_t insn_num)
1389 {
1390 	union {
1391 		uint8_t raw[sizeof(uint64_t)];
1392 		uint64_t num;
1393 	} value = {};
1394 
1395 	if (buf_len > sizeof(value)) {
1396 		log_err("Value is too big (%zd) to use in fixup", buf_len);
1397 		return -1;
1398 	}
1399 	if (prog[insn_num].code != (BPF_LD | BPF_DW | BPF_IMM)) {
1400 		log_err("Can fixup only BPF_LD_IMM64 insns");
1401 		return -1;
1402 	}
1403 
1404 	memcpy(value.raw, buf, buf_len);
1405 	prog[insn_num].imm = (uint32_t)value.num;
1406 	prog[insn_num + 1].imm = (uint32_t)(value.num >> 32);
1407 
1408 	return 0;
1409 }
1410 
1411 static int load_sysctl_prog_insns(struct sysctl_test *test,
1412 				  const char *sysctl_path)
1413 {
1414 	struct bpf_insn *prog = test->insns;
1415 	struct bpf_load_program_attr attr;
1416 	int ret;
1417 
1418 	memset(&attr, 0, sizeof(struct bpf_load_program_attr));
1419 	attr.prog_type = BPF_PROG_TYPE_CGROUP_SYSCTL;
1420 	attr.insns = prog;
1421 	attr.insns_cnt = probe_prog_length(attr.insns);
1422 	attr.license = "GPL";
1423 
1424 	if (test->fixup_value_insn) {
1425 		char buf[128];
1426 		ssize_t len;
1427 		int fd;
1428 
1429 		fd = open(sysctl_path, O_RDONLY | O_CLOEXEC);
1430 		if (fd < 0) {
1431 			log_err("open(%s) failed", sysctl_path);
1432 			return -1;
1433 		}
1434 		len = read(fd, buf, sizeof(buf));
1435 		if (len == -1) {
1436 			log_err("read(%s) failed", sysctl_path);
1437 			close(fd);
1438 			return -1;
1439 		}
1440 		close(fd);
1441 		if (fixup_sysctl_value(buf, len, prog, test->fixup_value_insn))
1442 			return -1;
1443 	}
1444 
1445 	ret = bpf_load_program_xattr(&attr, bpf_log_buf, BPF_LOG_BUF_SIZE);
1446 	if (ret < 0 && test->result != LOAD_REJECT) {
1447 		log_err(">>> Loading program error.\n"
1448 			">>> Verifier output:\n%s\n-------\n", bpf_log_buf);
1449 	}
1450 
1451 	return ret;
1452 }
1453 
1454 static int load_sysctl_prog_file(struct sysctl_test *test)
1455 {
1456 	struct bpf_prog_load_attr attr;
1457 	struct bpf_object *obj;
1458 	int prog_fd;
1459 
1460 	memset(&attr, 0, sizeof(struct bpf_prog_load_attr));
1461 	attr.file = test->prog_file;
1462 	attr.prog_type = BPF_PROG_TYPE_CGROUP_SYSCTL;
1463 
1464 	if (bpf_prog_load_xattr(&attr, &obj, &prog_fd)) {
1465 		if (test->result != LOAD_REJECT)
1466 			log_err(">>> Loading program (%s) error.\n",
1467 				test->prog_file);
1468 		return -1;
1469 	}
1470 
1471 	return prog_fd;
1472 }
1473 
1474 static int load_sysctl_prog(struct sysctl_test *test, const char *sysctl_path)
1475 {
1476 		return test->prog_file
1477 			? load_sysctl_prog_file(test)
1478 			: load_sysctl_prog_insns(test, sysctl_path);
1479 }
1480 
1481 static int access_sysctl(const char *sysctl_path,
1482 			 const struct sysctl_test *test)
1483 {
1484 	int err = 0;
1485 	int fd;
1486 
1487 	fd = open(sysctl_path, test->open_flags | O_CLOEXEC);
1488 	if (fd < 0)
1489 		return fd;
1490 
1491 	if (test->seek && lseek(fd, test->seek, SEEK_SET) == -1) {
1492 		log_err("lseek(%d) failed", test->seek);
1493 		goto err;
1494 	}
1495 
1496 	if (test->open_flags == O_RDONLY) {
1497 		char buf[128];
1498 
1499 		if (read(fd, buf, sizeof(buf)) == -1)
1500 			goto err;
1501 		if (test->oldval &&
1502 		    strncmp(buf, test->oldval, strlen(test->oldval))) {
1503 			log_err("Read value %s != %s", buf, test->oldval);
1504 			goto err;
1505 		}
1506 	} else if (test->open_flags == O_WRONLY) {
1507 		if (!test->newval) {
1508 			log_err("New value for sysctl is not set");
1509 			goto err;
1510 		}
1511 		if (write(fd, test->newval, strlen(test->newval)) == -1)
1512 			goto err;
1513 	} else {
1514 		log_err("Unexpected sysctl access: neither read nor write");
1515 		goto err;
1516 	}
1517 
1518 	goto out;
1519 err:
1520 	err = -1;
1521 out:
1522 	close(fd);
1523 	return err;
1524 }
1525 
1526 static int run_test_case(int cgfd, struct sysctl_test *test)
1527 {
1528 	enum bpf_attach_type atype = test->attach_type;
1529 	char sysctl_path[128];
1530 	int progfd = -1;
1531 	int err = 0;
1532 
1533 	printf("Test case: %s .. ", test->descr);
1534 
1535 	snprintf(sysctl_path, sizeof(sysctl_path), "/proc/sys/%s",
1536 		 test->sysctl);
1537 
1538 	progfd = load_sysctl_prog(test, sysctl_path);
1539 	if (progfd < 0) {
1540 		if (test->result == LOAD_REJECT)
1541 			goto out;
1542 		else
1543 			goto err;
1544 	}
1545 
1546 	if (bpf_prog_attach(progfd, cgfd, atype, BPF_F_ALLOW_OVERRIDE) == -1) {
1547 		if (test->result == ATTACH_REJECT)
1548 			goto out;
1549 		else
1550 			goto err;
1551 	}
1552 
1553 	errno = 0;
1554 	if (access_sysctl(sysctl_path, test) == -1) {
1555 		if (test->result == OP_EPERM && errno == EPERM)
1556 			goto out;
1557 		else
1558 			goto err;
1559 	}
1560 
1561 	if (test->result != SUCCESS) {
1562 		log_err("Unexpected success");
1563 		goto err;
1564 	}
1565 
1566 	goto out;
1567 err:
1568 	err = -1;
1569 out:
1570 	/* Detaching w/o checking return code: best effort attempt. */
1571 	if (progfd != -1)
1572 		bpf_prog_detach(cgfd, atype);
1573 	close(progfd);
1574 	printf("[%s]\n", err ? "FAIL" : "PASS");
1575 	return err;
1576 }
1577 
1578 static int run_tests(int cgfd)
1579 {
1580 	int passes = 0;
1581 	int fails = 0;
1582 	int i;
1583 
1584 	for (i = 0; i < ARRAY_SIZE(tests); ++i) {
1585 		if (run_test_case(cgfd, &tests[i]))
1586 			++fails;
1587 		else
1588 			++passes;
1589 	}
1590 	printf("Summary: %d PASSED, %d FAILED\n", passes, fails);
1591 	return fails ? -1 : 0;
1592 }
1593 
1594 int main(int argc, char **argv)
1595 {
1596 	int cgfd = -1;
1597 	int err = 0;
1598 
1599 	if (setup_cgroup_environment())
1600 		goto err;
1601 
1602 	cgfd = create_and_get_cgroup(CG_PATH);
1603 	if (cgfd < 0)
1604 		goto err;
1605 
1606 	if (join_cgroup(CG_PATH))
1607 		goto err;
1608 
1609 	if (run_tests(cgfd))
1610 		goto err;
1611 
1612 	goto out;
1613 err:
1614 	err = -1;
1615 out:
1616 	close(cgfd);
1617 	cleanup_cgroup_environment();
1618 	return err;
1619 }
1620