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