staging: rtl8192u: remove redundant assignment to pointer crypt
[linux/fpc-iii.git] / tools / testing / selftests / bpf / test_sockopt.c
blob23bd0819382dbd7ea8a1d7c8b06bcef5a1d50a35
1 // SPDX-License-Identifier: GPL-2.0
3 #include <errno.h>
4 #include <stdio.h>
5 #include <unistd.h>
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <netinet/in.h>
10 #include <linux/filter.h>
11 #include <bpf/bpf.h>
12 #include <bpf/libbpf.h>
14 #include "bpf_rlimit.h"
15 #include "bpf_util.h"
16 #include "cgroup_helpers.h"
18 #define CG_PATH "/sockopt"
20 static char bpf_log_buf[4096];
21 static bool verbose;
23 enum sockopt_test_error {
24 OK = 0,
25 DENY_LOAD,
26 DENY_ATTACH,
27 EPERM_GETSOCKOPT,
28 EFAULT_GETSOCKOPT,
29 EPERM_SETSOCKOPT,
30 EFAULT_SETSOCKOPT,
33 static struct sockopt_test {
34 const char *descr;
35 const struct bpf_insn insns[64];
36 enum bpf_attach_type attach_type;
37 enum bpf_attach_type expected_attach_type;
39 int set_optname;
40 int set_level;
41 const char set_optval[64];
42 socklen_t set_optlen;
44 int get_optname;
45 int get_level;
46 const char get_optval[64];
47 socklen_t get_optlen;
48 socklen_t get_optlen_ret;
50 enum sockopt_test_error error;
51 } tests[] = {
53 /* ==================== getsockopt ==================== */
56 .descr = "getsockopt: no expected_attach_type",
57 .insns = {
58 /* return 1 */
59 BPF_MOV64_IMM(BPF_REG_0, 1),
60 BPF_EXIT_INSN(),
63 .attach_type = BPF_CGROUP_GETSOCKOPT,
64 .expected_attach_type = 0,
65 .error = DENY_LOAD,
68 .descr = "getsockopt: wrong expected_attach_type",
69 .insns = {
70 /* return 1 */
71 BPF_MOV64_IMM(BPF_REG_0, 1),
72 BPF_EXIT_INSN(),
75 .attach_type = BPF_CGROUP_GETSOCKOPT,
76 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
77 .error = DENY_ATTACH,
80 .descr = "getsockopt: bypass bpf hook",
81 .insns = {
82 /* return 1 */
83 BPF_MOV64_IMM(BPF_REG_0, 1),
84 BPF_EXIT_INSN(),
86 .attach_type = BPF_CGROUP_GETSOCKOPT,
87 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
89 .get_level = SOL_IP,
90 .set_level = SOL_IP,
92 .get_optname = IP_TOS,
93 .set_optname = IP_TOS,
95 .set_optval = { 1 << 3 },
96 .set_optlen = 1,
98 .get_optval = { 1 << 3 },
99 .get_optlen = 1,
102 .descr = "getsockopt: return EPERM from bpf hook",
103 .insns = {
104 BPF_MOV64_IMM(BPF_REG_0, 0),
105 BPF_EXIT_INSN(),
107 .attach_type = BPF_CGROUP_GETSOCKOPT,
108 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
110 .get_level = SOL_IP,
111 .get_optname = IP_TOS,
113 .get_optlen = 1,
114 .error = EPERM_GETSOCKOPT,
117 .descr = "getsockopt: no optval bounds check, deny loading",
118 .insns = {
119 /* r6 = ctx->optval */
120 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
121 offsetof(struct bpf_sockopt, optval)),
123 /* ctx->optval[0] = 0x80 */
124 BPF_MOV64_IMM(BPF_REG_0, 0x80),
125 BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0),
127 /* return 1 */
128 BPF_MOV64_IMM(BPF_REG_0, 1),
129 BPF_EXIT_INSN(),
131 .attach_type = BPF_CGROUP_GETSOCKOPT,
132 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
133 .error = DENY_LOAD,
136 .descr = "getsockopt: read ctx->level",
137 .insns = {
138 /* r6 = ctx->level */
139 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
140 offsetof(struct bpf_sockopt, level)),
142 /* if (ctx->level == 123) { */
143 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
144 /* ctx->retval = 0 */
145 BPF_MOV64_IMM(BPF_REG_0, 0),
146 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
147 offsetof(struct bpf_sockopt, retval)),
148 /* return 1 */
149 BPF_MOV64_IMM(BPF_REG_0, 1),
150 BPF_JMP_A(1),
151 /* } else { */
152 /* return 0 */
153 BPF_MOV64_IMM(BPF_REG_0, 0),
154 /* } */
155 BPF_EXIT_INSN(),
157 .attach_type = BPF_CGROUP_GETSOCKOPT,
158 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
160 .get_level = 123,
162 .get_optlen = 1,
165 .descr = "getsockopt: deny writing to ctx->level",
166 .insns = {
167 /* ctx->level = 1 */
168 BPF_MOV64_IMM(BPF_REG_0, 1),
169 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
170 offsetof(struct bpf_sockopt, level)),
171 BPF_EXIT_INSN(),
173 .attach_type = BPF_CGROUP_GETSOCKOPT,
174 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
176 .error = DENY_LOAD,
179 .descr = "getsockopt: read ctx->optname",
180 .insns = {
181 /* r6 = ctx->optname */
182 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
183 offsetof(struct bpf_sockopt, optname)),
185 /* if (ctx->optname == 123) { */
186 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
187 /* ctx->retval = 0 */
188 BPF_MOV64_IMM(BPF_REG_0, 0),
189 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
190 offsetof(struct bpf_sockopt, retval)),
191 /* return 1 */
192 BPF_MOV64_IMM(BPF_REG_0, 1),
193 BPF_JMP_A(1),
194 /* } else { */
195 /* return 0 */
196 BPF_MOV64_IMM(BPF_REG_0, 0),
197 /* } */
198 BPF_EXIT_INSN(),
200 .attach_type = BPF_CGROUP_GETSOCKOPT,
201 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
203 .get_optname = 123,
205 .get_optlen = 1,
208 .descr = "getsockopt: read ctx->retval",
209 .insns = {
210 /* r6 = ctx->retval */
211 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
212 offsetof(struct bpf_sockopt, retval)),
214 /* return 1 */
215 BPF_MOV64_IMM(BPF_REG_0, 1),
216 BPF_EXIT_INSN(),
218 .attach_type = BPF_CGROUP_GETSOCKOPT,
219 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
221 .get_level = SOL_IP,
222 .get_optname = IP_TOS,
223 .get_optlen = 1,
226 .descr = "getsockopt: deny writing to ctx->optname",
227 .insns = {
228 /* ctx->optname = 1 */
229 BPF_MOV64_IMM(BPF_REG_0, 1),
230 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
231 offsetof(struct bpf_sockopt, optname)),
232 BPF_EXIT_INSN(),
234 .attach_type = BPF_CGROUP_GETSOCKOPT,
235 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
237 .error = DENY_LOAD,
240 .descr = "getsockopt: read ctx->optlen",
241 .insns = {
242 /* r6 = ctx->optlen */
243 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
244 offsetof(struct bpf_sockopt, optlen)),
246 /* if (ctx->optlen == 64) { */
247 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
248 /* ctx->retval = 0 */
249 BPF_MOV64_IMM(BPF_REG_0, 0),
250 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
251 offsetof(struct bpf_sockopt, retval)),
252 /* return 1 */
253 BPF_MOV64_IMM(BPF_REG_0, 1),
254 BPF_JMP_A(1),
255 /* } else { */
256 /* return 0 */
257 BPF_MOV64_IMM(BPF_REG_0, 0),
258 /* } */
259 BPF_EXIT_INSN(),
261 .attach_type = BPF_CGROUP_GETSOCKOPT,
262 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
264 .get_optlen = 64,
267 .descr = "getsockopt: deny bigger ctx->optlen",
268 .insns = {
269 /* ctx->optlen = 65 */
270 BPF_MOV64_IMM(BPF_REG_0, 65),
271 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
272 offsetof(struct bpf_sockopt, optlen)),
274 /* ctx->retval = 0 */
275 BPF_MOV64_IMM(BPF_REG_0, 0),
276 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
277 offsetof(struct bpf_sockopt, retval)),
279 /* return 1 */
280 BPF_MOV64_IMM(BPF_REG_0, 1),
281 BPF_EXIT_INSN(),
283 .attach_type = BPF_CGROUP_GETSOCKOPT,
284 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
286 .get_optlen = 64,
288 .error = EFAULT_GETSOCKOPT,
291 .descr = "getsockopt: deny arbitrary ctx->retval",
292 .insns = {
293 /* ctx->retval = 123 */
294 BPF_MOV64_IMM(BPF_REG_0, 123),
295 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
296 offsetof(struct bpf_sockopt, retval)),
298 /* return 1 */
299 BPF_MOV64_IMM(BPF_REG_0, 1),
300 BPF_EXIT_INSN(),
302 .attach_type = BPF_CGROUP_GETSOCKOPT,
303 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
305 .get_optlen = 64,
307 .error = EFAULT_GETSOCKOPT,
310 .descr = "getsockopt: support smaller ctx->optlen",
311 .insns = {
312 /* ctx->optlen = 32 */
313 BPF_MOV64_IMM(BPF_REG_0, 32),
314 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
315 offsetof(struct bpf_sockopt, optlen)),
316 /* ctx->retval = 0 */
317 BPF_MOV64_IMM(BPF_REG_0, 0),
318 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
319 offsetof(struct bpf_sockopt, retval)),
320 /* return 1 */
321 BPF_MOV64_IMM(BPF_REG_0, 1),
322 BPF_EXIT_INSN(),
324 .attach_type = BPF_CGROUP_GETSOCKOPT,
325 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
327 .get_optlen = 64,
328 .get_optlen_ret = 32,
331 .descr = "getsockopt: deny writing to ctx->optval",
332 .insns = {
333 /* ctx->optval = 1 */
334 BPF_MOV64_IMM(BPF_REG_0, 1),
335 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
336 offsetof(struct bpf_sockopt, optval)),
337 BPF_EXIT_INSN(),
339 .attach_type = BPF_CGROUP_GETSOCKOPT,
340 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
342 .error = DENY_LOAD,
345 .descr = "getsockopt: deny writing to ctx->optval_end",
346 .insns = {
347 /* ctx->optval_end = 1 */
348 BPF_MOV64_IMM(BPF_REG_0, 1),
349 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
350 offsetof(struct bpf_sockopt, optval_end)),
351 BPF_EXIT_INSN(),
353 .attach_type = BPF_CGROUP_GETSOCKOPT,
354 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
356 .error = DENY_LOAD,
359 .descr = "getsockopt: rewrite value",
360 .insns = {
361 /* r6 = ctx->optval */
362 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
363 offsetof(struct bpf_sockopt, optval)),
364 /* r2 = ctx->optval */
365 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
366 /* r6 = ctx->optval + 1 */
367 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
369 /* r7 = ctx->optval_end */
370 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
371 offsetof(struct bpf_sockopt, optval_end)),
373 /* if (ctx->optval + 1 <= ctx->optval_end) { */
374 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
375 /* ctx->optval[0] = 0xF0 */
376 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0),
377 /* } */
379 /* ctx->retval = 0 */
380 BPF_MOV64_IMM(BPF_REG_0, 0),
381 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
382 offsetof(struct bpf_sockopt, retval)),
384 /* return 1*/
385 BPF_MOV64_IMM(BPF_REG_0, 1),
386 BPF_EXIT_INSN(),
388 .attach_type = BPF_CGROUP_GETSOCKOPT,
389 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
391 .get_level = SOL_IP,
392 .get_optname = IP_TOS,
394 .get_optval = { 0xF0 },
395 .get_optlen = 1,
398 /* ==================== setsockopt ==================== */
401 .descr = "setsockopt: no expected_attach_type",
402 .insns = {
403 /* return 1 */
404 BPF_MOV64_IMM(BPF_REG_0, 1),
405 BPF_EXIT_INSN(),
408 .attach_type = BPF_CGROUP_SETSOCKOPT,
409 .expected_attach_type = 0,
410 .error = DENY_LOAD,
413 .descr = "setsockopt: wrong expected_attach_type",
414 .insns = {
415 /* return 1 */
416 BPF_MOV64_IMM(BPF_REG_0, 1),
417 BPF_EXIT_INSN(),
420 .attach_type = BPF_CGROUP_SETSOCKOPT,
421 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
422 .error = DENY_ATTACH,
425 .descr = "setsockopt: bypass bpf hook",
426 .insns = {
427 /* return 1 */
428 BPF_MOV64_IMM(BPF_REG_0, 1),
429 BPF_EXIT_INSN(),
431 .attach_type = BPF_CGROUP_SETSOCKOPT,
432 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
434 .get_level = SOL_IP,
435 .set_level = SOL_IP,
437 .get_optname = IP_TOS,
438 .set_optname = IP_TOS,
440 .set_optval = { 1 << 3 },
441 .set_optlen = 1,
443 .get_optval = { 1 << 3 },
444 .get_optlen = 1,
447 .descr = "setsockopt: return EPERM from bpf hook",
448 .insns = {
449 /* return 0 */
450 BPF_MOV64_IMM(BPF_REG_0, 0),
451 BPF_EXIT_INSN(),
453 .attach_type = BPF_CGROUP_SETSOCKOPT,
454 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
456 .set_level = SOL_IP,
457 .set_optname = IP_TOS,
459 .set_optlen = 1,
460 .error = EPERM_SETSOCKOPT,
463 .descr = "setsockopt: no optval bounds check, deny loading",
464 .insns = {
465 /* r6 = ctx->optval */
466 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
467 offsetof(struct bpf_sockopt, optval)),
469 /* r0 = ctx->optval[0] */
470 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
472 /* return 1 */
473 BPF_MOV64_IMM(BPF_REG_0, 1),
474 BPF_EXIT_INSN(),
476 .attach_type = BPF_CGROUP_SETSOCKOPT,
477 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
478 .error = DENY_LOAD,
481 .descr = "setsockopt: read ctx->level",
482 .insns = {
483 /* r6 = ctx->level */
484 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
485 offsetof(struct bpf_sockopt, level)),
487 /* if (ctx->level == 123) { */
488 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
489 /* ctx->optlen = -1 */
490 BPF_MOV64_IMM(BPF_REG_0, -1),
491 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
492 offsetof(struct bpf_sockopt, optlen)),
493 /* return 1 */
494 BPF_MOV64_IMM(BPF_REG_0, 1),
495 BPF_JMP_A(1),
496 /* } else { */
497 /* return 0 */
498 BPF_MOV64_IMM(BPF_REG_0, 0),
499 /* } */
500 BPF_EXIT_INSN(),
502 .attach_type = BPF_CGROUP_SETSOCKOPT,
503 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
505 .set_level = 123,
507 .set_optlen = 1,
510 .descr = "setsockopt: allow changing ctx->level",
511 .insns = {
512 /* ctx->level = SOL_IP */
513 BPF_MOV64_IMM(BPF_REG_0, SOL_IP),
514 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
515 offsetof(struct bpf_sockopt, level)),
516 /* return 1 */
517 BPF_MOV64_IMM(BPF_REG_0, 1),
518 BPF_EXIT_INSN(),
520 .attach_type = BPF_CGROUP_SETSOCKOPT,
521 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
523 .get_level = SOL_IP,
524 .set_level = 234, /* should be rewritten to SOL_IP */
526 .get_optname = IP_TOS,
527 .set_optname = IP_TOS,
529 .set_optval = { 1 << 3 },
530 .set_optlen = 1,
531 .get_optval = { 1 << 3 },
532 .get_optlen = 1,
535 .descr = "setsockopt: read ctx->optname",
536 .insns = {
537 /* r6 = ctx->optname */
538 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
539 offsetof(struct bpf_sockopt, optname)),
541 /* if (ctx->optname == 123) { */
542 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
543 /* ctx->optlen = -1 */
544 BPF_MOV64_IMM(BPF_REG_0, -1),
545 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
546 offsetof(struct bpf_sockopt, optlen)),
547 /* return 1 */
548 BPF_MOV64_IMM(BPF_REG_0, 1),
549 BPF_JMP_A(1),
550 /* } else { */
551 /* return 0 */
552 BPF_MOV64_IMM(BPF_REG_0, 0),
553 /* } */
554 BPF_EXIT_INSN(),
556 .attach_type = BPF_CGROUP_SETSOCKOPT,
557 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
559 .set_optname = 123,
561 .set_optlen = 1,
564 .descr = "setsockopt: allow changing ctx->optname",
565 .insns = {
566 /* ctx->optname = IP_TOS */
567 BPF_MOV64_IMM(BPF_REG_0, IP_TOS),
568 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
569 offsetof(struct bpf_sockopt, optname)),
570 /* return 1 */
571 BPF_MOV64_IMM(BPF_REG_0, 1),
572 BPF_EXIT_INSN(),
574 .attach_type = BPF_CGROUP_SETSOCKOPT,
575 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
577 .get_level = SOL_IP,
578 .set_level = SOL_IP,
580 .get_optname = IP_TOS,
581 .set_optname = 456, /* should be rewritten to IP_TOS */
583 .set_optval = { 1 << 3 },
584 .set_optlen = 1,
585 .get_optval = { 1 << 3 },
586 .get_optlen = 1,
589 .descr = "setsockopt: read ctx->optlen",
590 .insns = {
591 /* r6 = ctx->optlen */
592 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
593 offsetof(struct bpf_sockopt, optlen)),
595 /* if (ctx->optlen == 64) { */
596 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
597 /* ctx->optlen = -1 */
598 BPF_MOV64_IMM(BPF_REG_0, -1),
599 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
600 offsetof(struct bpf_sockopt, optlen)),
601 /* return 1 */
602 BPF_MOV64_IMM(BPF_REG_0, 1),
603 BPF_JMP_A(1),
604 /* } else { */
605 /* return 0 */
606 BPF_MOV64_IMM(BPF_REG_0, 0),
607 /* } */
608 BPF_EXIT_INSN(),
610 .attach_type = BPF_CGROUP_SETSOCKOPT,
611 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
613 .set_optlen = 64,
616 .descr = "setsockopt: ctx->optlen == -1 is ok",
617 .insns = {
618 /* ctx->optlen = -1 */
619 BPF_MOV64_IMM(BPF_REG_0, -1),
620 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
621 offsetof(struct bpf_sockopt, optlen)),
622 /* return 1 */
623 BPF_MOV64_IMM(BPF_REG_0, 1),
624 BPF_EXIT_INSN(),
626 .attach_type = BPF_CGROUP_SETSOCKOPT,
627 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
629 .set_optlen = 64,
632 .descr = "setsockopt: deny ctx->optlen < 0 (except -1)",
633 .insns = {
634 /* ctx->optlen = -2 */
635 BPF_MOV64_IMM(BPF_REG_0, -2),
636 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
637 offsetof(struct bpf_sockopt, optlen)),
638 /* return 1 */
639 BPF_MOV64_IMM(BPF_REG_0, 1),
640 BPF_EXIT_INSN(),
642 .attach_type = BPF_CGROUP_SETSOCKOPT,
643 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
645 .set_optlen = 4,
647 .error = EFAULT_SETSOCKOPT,
650 .descr = "setsockopt: deny ctx->optlen > input optlen",
651 .insns = {
652 /* ctx->optlen = 65 */
653 BPF_MOV64_IMM(BPF_REG_0, 65),
654 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
655 offsetof(struct bpf_sockopt, optlen)),
656 BPF_MOV64_IMM(BPF_REG_0, 1),
657 BPF_EXIT_INSN(),
659 .attach_type = BPF_CGROUP_SETSOCKOPT,
660 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
662 .set_optlen = 64,
664 .error = EFAULT_SETSOCKOPT,
667 .descr = "setsockopt: allow changing ctx->optlen within bounds",
668 .insns = {
669 /* r6 = ctx->optval */
670 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
671 offsetof(struct bpf_sockopt, optval)),
672 /* r2 = ctx->optval */
673 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
674 /* r6 = ctx->optval + 1 */
675 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
677 /* r7 = ctx->optval_end */
678 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
679 offsetof(struct bpf_sockopt, optval_end)),
681 /* if (ctx->optval + 1 <= ctx->optval_end) { */
682 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
683 /* ctx->optval[0] = 1 << 3 */
684 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 1 << 3),
685 /* } */
687 /* ctx->optlen = 1 */
688 BPF_MOV64_IMM(BPF_REG_0, 1),
689 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
690 offsetof(struct bpf_sockopt, optlen)),
692 /* return 1*/
693 BPF_MOV64_IMM(BPF_REG_0, 1),
694 BPF_EXIT_INSN(),
696 .attach_type = BPF_CGROUP_SETSOCKOPT,
697 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
699 .get_level = SOL_IP,
700 .set_level = SOL_IP,
702 .get_optname = IP_TOS,
703 .set_optname = IP_TOS,
705 .set_optval = { 1, 1, 1, 1 },
706 .set_optlen = 4,
707 .get_optval = { 1 << 3 },
708 .get_optlen = 1,
711 .descr = "setsockopt: deny write ctx->retval",
712 .insns = {
713 /* ctx->retval = 0 */
714 BPF_MOV64_IMM(BPF_REG_0, 0),
715 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
716 offsetof(struct bpf_sockopt, retval)),
718 /* return 1 */
719 BPF_MOV64_IMM(BPF_REG_0, 1),
720 BPF_EXIT_INSN(),
722 .attach_type = BPF_CGROUP_SETSOCKOPT,
723 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
725 .error = DENY_LOAD,
728 .descr = "setsockopt: deny read ctx->retval",
729 .insns = {
730 /* r6 = ctx->retval */
731 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
732 offsetof(struct bpf_sockopt, retval)),
734 /* return 1 */
735 BPF_MOV64_IMM(BPF_REG_0, 1),
736 BPF_EXIT_INSN(),
738 .attach_type = BPF_CGROUP_SETSOCKOPT,
739 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
741 .error = DENY_LOAD,
744 .descr = "setsockopt: deny writing to ctx->optval",
745 .insns = {
746 /* ctx->optval = 1 */
747 BPF_MOV64_IMM(BPF_REG_0, 1),
748 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
749 offsetof(struct bpf_sockopt, optval)),
750 BPF_EXIT_INSN(),
752 .attach_type = BPF_CGROUP_SETSOCKOPT,
753 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
755 .error = DENY_LOAD,
758 .descr = "setsockopt: deny writing to ctx->optval_end",
759 .insns = {
760 /* ctx->optval_end = 1 */
761 BPF_MOV64_IMM(BPF_REG_0, 1),
762 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
763 offsetof(struct bpf_sockopt, optval_end)),
764 BPF_EXIT_INSN(),
766 .attach_type = BPF_CGROUP_SETSOCKOPT,
767 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
769 .error = DENY_LOAD,
772 .descr = "setsockopt: allow IP_TOS <= 128",
773 .insns = {
774 /* r6 = ctx->optval */
775 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
776 offsetof(struct bpf_sockopt, optval)),
777 /* r7 = ctx->optval + 1 */
778 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
779 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
781 /* r8 = ctx->optval_end */
782 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
783 offsetof(struct bpf_sockopt, optval_end)),
785 /* if (ctx->optval + 1 <= ctx->optval_end) { */
786 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
788 /* r9 = ctx->optval[0] */
789 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
791 /* if (ctx->optval[0] < 128) */
792 BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
793 BPF_MOV64_IMM(BPF_REG_0, 1),
794 BPF_JMP_A(1),
795 /* } */
797 /* } else { */
798 BPF_MOV64_IMM(BPF_REG_0, 0),
799 /* } */
801 BPF_EXIT_INSN(),
803 .attach_type = BPF_CGROUP_SETSOCKOPT,
804 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
806 .get_level = SOL_IP,
807 .set_level = SOL_IP,
809 .get_optname = IP_TOS,
810 .set_optname = IP_TOS,
812 .set_optval = { 0x80 },
813 .set_optlen = 1,
814 .get_optval = { 0x80 },
815 .get_optlen = 1,
818 .descr = "setsockopt: deny IP_TOS > 128",
819 .insns = {
820 /* r6 = ctx->optval */
821 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
822 offsetof(struct bpf_sockopt, optval)),
823 /* r7 = ctx->optval + 1 */
824 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
825 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
827 /* r8 = ctx->optval_end */
828 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
829 offsetof(struct bpf_sockopt, optval_end)),
831 /* if (ctx->optval + 1 <= ctx->optval_end) { */
832 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
834 /* r9 = ctx->optval[0] */
835 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
837 /* if (ctx->optval[0] < 128) */
838 BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
839 BPF_MOV64_IMM(BPF_REG_0, 1),
840 BPF_JMP_A(1),
841 /* } */
843 /* } else { */
844 BPF_MOV64_IMM(BPF_REG_0, 0),
845 /* } */
847 BPF_EXIT_INSN(),
849 .attach_type = BPF_CGROUP_SETSOCKOPT,
850 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
852 .get_level = SOL_IP,
853 .set_level = SOL_IP,
855 .get_optname = IP_TOS,
856 .set_optname = IP_TOS,
858 .set_optval = { 0x81 },
859 .set_optlen = 1,
860 .get_optval = { 0x00 },
861 .get_optlen = 1,
863 .error = EPERM_SETSOCKOPT,
867 static int load_prog(const struct bpf_insn *insns,
868 enum bpf_attach_type expected_attach_type)
870 struct bpf_load_program_attr attr = {
871 .prog_type = BPF_PROG_TYPE_CGROUP_SOCKOPT,
872 .expected_attach_type = expected_attach_type,
873 .insns = insns,
874 .license = "GPL",
875 .log_level = 2,
877 int fd;
879 for (;
880 insns[attr.insns_cnt].code != (BPF_JMP | BPF_EXIT);
881 attr.insns_cnt++) {
883 attr.insns_cnt++;
885 fd = bpf_load_program_xattr(&attr, bpf_log_buf, sizeof(bpf_log_buf));
886 if (verbose && fd < 0)
887 fprintf(stderr, "%s\n", bpf_log_buf);
889 return fd;
892 static int run_test(int cgroup_fd, struct sockopt_test *test)
894 int sock_fd, err, prog_fd;
895 void *optval = NULL;
896 int ret = 0;
898 prog_fd = load_prog(test->insns, test->expected_attach_type);
899 if (prog_fd < 0) {
900 if (test->error == DENY_LOAD)
901 return 0;
903 log_err("Failed to load BPF program");
904 return -1;
907 err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0);
908 if (err < 0) {
909 if (test->error == DENY_ATTACH)
910 goto close_prog_fd;
912 log_err("Failed to attach BPF program");
913 ret = -1;
914 goto close_prog_fd;
917 sock_fd = socket(AF_INET, SOCK_STREAM, 0);
918 if (sock_fd < 0) {
919 log_err("Failed to create AF_INET socket");
920 ret = -1;
921 goto detach_prog;
924 if (test->set_optlen) {
925 err = setsockopt(sock_fd, test->set_level, test->set_optname,
926 test->set_optval, test->set_optlen);
927 if (err) {
928 if (errno == EPERM && test->error == EPERM_SETSOCKOPT)
929 goto close_sock_fd;
930 if (errno == EFAULT && test->error == EFAULT_SETSOCKOPT)
931 goto free_optval;
933 log_err("Failed to call setsockopt");
934 ret = -1;
935 goto close_sock_fd;
939 if (test->get_optlen) {
940 optval = malloc(test->get_optlen);
941 socklen_t optlen = test->get_optlen;
942 socklen_t expected_get_optlen = test->get_optlen_ret ?:
943 test->get_optlen;
945 err = getsockopt(sock_fd, test->get_level, test->get_optname,
946 optval, &optlen);
947 if (err) {
948 if (errno == EPERM && test->error == EPERM_GETSOCKOPT)
949 goto free_optval;
950 if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT)
951 goto free_optval;
953 log_err("Failed to call getsockopt");
954 ret = -1;
955 goto free_optval;
958 if (optlen != expected_get_optlen) {
959 errno = 0;
960 log_err("getsockopt returned unexpected optlen");
961 ret = -1;
962 goto free_optval;
965 if (memcmp(optval, test->get_optval, optlen) != 0) {
966 errno = 0;
967 log_err("getsockopt returned unexpected optval");
968 ret = -1;
969 goto free_optval;
973 ret = test->error != OK;
975 free_optval:
976 free(optval);
977 close_sock_fd:
978 close(sock_fd);
979 detach_prog:
980 bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);
981 close_prog_fd:
982 close(prog_fd);
983 return ret;
986 int main(int args, char **argv)
988 int err = EXIT_FAILURE, error_cnt = 0;
989 int cgroup_fd, i;
991 if (setup_cgroup_environment())
992 goto cleanup_obj;
994 cgroup_fd = create_and_get_cgroup(CG_PATH);
995 if (cgroup_fd < 0)
996 goto cleanup_cgroup_env;
998 if (join_cgroup(CG_PATH))
999 goto cleanup_cgroup;
1001 for (i = 0; i < ARRAY_SIZE(tests); i++) {
1002 int err = run_test(cgroup_fd, &tests[i]);
1004 if (err)
1005 error_cnt++;
1007 printf("#%d %s: %s\n", i, err ? "FAIL" : "PASS",
1008 tests[i].descr);
1011 printf("Summary: %ld PASSED, %d FAILED\n",
1012 ARRAY_SIZE(tests) - error_cnt, error_cnt);
1013 err = error_cnt ? EXIT_FAILURE : EXIT_SUCCESS;
1015 cleanup_cgroup:
1016 close(cgroup_fd);
1017 cleanup_cgroup_env:
1018 cleanup_cgroup_environment();
1019 cleanup_obj:
1020 return err;