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