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