WIP FPC-III support
[linux/fpc-iii.git] / tools / testing / selftests / bpf / prog_tests / sockopt.c
blob3e8517a8395ad6ec0a9b68b7dd8ce60c6b2fa1d3
1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include "cgroup_helpers.h"
5 static char bpf_log_buf[4096];
6 static bool verbose;
8 enum sockopt_test_error {
9 OK = 0,
10 DENY_LOAD,
11 DENY_ATTACH,
12 EPERM_GETSOCKOPT,
13 EFAULT_GETSOCKOPT,
14 EPERM_SETSOCKOPT,
15 EFAULT_SETSOCKOPT,
18 static struct sockopt_test {
19 const char *descr;
20 const struct bpf_insn insns[64];
21 enum bpf_attach_type attach_type;
22 enum bpf_attach_type expected_attach_type;
24 int set_optname;
25 int set_level;
26 const char set_optval[64];
27 socklen_t set_optlen;
29 int get_optname;
30 int get_level;
31 const char get_optval[64];
32 socklen_t get_optlen;
33 socklen_t get_optlen_ret;
35 enum sockopt_test_error error;
36 } tests[] = {
38 /* ==================== getsockopt ==================== */
41 .descr = "getsockopt: no expected_attach_type",
42 .insns = {
43 /* return 1 */
44 BPF_MOV64_IMM(BPF_REG_0, 1),
45 BPF_EXIT_INSN(),
48 .attach_type = BPF_CGROUP_GETSOCKOPT,
49 .expected_attach_type = 0,
50 .error = DENY_LOAD,
53 .descr = "getsockopt: wrong expected_attach_type",
54 .insns = {
55 /* return 1 */
56 BPF_MOV64_IMM(BPF_REG_0, 1),
57 BPF_EXIT_INSN(),
60 .attach_type = BPF_CGROUP_GETSOCKOPT,
61 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
62 .error = DENY_ATTACH,
65 .descr = "getsockopt: bypass bpf hook",
66 .insns = {
67 /* return 1 */
68 BPF_MOV64_IMM(BPF_REG_0, 1),
69 BPF_EXIT_INSN(),
71 .attach_type = BPF_CGROUP_GETSOCKOPT,
72 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
74 .get_level = SOL_IP,
75 .set_level = SOL_IP,
77 .get_optname = IP_TOS,
78 .set_optname = IP_TOS,
80 .set_optval = { 1 << 3 },
81 .set_optlen = 1,
83 .get_optval = { 1 << 3 },
84 .get_optlen = 1,
87 .descr = "getsockopt: return EPERM from bpf hook",
88 .insns = {
89 BPF_MOV64_IMM(BPF_REG_0, 0),
90 BPF_EXIT_INSN(),
92 .attach_type = BPF_CGROUP_GETSOCKOPT,
93 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
95 .get_level = SOL_IP,
96 .get_optname = IP_TOS,
98 .get_optlen = 1,
99 .error = EPERM_GETSOCKOPT,
102 .descr = "getsockopt: no optval bounds check, deny loading",
103 .insns = {
104 /* r6 = ctx->optval */
105 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
106 offsetof(struct bpf_sockopt, optval)),
108 /* ctx->optval[0] = 0x80 */
109 BPF_MOV64_IMM(BPF_REG_0, 0x80),
110 BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0),
112 /* return 1 */
113 BPF_MOV64_IMM(BPF_REG_0, 1),
114 BPF_EXIT_INSN(),
116 .attach_type = BPF_CGROUP_GETSOCKOPT,
117 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
118 .error = DENY_LOAD,
121 .descr = "getsockopt: read ctx->level",
122 .insns = {
123 /* r6 = ctx->level */
124 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
125 offsetof(struct bpf_sockopt, level)),
127 /* if (ctx->level == 123) { */
128 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
129 /* ctx->retval = 0 */
130 BPF_MOV64_IMM(BPF_REG_0, 0),
131 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
132 offsetof(struct bpf_sockopt, retval)),
133 /* return 1 */
134 BPF_MOV64_IMM(BPF_REG_0, 1),
135 BPF_JMP_A(1),
136 /* } else { */
137 /* return 0 */
138 BPF_MOV64_IMM(BPF_REG_0, 0),
139 /* } */
140 BPF_EXIT_INSN(),
142 .attach_type = BPF_CGROUP_GETSOCKOPT,
143 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
145 .get_level = 123,
147 .get_optlen = 1,
150 .descr = "getsockopt: deny writing to ctx->level",
151 .insns = {
152 /* ctx->level = 1 */
153 BPF_MOV64_IMM(BPF_REG_0, 1),
154 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
155 offsetof(struct bpf_sockopt, level)),
156 BPF_EXIT_INSN(),
158 .attach_type = BPF_CGROUP_GETSOCKOPT,
159 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
161 .error = DENY_LOAD,
164 .descr = "getsockopt: read ctx->optname",
165 .insns = {
166 /* r6 = ctx->optname */
167 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
168 offsetof(struct bpf_sockopt, optname)),
170 /* if (ctx->optname == 123) { */
171 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
172 /* ctx->retval = 0 */
173 BPF_MOV64_IMM(BPF_REG_0, 0),
174 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
175 offsetof(struct bpf_sockopt, retval)),
176 /* return 1 */
177 BPF_MOV64_IMM(BPF_REG_0, 1),
178 BPF_JMP_A(1),
179 /* } else { */
180 /* return 0 */
181 BPF_MOV64_IMM(BPF_REG_0, 0),
182 /* } */
183 BPF_EXIT_INSN(),
185 .attach_type = BPF_CGROUP_GETSOCKOPT,
186 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
188 .get_optname = 123,
190 .get_optlen = 1,
193 .descr = "getsockopt: read ctx->retval",
194 .insns = {
195 /* r6 = ctx->retval */
196 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
197 offsetof(struct bpf_sockopt, retval)),
199 /* return 1 */
200 BPF_MOV64_IMM(BPF_REG_0, 1),
201 BPF_EXIT_INSN(),
203 .attach_type = BPF_CGROUP_GETSOCKOPT,
204 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
206 .get_level = SOL_IP,
207 .get_optname = IP_TOS,
208 .get_optlen = 1,
211 .descr = "getsockopt: deny writing to ctx->optname",
212 .insns = {
213 /* ctx->optname = 1 */
214 BPF_MOV64_IMM(BPF_REG_0, 1),
215 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
216 offsetof(struct bpf_sockopt, optname)),
217 BPF_EXIT_INSN(),
219 .attach_type = BPF_CGROUP_GETSOCKOPT,
220 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
222 .error = DENY_LOAD,
225 .descr = "getsockopt: read ctx->optlen",
226 .insns = {
227 /* r6 = ctx->optlen */
228 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
229 offsetof(struct bpf_sockopt, optlen)),
231 /* if (ctx->optlen == 64) { */
232 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
233 /* ctx->retval = 0 */
234 BPF_MOV64_IMM(BPF_REG_0, 0),
235 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
236 offsetof(struct bpf_sockopt, retval)),
237 /* return 1 */
238 BPF_MOV64_IMM(BPF_REG_0, 1),
239 BPF_JMP_A(1),
240 /* } else { */
241 /* return 0 */
242 BPF_MOV64_IMM(BPF_REG_0, 0),
243 /* } */
244 BPF_EXIT_INSN(),
246 .attach_type = BPF_CGROUP_GETSOCKOPT,
247 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
249 .get_optlen = 64,
252 .descr = "getsockopt: deny bigger ctx->optlen",
253 .insns = {
254 /* ctx->optlen = 65 */
255 BPF_MOV64_IMM(BPF_REG_0, 65),
256 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
257 offsetof(struct bpf_sockopt, optlen)),
259 /* ctx->retval = 0 */
260 BPF_MOV64_IMM(BPF_REG_0, 0),
261 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
262 offsetof(struct bpf_sockopt, retval)),
264 /* return 1 */
265 BPF_MOV64_IMM(BPF_REG_0, 1),
266 BPF_EXIT_INSN(),
268 .attach_type = BPF_CGROUP_GETSOCKOPT,
269 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
271 .get_optlen = 64,
273 .error = EFAULT_GETSOCKOPT,
276 .descr = "getsockopt: deny arbitrary ctx->retval",
277 .insns = {
278 /* ctx->retval = 123 */
279 BPF_MOV64_IMM(BPF_REG_0, 123),
280 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
281 offsetof(struct bpf_sockopt, retval)),
283 /* return 1 */
284 BPF_MOV64_IMM(BPF_REG_0, 1),
285 BPF_EXIT_INSN(),
287 .attach_type = BPF_CGROUP_GETSOCKOPT,
288 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
290 .get_optlen = 64,
292 .error = EFAULT_GETSOCKOPT,
295 .descr = "getsockopt: support smaller ctx->optlen",
296 .insns = {
297 /* ctx->optlen = 32 */
298 BPF_MOV64_IMM(BPF_REG_0, 32),
299 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
300 offsetof(struct bpf_sockopt, optlen)),
301 /* ctx->retval = 0 */
302 BPF_MOV64_IMM(BPF_REG_0, 0),
303 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
304 offsetof(struct bpf_sockopt, retval)),
305 /* return 1 */
306 BPF_MOV64_IMM(BPF_REG_0, 1),
307 BPF_EXIT_INSN(),
309 .attach_type = BPF_CGROUP_GETSOCKOPT,
310 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
312 .get_optlen = 64,
313 .get_optlen_ret = 32,
316 .descr = "getsockopt: deny writing to ctx->optval",
317 .insns = {
318 /* ctx->optval = 1 */
319 BPF_MOV64_IMM(BPF_REG_0, 1),
320 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
321 offsetof(struct bpf_sockopt, optval)),
322 BPF_EXIT_INSN(),
324 .attach_type = BPF_CGROUP_GETSOCKOPT,
325 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
327 .error = DENY_LOAD,
330 .descr = "getsockopt: deny writing to ctx->optval_end",
331 .insns = {
332 /* ctx->optval_end = 1 */
333 BPF_MOV64_IMM(BPF_REG_0, 1),
334 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
335 offsetof(struct bpf_sockopt, optval_end)),
336 BPF_EXIT_INSN(),
338 .attach_type = BPF_CGROUP_GETSOCKOPT,
339 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
341 .error = DENY_LOAD,
344 .descr = "getsockopt: rewrite value",
345 .insns = {
346 /* r6 = ctx->optval */
347 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
348 offsetof(struct bpf_sockopt, optval)),
349 /* r2 = ctx->optval */
350 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
351 /* r6 = ctx->optval + 1 */
352 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
354 /* r7 = ctx->optval_end */
355 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
356 offsetof(struct bpf_sockopt, optval_end)),
358 /* if (ctx->optval + 1 <= ctx->optval_end) { */
359 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
360 /* ctx->optval[0] = 0xF0 */
361 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0),
362 /* } */
364 /* ctx->retval = 0 */
365 BPF_MOV64_IMM(BPF_REG_0, 0),
366 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
367 offsetof(struct bpf_sockopt, retval)),
369 /* return 1*/
370 BPF_MOV64_IMM(BPF_REG_0, 1),
371 BPF_EXIT_INSN(),
373 .attach_type = BPF_CGROUP_GETSOCKOPT,
374 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
376 .get_level = SOL_IP,
377 .get_optname = IP_TOS,
379 .get_optval = { 0xF0 },
380 .get_optlen = 1,
383 /* ==================== setsockopt ==================== */
386 .descr = "setsockopt: no expected_attach_type",
387 .insns = {
388 /* return 1 */
389 BPF_MOV64_IMM(BPF_REG_0, 1),
390 BPF_EXIT_INSN(),
393 .attach_type = BPF_CGROUP_SETSOCKOPT,
394 .expected_attach_type = 0,
395 .error = DENY_LOAD,
398 .descr = "setsockopt: wrong expected_attach_type",
399 .insns = {
400 /* return 1 */
401 BPF_MOV64_IMM(BPF_REG_0, 1),
402 BPF_EXIT_INSN(),
405 .attach_type = BPF_CGROUP_SETSOCKOPT,
406 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
407 .error = DENY_ATTACH,
410 .descr = "setsockopt: bypass bpf hook",
411 .insns = {
412 /* return 1 */
413 BPF_MOV64_IMM(BPF_REG_0, 1),
414 BPF_EXIT_INSN(),
416 .attach_type = BPF_CGROUP_SETSOCKOPT,
417 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
419 .get_level = SOL_IP,
420 .set_level = SOL_IP,
422 .get_optname = IP_TOS,
423 .set_optname = IP_TOS,
425 .set_optval = { 1 << 3 },
426 .set_optlen = 1,
428 .get_optval = { 1 << 3 },
429 .get_optlen = 1,
432 .descr = "setsockopt: return EPERM from bpf hook",
433 .insns = {
434 /* return 0 */
435 BPF_MOV64_IMM(BPF_REG_0, 0),
436 BPF_EXIT_INSN(),
438 .attach_type = BPF_CGROUP_SETSOCKOPT,
439 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
441 .set_level = SOL_IP,
442 .set_optname = IP_TOS,
444 .set_optlen = 1,
445 .error = EPERM_SETSOCKOPT,
448 .descr = "setsockopt: no optval bounds check, deny loading",
449 .insns = {
450 /* r6 = ctx->optval */
451 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
452 offsetof(struct bpf_sockopt, optval)),
454 /* r0 = ctx->optval[0] */
455 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
457 /* return 1 */
458 BPF_MOV64_IMM(BPF_REG_0, 1),
459 BPF_EXIT_INSN(),
461 .attach_type = BPF_CGROUP_SETSOCKOPT,
462 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
463 .error = DENY_LOAD,
466 .descr = "setsockopt: read ctx->level",
467 .insns = {
468 /* r6 = ctx->level */
469 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
470 offsetof(struct bpf_sockopt, level)),
472 /* if (ctx->level == 123) { */
473 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
474 /* ctx->optlen = -1 */
475 BPF_MOV64_IMM(BPF_REG_0, -1),
476 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
477 offsetof(struct bpf_sockopt, optlen)),
478 /* return 1 */
479 BPF_MOV64_IMM(BPF_REG_0, 1),
480 BPF_JMP_A(1),
481 /* } else { */
482 /* return 0 */
483 BPF_MOV64_IMM(BPF_REG_0, 0),
484 /* } */
485 BPF_EXIT_INSN(),
487 .attach_type = BPF_CGROUP_SETSOCKOPT,
488 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
490 .set_level = 123,
492 .set_optlen = 1,
495 .descr = "setsockopt: allow changing ctx->level",
496 .insns = {
497 /* ctx->level = SOL_IP */
498 BPF_MOV64_IMM(BPF_REG_0, SOL_IP),
499 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
500 offsetof(struct bpf_sockopt, level)),
501 /* return 1 */
502 BPF_MOV64_IMM(BPF_REG_0, 1),
503 BPF_EXIT_INSN(),
505 .attach_type = BPF_CGROUP_SETSOCKOPT,
506 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
508 .get_level = SOL_IP,
509 .set_level = 234, /* should be rewritten to SOL_IP */
511 .get_optname = IP_TOS,
512 .set_optname = IP_TOS,
514 .set_optval = { 1 << 3 },
515 .set_optlen = 1,
516 .get_optval = { 1 << 3 },
517 .get_optlen = 1,
520 .descr = "setsockopt: read ctx->optname",
521 .insns = {
522 /* r6 = ctx->optname */
523 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
524 offsetof(struct bpf_sockopt, optname)),
526 /* if (ctx->optname == 123) { */
527 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
528 /* ctx->optlen = -1 */
529 BPF_MOV64_IMM(BPF_REG_0, -1),
530 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
531 offsetof(struct bpf_sockopt, optlen)),
532 /* return 1 */
533 BPF_MOV64_IMM(BPF_REG_0, 1),
534 BPF_JMP_A(1),
535 /* } else { */
536 /* return 0 */
537 BPF_MOV64_IMM(BPF_REG_0, 0),
538 /* } */
539 BPF_EXIT_INSN(),
541 .attach_type = BPF_CGROUP_SETSOCKOPT,
542 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
544 .set_optname = 123,
546 .set_optlen = 1,
549 .descr = "setsockopt: allow changing ctx->optname",
550 .insns = {
551 /* ctx->optname = IP_TOS */
552 BPF_MOV64_IMM(BPF_REG_0, IP_TOS),
553 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
554 offsetof(struct bpf_sockopt, optname)),
555 /* return 1 */
556 BPF_MOV64_IMM(BPF_REG_0, 1),
557 BPF_EXIT_INSN(),
559 .attach_type = BPF_CGROUP_SETSOCKOPT,
560 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
562 .get_level = SOL_IP,
563 .set_level = SOL_IP,
565 .get_optname = IP_TOS,
566 .set_optname = 456, /* should be rewritten to IP_TOS */
568 .set_optval = { 1 << 3 },
569 .set_optlen = 1,
570 .get_optval = { 1 << 3 },
571 .get_optlen = 1,
574 .descr = "setsockopt: read ctx->optlen",
575 .insns = {
576 /* r6 = ctx->optlen */
577 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
578 offsetof(struct bpf_sockopt, optlen)),
580 /* if (ctx->optlen == 64) { */
581 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
582 /* ctx->optlen = -1 */
583 BPF_MOV64_IMM(BPF_REG_0, -1),
584 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
585 offsetof(struct bpf_sockopt, optlen)),
586 /* return 1 */
587 BPF_MOV64_IMM(BPF_REG_0, 1),
588 BPF_JMP_A(1),
589 /* } else { */
590 /* return 0 */
591 BPF_MOV64_IMM(BPF_REG_0, 0),
592 /* } */
593 BPF_EXIT_INSN(),
595 .attach_type = BPF_CGROUP_SETSOCKOPT,
596 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
598 .set_optlen = 64,
601 .descr = "setsockopt: ctx->optlen == -1 is ok",
602 .insns = {
603 /* ctx->optlen = -1 */
604 BPF_MOV64_IMM(BPF_REG_0, -1),
605 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
606 offsetof(struct bpf_sockopt, optlen)),
607 /* return 1 */
608 BPF_MOV64_IMM(BPF_REG_0, 1),
609 BPF_EXIT_INSN(),
611 .attach_type = BPF_CGROUP_SETSOCKOPT,
612 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
614 .set_optlen = 64,
617 .descr = "setsockopt: deny ctx->optlen < 0 (except -1)",
618 .insns = {
619 /* ctx->optlen = -2 */
620 BPF_MOV64_IMM(BPF_REG_0, -2),
621 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
622 offsetof(struct bpf_sockopt, optlen)),
623 /* return 1 */
624 BPF_MOV64_IMM(BPF_REG_0, 1),
625 BPF_EXIT_INSN(),
627 .attach_type = BPF_CGROUP_SETSOCKOPT,
628 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
630 .set_optlen = 4,
632 .error = EFAULT_SETSOCKOPT,
635 .descr = "setsockopt: deny ctx->optlen > input optlen",
636 .insns = {
637 /* ctx->optlen = 65 */
638 BPF_MOV64_IMM(BPF_REG_0, 65),
639 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
640 offsetof(struct bpf_sockopt, optlen)),
641 BPF_MOV64_IMM(BPF_REG_0, 1),
642 BPF_EXIT_INSN(),
644 .attach_type = BPF_CGROUP_SETSOCKOPT,
645 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
647 .set_optlen = 64,
649 .error = EFAULT_SETSOCKOPT,
652 .descr = "setsockopt: allow changing ctx->optlen within bounds",
653 .insns = {
654 /* r6 = ctx->optval */
655 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
656 offsetof(struct bpf_sockopt, optval)),
657 /* r2 = ctx->optval */
658 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
659 /* r6 = ctx->optval + 1 */
660 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
662 /* r7 = ctx->optval_end */
663 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
664 offsetof(struct bpf_sockopt, optval_end)),
666 /* if (ctx->optval + 1 <= ctx->optval_end) { */
667 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
668 /* ctx->optval[0] = 1 << 3 */
669 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 1 << 3),
670 /* } */
672 /* ctx->optlen = 1 */
673 BPF_MOV64_IMM(BPF_REG_0, 1),
674 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
675 offsetof(struct bpf_sockopt, optlen)),
677 /* return 1*/
678 BPF_MOV64_IMM(BPF_REG_0, 1),
679 BPF_EXIT_INSN(),
681 .attach_type = BPF_CGROUP_SETSOCKOPT,
682 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
684 .get_level = SOL_IP,
685 .set_level = SOL_IP,
687 .get_optname = IP_TOS,
688 .set_optname = IP_TOS,
690 .set_optval = { 1, 1, 1, 1 },
691 .set_optlen = 4,
692 .get_optval = { 1 << 3 },
693 .get_optlen = 1,
696 .descr = "setsockopt: deny write ctx->retval",
697 .insns = {
698 /* ctx->retval = 0 */
699 BPF_MOV64_IMM(BPF_REG_0, 0),
700 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
701 offsetof(struct bpf_sockopt, retval)),
703 /* return 1 */
704 BPF_MOV64_IMM(BPF_REG_0, 1),
705 BPF_EXIT_INSN(),
707 .attach_type = BPF_CGROUP_SETSOCKOPT,
708 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
710 .error = DENY_LOAD,
713 .descr = "setsockopt: deny read ctx->retval",
714 .insns = {
715 /* r6 = ctx->retval */
716 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
717 offsetof(struct bpf_sockopt, retval)),
719 /* return 1 */
720 BPF_MOV64_IMM(BPF_REG_0, 1),
721 BPF_EXIT_INSN(),
723 .attach_type = BPF_CGROUP_SETSOCKOPT,
724 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
726 .error = DENY_LOAD,
729 .descr = "setsockopt: deny writing to ctx->optval",
730 .insns = {
731 /* ctx->optval = 1 */
732 BPF_MOV64_IMM(BPF_REG_0, 1),
733 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
734 offsetof(struct bpf_sockopt, optval)),
735 BPF_EXIT_INSN(),
737 .attach_type = BPF_CGROUP_SETSOCKOPT,
738 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
740 .error = DENY_LOAD,
743 .descr = "setsockopt: deny writing to ctx->optval_end",
744 .insns = {
745 /* ctx->optval_end = 1 */
746 BPF_MOV64_IMM(BPF_REG_0, 1),
747 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
748 offsetof(struct bpf_sockopt, optval_end)),
749 BPF_EXIT_INSN(),
751 .attach_type = BPF_CGROUP_SETSOCKOPT,
752 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
754 .error = DENY_LOAD,
757 .descr = "setsockopt: allow IP_TOS <= 128",
758 .insns = {
759 /* r6 = ctx->optval */
760 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
761 offsetof(struct bpf_sockopt, optval)),
762 /* r7 = ctx->optval + 1 */
763 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
764 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
766 /* r8 = ctx->optval_end */
767 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
768 offsetof(struct bpf_sockopt, optval_end)),
770 /* if (ctx->optval + 1 <= ctx->optval_end) { */
771 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
773 /* r9 = ctx->optval[0] */
774 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
776 /* if (ctx->optval[0] < 128) */
777 BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
778 BPF_MOV64_IMM(BPF_REG_0, 1),
779 BPF_JMP_A(1),
780 /* } */
782 /* } else { */
783 BPF_MOV64_IMM(BPF_REG_0, 0),
784 /* } */
786 BPF_EXIT_INSN(),
788 .attach_type = BPF_CGROUP_SETSOCKOPT,
789 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
791 .get_level = SOL_IP,
792 .set_level = SOL_IP,
794 .get_optname = IP_TOS,
795 .set_optname = IP_TOS,
797 .set_optval = { 0x80 },
798 .set_optlen = 1,
799 .get_optval = { 0x80 },
800 .get_optlen = 1,
803 .descr = "setsockopt: deny IP_TOS > 128",
804 .insns = {
805 /* r6 = ctx->optval */
806 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
807 offsetof(struct bpf_sockopt, optval)),
808 /* r7 = ctx->optval + 1 */
809 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
810 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
812 /* r8 = ctx->optval_end */
813 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
814 offsetof(struct bpf_sockopt, optval_end)),
816 /* if (ctx->optval + 1 <= ctx->optval_end) { */
817 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
819 /* r9 = ctx->optval[0] */
820 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
822 /* if (ctx->optval[0] < 128) */
823 BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
824 BPF_MOV64_IMM(BPF_REG_0, 1),
825 BPF_JMP_A(1),
826 /* } */
828 /* } else { */
829 BPF_MOV64_IMM(BPF_REG_0, 0),
830 /* } */
832 BPF_EXIT_INSN(),
834 .attach_type = BPF_CGROUP_SETSOCKOPT,
835 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
837 .get_level = SOL_IP,
838 .set_level = SOL_IP,
840 .get_optname = IP_TOS,
841 .set_optname = IP_TOS,
843 .set_optval = { 0x81 },
844 .set_optlen = 1,
845 .get_optval = { 0x00 },
846 .get_optlen = 1,
848 .error = EPERM_SETSOCKOPT,
852 static int load_prog(const struct bpf_insn *insns,
853 enum bpf_attach_type expected_attach_type)
855 struct bpf_load_program_attr attr = {
856 .prog_type = BPF_PROG_TYPE_CGROUP_SOCKOPT,
857 .expected_attach_type = expected_attach_type,
858 .insns = insns,
859 .license = "GPL",
860 .log_level = 2,
862 int fd;
864 for (;
865 insns[attr.insns_cnt].code != (BPF_JMP | BPF_EXIT);
866 attr.insns_cnt++) {
868 attr.insns_cnt++;
870 fd = bpf_load_program_xattr(&attr, bpf_log_buf, sizeof(bpf_log_buf));
871 if (verbose && fd < 0)
872 fprintf(stderr, "%s\n", bpf_log_buf);
874 return fd;
877 static int run_test(int cgroup_fd, struct sockopt_test *test)
879 int sock_fd, err, prog_fd;
880 void *optval = NULL;
881 int ret = 0;
883 prog_fd = load_prog(test->insns, test->expected_attach_type);
884 if (prog_fd < 0) {
885 if (test->error == DENY_LOAD)
886 return 0;
888 log_err("Failed to load BPF program");
889 return -1;
892 err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0);
893 if (err < 0) {
894 if (test->error == DENY_ATTACH)
895 goto close_prog_fd;
897 log_err("Failed to attach BPF program");
898 ret = -1;
899 goto close_prog_fd;
902 sock_fd = socket(AF_INET, SOCK_STREAM, 0);
903 if (sock_fd < 0) {
904 log_err("Failed to create AF_INET socket");
905 ret = -1;
906 goto detach_prog;
909 if (test->set_optlen) {
910 err = setsockopt(sock_fd, test->set_level, test->set_optname,
911 test->set_optval, test->set_optlen);
912 if (err) {
913 if (errno == EPERM && test->error == EPERM_SETSOCKOPT)
914 goto close_sock_fd;
915 if (errno == EFAULT && test->error == EFAULT_SETSOCKOPT)
916 goto free_optval;
918 log_err("Failed to call setsockopt");
919 ret = -1;
920 goto close_sock_fd;
924 if (test->get_optlen) {
925 optval = malloc(test->get_optlen);
926 socklen_t optlen = test->get_optlen;
927 socklen_t expected_get_optlen = test->get_optlen_ret ?:
928 test->get_optlen;
930 err = getsockopt(sock_fd, test->get_level, test->get_optname,
931 optval, &optlen);
932 if (err) {
933 if (errno == EPERM && test->error == EPERM_GETSOCKOPT)
934 goto free_optval;
935 if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT)
936 goto free_optval;
938 log_err("Failed to call getsockopt");
939 ret = -1;
940 goto free_optval;
943 if (optlen != expected_get_optlen) {
944 errno = 0;
945 log_err("getsockopt returned unexpected optlen");
946 ret = -1;
947 goto free_optval;
950 if (memcmp(optval, test->get_optval, optlen) != 0) {
951 errno = 0;
952 log_err("getsockopt returned unexpected optval");
953 ret = -1;
954 goto free_optval;
958 ret = test->error != OK;
960 free_optval:
961 free(optval);
962 close_sock_fd:
963 close(sock_fd);
964 detach_prog:
965 bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);
966 close_prog_fd:
967 close(prog_fd);
968 return ret;
971 void test_sockopt(void)
973 int cgroup_fd, i;
975 cgroup_fd = test__join_cgroup("/sockopt");
976 if (CHECK_FAIL(cgroup_fd < 0))
977 return;
979 for (i = 0; i < ARRAY_SIZE(tests); i++) {
980 test__start_subtest(tests[i].descr);
981 CHECK_FAIL(run_test(cgroup_fd, &tests[i]));
984 close(cgroup_fd);