Loading...
1// SPDX-License-Identifier: GPL-2.0
2#include <test_progs.h>
3#include <io_uring/mini_liburing.h>
4#include "cgroup_helpers.h"
5
6static char bpf_log_buf[4096];
7static bool verbose;
8
9#ifndef PAGE_SIZE
10#define PAGE_SIZE 4096
11#endif
12
13enum sockopt_test_error {
14 OK = 0,
15 DENY_LOAD,
16 DENY_ATTACH,
17 EOPNOTSUPP_GETSOCKOPT,
18 EPERM_GETSOCKOPT,
19 EFAULT_GETSOCKOPT,
20 EPERM_SETSOCKOPT,
21 EFAULT_SETSOCKOPT,
22};
23
24static struct sockopt_test {
25 const char *descr;
26 const struct bpf_insn insns[64];
27 enum bpf_prog_type prog_type;
28 enum bpf_attach_type attach_type;
29 enum bpf_attach_type expected_attach_type;
30
31 int set_optname;
32 int set_level;
33 const char set_optval[64];
34 socklen_t set_optlen;
35
36 int get_optname;
37 int get_level;
38 const char get_optval[64];
39 socklen_t get_optlen;
40 socklen_t get_optlen_ret;
41
42 enum sockopt_test_error error;
43 bool io_uring_support;
44} tests[] = {
45
46 /* ==================== getsockopt ==================== */
47
48 {
49 .descr = "getsockopt: no expected_attach_type",
50 .insns = {
51 /* return 1 */
52 BPF_MOV64_IMM(BPF_REG_0, 1),
53 BPF_EXIT_INSN(),
54
55 },
56 .attach_type = BPF_CGROUP_GETSOCKOPT,
57 .expected_attach_type = 0,
58 .error = DENY_LOAD,
59 },
60 {
61 .descr = "getsockopt: wrong expected_attach_type",
62 .insns = {
63 /* return 1 */
64 BPF_MOV64_IMM(BPF_REG_0, 1),
65 BPF_EXIT_INSN(),
66
67 },
68 .attach_type = BPF_CGROUP_GETSOCKOPT,
69 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
70 .error = DENY_ATTACH,
71 },
72 {
73 .descr = "getsockopt: bypass bpf hook",
74 .insns = {
75 /* return 1 */
76 BPF_MOV64_IMM(BPF_REG_0, 1),
77 BPF_EXIT_INSN(),
78 },
79 .attach_type = BPF_CGROUP_GETSOCKOPT,
80 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
81
82 .get_level = SOL_IP,
83 .set_level = SOL_IP,
84
85 .get_optname = IP_TOS,
86 .set_optname = IP_TOS,
87
88 .set_optval = { 1 << 3 },
89 .set_optlen = 1,
90
91 .get_optval = { 1 << 3 },
92 .get_optlen = 1,
93 },
94 {
95 .descr = "getsockopt: return EPERM from bpf hook",
96 .insns = {
97 BPF_MOV64_IMM(BPF_REG_0, 0),
98 BPF_EXIT_INSN(),
99 },
100 .attach_type = BPF_CGROUP_GETSOCKOPT,
101 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
102
103 .get_level = SOL_IP,
104 .get_optname = IP_TOS,
105
106 .get_optlen = 1,
107 .error = EPERM_GETSOCKOPT,
108 },
109 {
110 .descr = "getsockopt: no optval bounds check, deny loading",
111 .insns = {
112 /* r6 = ctx->optval */
113 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
114 offsetof(struct bpf_sockopt, optval)),
115
116 /* ctx->optval[0] = 0x80 */
117 BPF_MOV64_IMM(BPF_REG_0, 0x80),
118 BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0),
119
120 /* return 1 */
121 BPF_MOV64_IMM(BPF_REG_0, 1),
122 BPF_EXIT_INSN(),
123 },
124 .attach_type = BPF_CGROUP_GETSOCKOPT,
125 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
126 .error = DENY_LOAD,
127 },
128 {
129 .descr = "getsockopt: read ctx->level",
130 .insns = {
131 /* r6 = ctx->level */
132 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
133 offsetof(struct bpf_sockopt, level)),
134
135 /* if (ctx->level == 123) { */
136 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
137 /* ctx->retval = 0 */
138 BPF_MOV64_IMM(BPF_REG_0, 0),
139 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
140 offsetof(struct bpf_sockopt, retval)),
141 /* return 1 */
142 BPF_MOV64_IMM(BPF_REG_0, 1),
143 BPF_JMP_A(1),
144 /* } else { */
145 /* return 0 */
146 BPF_MOV64_IMM(BPF_REG_0, 0),
147 /* } */
148 BPF_EXIT_INSN(),
149 },
150 .attach_type = BPF_CGROUP_GETSOCKOPT,
151 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
152
153 .get_level = 123,
154
155 .get_optlen = 1,
156 },
157 {
158 .descr = "getsockopt: deny writing to ctx->level",
159 .insns = {
160 /* ctx->level = 1 */
161 BPF_MOV64_IMM(BPF_REG_0, 1),
162 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
163 offsetof(struct bpf_sockopt, level)),
164 BPF_EXIT_INSN(),
165 },
166 .attach_type = BPF_CGROUP_GETSOCKOPT,
167 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
168
169 .error = DENY_LOAD,
170 },
171 {
172 .descr = "getsockopt: read ctx->optname",
173 .insns = {
174 /* r6 = ctx->optname */
175 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
176 offsetof(struct bpf_sockopt, optname)),
177
178 /* if (ctx->optname == 123) { */
179 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
180 /* ctx->retval = 0 */
181 BPF_MOV64_IMM(BPF_REG_0, 0),
182 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
183 offsetof(struct bpf_sockopt, retval)),
184 /* return 1 */
185 BPF_MOV64_IMM(BPF_REG_0, 1),
186 BPF_JMP_A(1),
187 /* } else { */
188 /* return 0 */
189 BPF_MOV64_IMM(BPF_REG_0, 0),
190 /* } */
191 BPF_EXIT_INSN(),
192 },
193 .attach_type = BPF_CGROUP_GETSOCKOPT,
194 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
195
196 .get_optname = 123,
197
198 .get_optlen = 1,
199 },
200 {
201 .descr = "getsockopt: read ctx->retval",
202 .insns = {
203 /* r6 = ctx->retval */
204 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
205 offsetof(struct bpf_sockopt, retval)),
206
207 /* return 1 */
208 BPF_MOV64_IMM(BPF_REG_0, 1),
209 BPF_EXIT_INSN(),
210 },
211 .attach_type = BPF_CGROUP_GETSOCKOPT,
212 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
213
214 .get_level = SOL_IP,
215 .get_optname = IP_TOS,
216 .get_optlen = 1,
217 },
218 {
219 .descr = "getsockopt: deny writing to ctx->optname",
220 .insns = {
221 /* ctx->optname = 1 */
222 BPF_MOV64_IMM(BPF_REG_0, 1),
223 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
224 offsetof(struct bpf_sockopt, optname)),
225 BPF_EXIT_INSN(),
226 },
227 .attach_type = BPF_CGROUP_GETSOCKOPT,
228 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
229
230 .error = DENY_LOAD,
231 },
232 {
233 .descr = "getsockopt: read ctx->optlen",
234 .insns = {
235 /* r6 = ctx->optlen */
236 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
237 offsetof(struct bpf_sockopt, optlen)),
238
239 /* if (ctx->optlen == 64) { */
240 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
241 /* ctx->retval = 0 */
242 BPF_MOV64_IMM(BPF_REG_0, 0),
243 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
244 offsetof(struct bpf_sockopt, retval)),
245 /* return 1 */
246 BPF_MOV64_IMM(BPF_REG_0, 1),
247 BPF_JMP_A(1),
248 /* } else { */
249 /* return 0 */
250 BPF_MOV64_IMM(BPF_REG_0, 0),
251 /* } */
252 BPF_EXIT_INSN(),
253 },
254 .attach_type = BPF_CGROUP_GETSOCKOPT,
255 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
256
257 .get_level = SOL_SOCKET,
258 .get_optlen = 64,
259 .io_uring_support = true,
260 },
261 {
262 .descr = "getsockopt: deny bigger ctx->optlen",
263 .insns = {
264 /* ctx->optlen = 65 */
265 BPF_MOV64_IMM(BPF_REG_0, 65),
266 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
267 offsetof(struct bpf_sockopt, optlen)),
268
269 /* ctx->retval = 0 */
270 BPF_MOV64_IMM(BPF_REG_0, 0),
271 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
272 offsetof(struct bpf_sockopt, retval)),
273
274 /* return 1 */
275 BPF_MOV64_IMM(BPF_REG_0, 1),
276 BPF_EXIT_INSN(),
277 },
278 .attach_type = BPF_CGROUP_GETSOCKOPT,
279 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
280
281 .get_optlen = 64,
282
283 .error = EFAULT_GETSOCKOPT,
284 .io_uring_support = true,
285 },
286 {
287 .descr = "getsockopt: ignore >PAGE_SIZE optlen",
288 .insns = {
289 /* write 0xFF to the first optval byte */
290
291 /* r6 = ctx->optval */
292 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
293 offsetof(struct bpf_sockopt, optval)),
294 /* r2 = ctx->optval */
295 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
296 /* r6 = ctx->optval + 1 */
297 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
298
299 /* r7 = ctx->optval_end */
300 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
301 offsetof(struct bpf_sockopt, optval_end)),
302
303 /* if (ctx->optval + 1 <= ctx->optval_end) { */
304 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
305 /* ctx->optval[0] = 0xF0 */
306 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xFF),
307 /* } */
308
309 /* retval changes are ignored */
310 /* ctx->retval = 5 */
311 BPF_MOV64_IMM(BPF_REG_0, 5),
312 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
313 offsetof(struct bpf_sockopt, retval)),
314
315 /* return 1 */
316 BPF_MOV64_IMM(BPF_REG_0, 1),
317 BPF_EXIT_INSN(),
318 },
319 .attach_type = BPF_CGROUP_GETSOCKOPT,
320 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
321
322 .get_level = 1234,
323 .get_optname = 5678,
324 .get_optval = {}, /* the changes are ignored */
325 .get_optlen = PAGE_SIZE + 1,
326 .error = EOPNOTSUPP_GETSOCKOPT,
327 .io_uring_support = true,
328 },
329 {
330 .descr = "getsockopt: support smaller ctx->optlen",
331 .insns = {
332 /* ctx->optlen = 32 */
333 BPF_MOV64_IMM(BPF_REG_0, 32),
334 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
335 offsetof(struct bpf_sockopt, optlen)),
336 /* ctx->retval = 0 */
337 BPF_MOV64_IMM(BPF_REG_0, 0),
338 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
339 offsetof(struct bpf_sockopt, retval)),
340 /* return 1 */
341 BPF_MOV64_IMM(BPF_REG_0, 1),
342 BPF_EXIT_INSN(),
343 },
344 .attach_type = BPF_CGROUP_GETSOCKOPT,
345 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
346
347 .get_level = SOL_SOCKET,
348 .get_optlen = 64,
349 .get_optlen_ret = 32,
350 .io_uring_support = true,
351 },
352 {
353 .descr = "getsockopt: deny writing to ctx->optval",
354 .insns = {
355 /* ctx->optval = 1 */
356 BPF_MOV64_IMM(BPF_REG_0, 1),
357 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
358 offsetof(struct bpf_sockopt, optval)),
359 BPF_EXIT_INSN(),
360 },
361 .attach_type = BPF_CGROUP_GETSOCKOPT,
362 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
363
364 .error = DENY_LOAD,
365 },
366 {
367 .descr = "getsockopt: deny writing to ctx->optval_end",
368 .insns = {
369 /* ctx->optval_end = 1 */
370 BPF_MOV64_IMM(BPF_REG_0, 1),
371 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
372 offsetof(struct bpf_sockopt, optval_end)),
373 BPF_EXIT_INSN(),
374 },
375 .attach_type = BPF_CGROUP_GETSOCKOPT,
376 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
377
378 .error = DENY_LOAD,
379 },
380 {
381 .descr = "getsockopt: rewrite value",
382 .insns = {
383 /* r6 = ctx->optval */
384 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
385 offsetof(struct bpf_sockopt, optval)),
386 /* r2 = ctx->optval */
387 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
388 /* r6 = ctx->optval + 1 */
389 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
390
391 /* r7 = ctx->optval_end */
392 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
393 offsetof(struct bpf_sockopt, optval_end)),
394
395 /* if (ctx->optval + 1 <= ctx->optval_end) { */
396 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
397 /* ctx->optval[0] = 0xF0 */
398 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0),
399 /* } */
400
401 /* ctx->retval = 0 */
402 BPF_MOV64_IMM(BPF_REG_0, 0),
403 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
404 offsetof(struct bpf_sockopt, retval)),
405
406 /* return 1*/
407 BPF_MOV64_IMM(BPF_REG_0, 1),
408 BPF_EXIT_INSN(),
409 },
410 .attach_type = BPF_CGROUP_GETSOCKOPT,
411 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
412
413 .get_level = SOL_IP,
414 .get_optname = IP_TOS,
415
416 .get_optval = { 0xF0 },
417 .get_optlen = 1,
418 },
419
420 /* ==================== setsockopt ==================== */
421
422 {
423 .descr = "setsockopt: no expected_attach_type",
424 .insns = {
425 /* return 1 */
426 BPF_MOV64_IMM(BPF_REG_0, 1),
427 BPF_EXIT_INSN(),
428
429 },
430 .attach_type = BPF_CGROUP_SETSOCKOPT,
431 .expected_attach_type = 0,
432 .error = DENY_LOAD,
433 },
434 {
435 .descr = "setsockopt: wrong expected_attach_type",
436 .insns = {
437 /* return 1 */
438 BPF_MOV64_IMM(BPF_REG_0, 1),
439 BPF_EXIT_INSN(),
440
441 },
442 .attach_type = BPF_CGROUP_SETSOCKOPT,
443 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
444 .error = DENY_ATTACH,
445 },
446 {
447 .descr = "setsockopt: bypass bpf hook",
448 .insns = {
449 /* return 1 */
450 BPF_MOV64_IMM(BPF_REG_0, 1),
451 BPF_EXIT_INSN(),
452 },
453 .attach_type = BPF_CGROUP_SETSOCKOPT,
454 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
455
456 .get_level = SOL_IP,
457 .set_level = SOL_IP,
458
459 .get_optname = IP_TOS,
460 .set_optname = IP_TOS,
461
462 .set_optval = { 1 << 3 },
463 .set_optlen = 1,
464
465 .get_optval = { 1 << 3 },
466 .get_optlen = 1,
467 },
468 {
469 .descr = "setsockopt: return EPERM from bpf hook",
470 .insns = {
471 /* return 0 */
472 BPF_MOV64_IMM(BPF_REG_0, 0),
473 BPF_EXIT_INSN(),
474 },
475 .attach_type = BPF_CGROUP_SETSOCKOPT,
476 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
477
478 .set_level = SOL_IP,
479 .set_optname = IP_TOS,
480
481 .set_optlen = 1,
482 .error = EPERM_SETSOCKOPT,
483 },
484 {
485 .descr = "setsockopt: no optval bounds check, deny loading",
486 .insns = {
487 /* r6 = ctx->optval */
488 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
489 offsetof(struct bpf_sockopt, optval)),
490
491 /* r0 = ctx->optval[0] */
492 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
493
494 /* return 1 */
495 BPF_MOV64_IMM(BPF_REG_0, 1),
496 BPF_EXIT_INSN(),
497 },
498 .attach_type = BPF_CGROUP_SETSOCKOPT,
499 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
500 .error = DENY_LOAD,
501 },
502 {
503 .descr = "setsockopt: read ctx->level",
504 .insns = {
505 /* r6 = ctx->level */
506 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
507 offsetof(struct bpf_sockopt, level)),
508
509 /* if (ctx->level == 123) { */
510 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
511 /* ctx->optlen = -1 */
512 BPF_MOV64_IMM(BPF_REG_0, -1),
513 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
514 offsetof(struct bpf_sockopt, optlen)),
515 /* return 1 */
516 BPF_MOV64_IMM(BPF_REG_0, 1),
517 BPF_JMP_A(1),
518 /* } else { */
519 /* return 0 */
520 BPF_MOV64_IMM(BPF_REG_0, 0),
521 /* } */
522 BPF_EXIT_INSN(),
523 },
524 .attach_type = BPF_CGROUP_SETSOCKOPT,
525 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
526
527 .set_level = 123,
528
529 .set_optlen = 1,
530 .io_uring_support = true,
531 },
532 {
533 .descr = "setsockopt: allow changing ctx->level",
534 .insns = {
535 /* ctx->level = SOL_IP */
536 BPF_MOV64_IMM(BPF_REG_0, SOL_IP),
537 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
538 offsetof(struct bpf_sockopt, level)),
539 /* return 1 */
540 BPF_MOV64_IMM(BPF_REG_0, 1),
541 BPF_EXIT_INSN(),
542 },
543 .attach_type = BPF_CGROUP_SETSOCKOPT,
544 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
545
546 .get_level = SOL_IP,
547 .set_level = 234, /* should be rewritten to SOL_IP */
548
549 .get_optname = IP_TOS,
550 .set_optname = IP_TOS,
551
552 .set_optval = { 1 << 3 },
553 .set_optlen = 1,
554 .get_optval = { 1 << 3 },
555 .get_optlen = 1,
556 },
557 {
558 .descr = "setsockopt: read ctx->optname",
559 .insns = {
560 /* r6 = ctx->optname */
561 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
562 offsetof(struct bpf_sockopt, optname)),
563
564 /* if (ctx->optname == 123) { */
565 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
566 /* ctx->optlen = -1 */
567 BPF_MOV64_IMM(BPF_REG_0, -1),
568 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
569 offsetof(struct bpf_sockopt, optlen)),
570 /* return 1 */
571 BPF_MOV64_IMM(BPF_REG_0, 1),
572 BPF_JMP_A(1),
573 /* } else { */
574 /* return 0 */
575 BPF_MOV64_IMM(BPF_REG_0, 0),
576 /* } */
577 BPF_EXIT_INSN(),
578 },
579 .attach_type = BPF_CGROUP_SETSOCKOPT,
580 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
581
582 .set_optname = 123,
583
584 .set_optlen = 1,
585 .io_uring_support = true,
586 },
587 {
588 .descr = "setsockopt: allow changing ctx->optname",
589 .insns = {
590 /* ctx->optname = IP_TOS */
591 BPF_MOV64_IMM(BPF_REG_0, IP_TOS),
592 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
593 offsetof(struct bpf_sockopt, optname)),
594 /* return 1 */
595 BPF_MOV64_IMM(BPF_REG_0, 1),
596 BPF_EXIT_INSN(),
597 },
598 .attach_type = BPF_CGROUP_SETSOCKOPT,
599 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
600
601 .get_level = SOL_IP,
602 .set_level = SOL_IP,
603
604 .get_optname = IP_TOS,
605 .set_optname = 456, /* should be rewritten to IP_TOS */
606
607 .set_optval = { 1 << 3 },
608 .set_optlen = 1,
609 .get_optval = { 1 << 3 },
610 .get_optlen = 1,
611 },
612 {
613 .descr = "setsockopt: read ctx->optlen",
614 .insns = {
615 /* r6 = ctx->optlen */
616 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
617 offsetof(struct bpf_sockopt, optlen)),
618
619 /* if (ctx->optlen == 64) { */
620 BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
621 /* ctx->optlen = -1 */
622 BPF_MOV64_IMM(BPF_REG_0, -1),
623 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
624 offsetof(struct bpf_sockopt, optlen)),
625 /* return 1 */
626 BPF_MOV64_IMM(BPF_REG_0, 1),
627 BPF_JMP_A(1),
628 /* } else { */
629 /* return 0 */
630 BPF_MOV64_IMM(BPF_REG_0, 0),
631 /* } */
632 BPF_EXIT_INSN(),
633 },
634 .attach_type = BPF_CGROUP_SETSOCKOPT,
635 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
636
637 .set_optlen = 64,
638 .io_uring_support = true,
639 },
640 {
641 .descr = "setsockopt: ctx->optlen == -1 is ok",
642 .insns = {
643 /* ctx->optlen = -1 */
644 BPF_MOV64_IMM(BPF_REG_0, -1),
645 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
646 offsetof(struct bpf_sockopt, optlen)),
647 /* return 1 */
648 BPF_MOV64_IMM(BPF_REG_0, 1),
649 BPF_EXIT_INSN(),
650 },
651 .attach_type = BPF_CGROUP_SETSOCKOPT,
652 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
653
654 .set_optlen = 64,
655 .io_uring_support = true,
656 },
657 {
658 .descr = "setsockopt: deny ctx->optlen < 0 (except -1)",
659 .insns = {
660 /* ctx->optlen = -2 */
661 BPF_MOV64_IMM(BPF_REG_0, -2),
662 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
663 offsetof(struct bpf_sockopt, optlen)),
664 /* return 1 */
665 BPF_MOV64_IMM(BPF_REG_0, 1),
666 BPF_EXIT_INSN(),
667 },
668 .attach_type = BPF_CGROUP_SETSOCKOPT,
669 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
670
671 .set_optlen = 4,
672
673 .error = EFAULT_SETSOCKOPT,
674 .io_uring_support = true,
675 },
676 {
677 .descr = "setsockopt: deny ctx->optlen > input optlen",
678 .insns = {
679 /* ctx->optlen = 65 */
680 BPF_MOV64_IMM(BPF_REG_0, 65),
681 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
682 offsetof(struct bpf_sockopt, optlen)),
683 BPF_MOV64_IMM(BPF_REG_0, 1),
684 BPF_EXIT_INSN(),
685 },
686 .attach_type = BPF_CGROUP_SETSOCKOPT,
687 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
688
689 .set_optlen = 64,
690
691 .error = EFAULT_SETSOCKOPT,
692 .io_uring_support = true,
693 },
694 {
695 .descr = "setsockopt: ignore >PAGE_SIZE optlen",
696 .insns = {
697 /* write 0xFF to the first optval byte */
698
699 /* r6 = ctx->optval */
700 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
701 offsetof(struct bpf_sockopt, optval)),
702 /* r2 = ctx->optval */
703 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
704 /* r6 = ctx->optval + 1 */
705 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
706
707 /* r7 = ctx->optval_end */
708 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
709 offsetof(struct bpf_sockopt, optval_end)),
710
711 /* if (ctx->optval + 1 <= ctx->optval_end) { */
712 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
713 /* ctx->optval[0] = 0xF0 */
714 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0),
715 /* } */
716
717 BPF_MOV64_IMM(BPF_REG_0, 1),
718 BPF_EXIT_INSN(),
719 },
720 .attach_type = BPF_CGROUP_SETSOCKOPT,
721 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
722
723 .set_level = SOL_IP,
724 .set_optname = IP_TOS,
725 .set_optval = {},
726 .set_optlen = PAGE_SIZE + 1,
727
728 .get_level = SOL_IP,
729 .get_optname = IP_TOS,
730 .get_optval = {}, /* the changes are ignored */
731 .get_optlen = 4,
732 },
733 {
734 .descr = "setsockopt: allow changing ctx->optlen within bounds",
735 .insns = {
736 /* r6 = ctx->optval */
737 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
738 offsetof(struct bpf_sockopt, optval)),
739 /* r2 = ctx->optval */
740 BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
741 /* r6 = ctx->optval + 1 */
742 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
743
744 /* r7 = ctx->optval_end */
745 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
746 offsetof(struct bpf_sockopt, optval_end)),
747
748 /* if (ctx->optval + 1 <= ctx->optval_end) { */
749 BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
750 /* ctx->optval[0] = 1 << 3 */
751 BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 1 << 3),
752 /* } */
753
754 /* ctx->optlen = 1 */
755 BPF_MOV64_IMM(BPF_REG_0, 1),
756 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
757 offsetof(struct bpf_sockopt, optlen)),
758
759 /* return 1*/
760 BPF_MOV64_IMM(BPF_REG_0, 1),
761 BPF_EXIT_INSN(),
762 },
763 .attach_type = BPF_CGROUP_SETSOCKOPT,
764 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
765
766 .get_level = SOL_IP,
767 .set_level = SOL_IP,
768
769 .get_optname = IP_TOS,
770 .set_optname = IP_TOS,
771
772 .set_optval = { 1, 1, 1, 1 },
773 .set_optlen = 4,
774 .get_optval = { 1 << 3 },
775 .get_optlen = 1,
776 },
777 {
778 .descr = "setsockopt: deny write ctx->retval",
779 .insns = {
780 /* ctx->retval = 0 */
781 BPF_MOV64_IMM(BPF_REG_0, 0),
782 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
783 offsetof(struct bpf_sockopt, retval)),
784
785 /* return 1 */
786 BPF_MOV64_IMM(BPF_REG_0, 1),
787 BPF_EXIT_INSN(),
788 },
789 .attach_type = BPF_CGROUP_SETSOCKOPT,
790 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
791
792 .error = DENY_LOAD,
793 },
794 {
795 .descr = "setsockopt: deny read ctx->retval",
796 .insns = {
797 /* r6 = ctx->retval */
798 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
799 offsetof(struct bpf_sockopt, retval)),
800
801 /* return 1 */
802 BPF_MOV64_IMM(BPF_REG_0, 1),
803 BPF_EXIT_INSN(),
804 },
805 .attach_type = BPF_CGROUP_SETSOCKOPT,
806 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
807
808 .error = DENY_LOAD,
809 },
810 {
811 .descr = "setsockopt: deny writing to ctx->optval",
812 .insns = {
813 /* ctx->optval = 1 */
814 BPF_MOV64_IMM(BPF_REG_0, 1),
815 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
816 offsetof(struct bpf_sockopt, optval)),
817 BPF_EXIT_INSN(),
818 },
819 .attach_type = BPF_CGROUP_SETSOCKOPT,
820 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
821
822 .error = DENY_LOAD,
823 },
824 {
825 .descr = "setsockopt: deny writing to ctx->optval_end",
826 .insns = {
827 /* ctx->optval_end = 1 */
828 BPF_MOV64_IMM(BPF_REG_0, 1),
829 BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
830 offsetof(struct bpf_sockopt, optval_end)),
831 BPF_EXIT_INSN(),
832 },
833 .attach_type = BPF_CGROUP_SETSOCKOPT,
834 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
835
836 .error = DENY_LOAD,
837 },
838 {
839 .descr = "setsockopt: allow IP_TOS <= 128",
840 .insns = {
841 /* r6 = ctx->optval */
842 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
843 offsetof(struct bpf_sockopt, optval)),
844 /* r7 = ctx->optval + 1 */
845 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
846 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
847
848 /* r8 = ctx->optval_end */
849 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
850 offsetof(struct bpf_sockopt, optval_end)),
851
852 /* if (ctx->optval + 1 <= ctx->optval_end) { */
853 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
854
855 /* r9 = ctx->optval[0] */
856 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
857
858 /* if (ctx->optval[0] < 128) */
859 BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
860 BPF_MOV64_IMM(BPF_REG_0, 1),
861 BPF_JMP_A(1),
862 /* } */
863
864 /* } else { */
865 BPF_MOV64_IMM(BPF_REG_0, 0),
866 /* } */
867
868 BPF_EXIT_INSN(),
869 },
870 .attach_type = BPF_CGROUP_SETSOCKOPT,
871 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
872
873 .get_level = SOL_IP,
874 .set_level = SOL_IP,
875
876 .get_optname = IP_TOS,
877 .set_optname = IP_TOS,
878
879 .set_optval = { 0x80 },
880 .set_optlen = 1,
881 .get_optval = { 0x80 },
882 .get_optlen = 1,
883 },
884 {
885 .descr = "setsockopt: deny IP_TOS > 128",
886 .insns = {
887 /* r6 = ctx->optval */
888 BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
889 offsetof(struct bpf_sockopt, optval)),
890 /* r7 = ctx->optval + 1 */
891 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
892 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
893
894 /* r8 = ctx->optval_end */
895 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
896 offsetof(struct bpf_sockopt, optval_end)),
897
898 /* if (ctx->optval + 1 <= ctx->optval_end) { */
899 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
900
901 /* r9 = ctx->optval[0] */
902 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
903
904 /* if (ctx->optval[0] < 128) */
905 BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
906 BPF_MOV64_IMM(BPF_REG_0, 1),
907 BPF_JMP_A(1),
908 /* } */
909
910 /* } else { */
911 BPF_MOV64_IMM(BPF_REG_0, 0),
912 /* } */
913
914 BPF_EXIT_INSN(),
915 },
916 .attach_type = BPF_CGROUP_SETSOCKOPT,
917 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
918
919 .get_level = SOL_IP,
920 .set_level = SOL_IP,
921
922 .get_optname = IP_TOS,
923 .set_optname = IP_TOS,
924
925 .set_optval = { 0x81 },
926 .set_optlen = 1,
927 .get_optval = { 0x00 },
928 .get_optlen = 1,
929
930 .error = EPERM_SETSOCKOPT,
931 },
932
933 /* ==================== prog_type ==================== */
934
935 {
936 .descr = "can attach only BPF_CGROUP_SETSOCKOP",
937 .insns = {
938 /* return 1 */
939 BPF_MOV64_IMM(BPF_REG_0, 1),
940 BPF_EXIT_INSN(),
941
942 },
943 .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
944 .attach_type = BPF_CGROUP_SETSOCKOPT,
945 .expected_attach_type = 0,
946 .error = DENY_ATTACH,
947 },
948
949 {
950 .descr = "can attach only BPF_CGROUP_GETSOCKOP",
951 .insns = {
952 /* return 1 */
953 BPF_MOV64_IMM(BPF_REG_0, 1),
954 BPF_EXIT_INSN(),
955
956 },
957 .prog_type = BPF_PROG_TYPE_CGROUP_SKB,
958 .attach_type = BPF_CGROUP_GETSOCKOPT,
959 .expected_attach_type = 0,
960 .error = DENY_ATTACH,
961 },
962};
963
964static int load_prog(const struct bpf_insn *insns,
965 enum bpf_prog_type prog_type,
966 enum bpf_attach_type expected_attach_type)
967{
968 LIBBPF_OPTS(bpf_prog_load_opts, opts,
969 .expected_attach_type = expected_attach_type,
970 .log_level = 2,
971 .log_buf = bpf_log_buf,
972 .log_size = sizeof(bpf_log_buf),
973 );
974 int fd, insns_cnt = 0;
975
976 for (;
977 insns[insns_cnt].code != (BPF_JMP | BPF_EXIT);
978 insns_cnt++) {
979 }
980 insns_cnt++;
981
982 fd = bpf_prog_load(prog_type, NULL, "GPL", insns, insns_cnt, &opts);
983 if (verbose && fd < 0)
984 fprintf(stderr, "%s\n", bpf_log_buf);
985
986 return fd;
987}
988
989/* Core function that handles io_uring ring initialization,
990 * sending SQE with sockopt command and waiting for the CQE.
991 */
992static int uring_sockopt(int op, int fd, int level, int optname,
993 const void *optval, socklen_t optlen)
994{
995 struct io_uring_cqe *cqe;
996 struct io_uring_sqe *sqe;
997 struct io_uring ring;
998 int err;
999
1000 err = io_uring_queue_init(1, &ring, 0);
1001 if (!ASSERT_OK(err, "io_uring initialization"))
1002 return err;
1003
1004 sqe = io_uring_get_sqe(&ring);
1005 if (!ASSERT_NEQ(sqe, NULL, "Get an SQE")) {
1006 err = -1;
1007 goto fail;
1008 }
1009
1010 io_uring_prep_cmd(sqe, op, fd, level, optname, optval, optlen);
1011
1012 err = io_uring_submit(&ring);
1013 if (!ASSERT_EQ(err, 1, "Submit SQE"))
1014 goto fail;
1015
1016 err = io_uring_wait_cqe(&ring, &cqe);
1017 if (!ASSERT_OK(err, "Wait for CQE"))
1018 goto fail;
1019
1020 err = cqe->res;
1021
1022fail:
1023 io_uring_queue_exit(&ring);
1024
1025 return err;
1026}
1027
1028static int uring_setsockopt(int fd, int level, int optname, const void *optval,
1029 socklen_t optlen)
1030{
1031 return uring_sockopt(SOCKET_URING_OP_SETSOCKOPT, fd, level, optname,
1032 optval, optlen);
1033}
1034
1035static int uring_getsockopt(int fd, int level, int optname, void *optval,
1036 socklen_t *optlen)
1037{
1038 int ret = uring_sockopt(SOCKET_URING_OP_GETSOCKOPT, fd, level, optname,
1039 optval, *optlen);
1040 if (ret < 0)
1041 return ret;
1042
1043 /* Populate optlen back to be compatible with systemcall interface,
1044 * and simplify the test.
1045 */
1046 *optlen = ret;
1047
1048 return 0;
1049}
1050
1051/* Execute the setsocktopt operation */
1052static int call_setsockopt(bool use_io_uring, int fd, int level, int optname,
1053 const void *optval, socklen_t optlen)
1054{
1055 if (use_io_uring)
1056 return uring_setsockopt(fd, level, optname, optval, optlen);
1057
1058 return setsockopt(fd, level, optname, optval, optlen);
1059}
1060
1061/* Execute the getsocktopt operation */
1062static int call_getsockopt(bool use_io_uring, int fd, int level, int optname,
1063 void *optval, socklen_t *optlen)
1064{
1065 if (use_io_uring)
1066 return uring_getsockopt(fd, level, optname, optval, optlen);
1067
1068 return getsockopt(fd, level, optname, optval, optlen);
1069}
1070
1071static int run_test(int cgroup_fd, struct sockopt_test *test, bool use_io_uring,
1072 bool use_link)
1073{
1074 int prog_type = BPF_PROG_TYPE_CGROUP_SOCKOPT;
1075 int sock_fd, err, prog_fd, link_fd = -1;
1076 void *optval = NULL;
1077 int ret = 0;
1078
1079 if (test->prog_type)
1080 prog_type = test->prog_type;
1081
1082 prog_fd = load_prog(test->insns, prog_type, test->expected_attach_type);
1083 if (prog_fd < 0) {
1084 if (test->error == DENY_LOAD)
1085 return 0;
1086
1087 log_err("Failed to load BPF program");
1088 return -1;
1089 }
1090
1091 if (use_link) {
1092 err = bpf_link_create(prog_fd, cgroup_fd, test->attach_type, NULL);
1093 link_fd = err;
1094 } else {
1095 err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0);
1096 }
1097 if (err < 0) {
1098 if (test->error == DENY_ATTACH)
1099 goto close_prog_fd;
1100
1101 log_err("Failed to attach BPF program");
1102 ret = -1;
1103 goto close_prog_fd;
1104 }
1105
1106 sock_fd = socket(AF_INET, SOCK_STREAM, 0);
1107 if (sock_fd < 0) {
1108 log_err("Failed to create AF_INET socket");
1109 ret = -1;
1110 goto detach_prog;
1111 }
1112
1113 if (test->set_optlen) {
1114 if (test->set_optlen >= PAGE_SIZE) {
1115 int num_pages = test->set_optlen / PAGE_SIZE;
1116 int remainder = test->set_optlen % PAGE_SIZE;
1117
1118 test->set_optlen = num_pages * sysconf(_SC_PAGESIZE) + remainder;
1119 }
1120
1121 err = call_setsockopt(use_io_uring, sock_fd, test->set_level,
1122 test->set_optname, test->set_optval,
1123 test->set_optlen);
1124 if (err) {
1125 if (errno == EPERM && test->error == EPERM_SETSOCKOPT)
1126 goto close_sock_fd;
1127 if (errno == EFAULT && test->error == EFAULT_SETSOCKOPT)
1128 goto free_optval;
1129
1130 log_err("Failed to call setsockopt");
1131 ret = -1;
1132 goto close_sock_fd;
1133 }
1134 }
1135
1136 if (test->get_optlen) {
1137 if (test->get_optlen >= PAGE_SIZE) {
1138 int num_pages = test->get_optlen / PAGE_SIZE;
1139 int remainder = test->get_optlen % PAGE_SIZE;
1140
1141 test->get_optlen = num_pages * sysconf(_SC_PAGESIZE) + remainder;
1142 }
1143
1144 optval = malloc(test->get_optlen);
1145 memset(optval, 0, test->get_optlen);
1146 socklen_t optlen = test->get_optlen;
1147 socklen_t expected_get_optlen = test->get_optlen_ret ?:
1148 test->get_optlen;
1149
1150 err = call_getsockopt(use_io_uring, sock_fd, test->get_level,
1151 test->get_optname, optval, &optlen);
1152 if (err) {
1153 if (errno == EOPNOTSUPP && test->error == EOPNOTSUPP_GETSOCKOPT)
1154 goto free_optval;
1155 if (errno == EPERM && test->error == EPERM_GETSOCKOPT)
1156 goto free_optval;
1157 if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT)
1158 goto free_optval;
1159
1160 log_err("Failed to call getsockopt");
1161 ret = -1;
1162 goto free_optval;
1163 }
1164
1165 if (optlen != expected_get_optlen) {
1166 errno = 0;
1167 log_err("getsockopt returned unexpected optlen");
1168 ret = -1;
1169 goto free_optval;
1170 }
1171
1172 if (memcmp(optval, test->get_optval, optlen) != 0) {
1173 errno = 0;
1174 log_err("getsockopt returned unexpected optval");
1175 ret = -1;
1176 goto free_optval;
1177 }
1178 }
1179
1180 ret = test->error != OK;
1181
1182free_optval:
1183 free(optval);
1184close_sock_fd:
1185 close(sock_fd);
1186detach_prog:
1187 if (use_link) {
1188 if (link_fd >= 0)
1189 close(link_fd);
1190 } else {
1191 bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);
1192 }
1193close_prog_fd:
1194 close(prog_fd);
1195 return ret;
1196}
1197
1198void test_sockopt(void)
1199{
1200 int cgroup_fd, i;
1201
1202 cgroup_fd = test__join_cgroup("/sockopt");
1203 if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup"))
1204 return;
1205
1206 for (i = 0; i < ARRAY_SIZE(tests); i++) {
1207 if (!test__start_subtest(tests[i].descr))
1208 continue;
1209
1210 ASSERT_OK(run_test(cgroup_fd, &tests[i], false, false),
1211 tests[i].descr);
1212 ASSERT_OK(run_test(cgroup_fd, &tests[i], false, true),
1213 tests[i].descr);
1214 if (tests[i].io_uring_support)
1215 ASSERT_OK(run_test(cgroup_fd, &tests[i], true, false),
1216 tests[i].descr);
1217 }
1218
1219 close(cgroup_fd);
1220}
1// SPDX-License-Identifier: GPL-2.0
2#include <test_progs.h>
3#include "cgroup_helpers.h"
4
5static char bpf_log_buf[4096];
6static bool verbose;
7
8enum sockopt_test_error {
9 OK = 0,
10 DENY_LOAD,
11 DENY_ATTACH,
12 EPERM_GETSOCKOPT,
13 EFAULT_GETSOCKOPT,
14 EPERM_SETSOCKOPT,
15 EFAULT_SETSOCKOPT,
16};
17
18static 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;
23
24 int set_optname;
25 int set_level;
26 const char set_optval[64];
27 socklen_t set_optlen;
28
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;
34
35 enum sockopt_test_error error;
36} tests[] = {
37
38 /* ==================== getsockopt ==================== */
39
40 {
41 .descr = "getsockopt: no expected_attach_type",
42 .insns = {
43 /* return 1 */
44 BPF_MOV64_IMM(BPF_REG_0, 1),
45 BPF_EXIT_INSN(),
46
47 },
48 .attach_type = BPF_CGROUP_GETSOCKOPT,
49 .expected_attach_type = 0,
50 .error = DENY_LOAD,
51 },
52 {
53 .descr = "getsockopt: wrong expected_attach_type",
54 .insns = {
55 /* return 1 */
56 BPF_MOV64_IMM(BPF_REG_0, 1),
57 BPF_EXIT_INSN(),
58
59 },
60 .attach_type = BPF_CGROUP_GETSOCKOPT,
61 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
62 .error = DENY_ATTACH,
63 },
64 {
65 .descr = "getsockopt: bypass bpf hook",
66 .insns = {
67 /* return 1 */
68 BPF_MOV64_IMM(BPF_REG_0, 1),
69 BPF_EXIT_INSN(),
70 },
71 .attach_type = BPF_CGROUP_GETSOCKOPT,
72 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
73
74 .get_level = SOL_IP,
75 .set_level = SOL_IP,
76
77 .get_optname = IP_TOS,
78 .set_optname = IP_TOS,
79
80 .set_optval = { 1 << 3 },
81 .set_optlen = 1,
82
83 .get_optval = { 1 << 3 },
84 .get_optlen = 1,
85 },
86 {
87 .descr = "getsockopt: return EPERM from bpf hook",
88 .insns = {
89 BPF_MOV64_IMM(BPF_REG_0, 0),
90 BPF_EXIT_INSN(),
91 },
92 .attach_type = BPF_CGROUP_GETSOCKOPT,
93 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
94
95 .get_level = SOL_IP,
96 .get_optname = IP_TOS,
97
98 .get_optlen = 1,
99 .error = EPERM_GETSOCKOPT,
100 },
101 {
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)),
107
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),
111
112 /* return 1 */
113 BPF_MOV64_IMM(BPF_REG_0, 1),
114 BPF_EXIT_INSN(),
115 },
116 .attach_type = BPF_CGROUP_GETSOCKOPT,
117 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
118 .error = DENY_LOAD,
119 },
120 {
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)),
126
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(),
141 },
142 .attach_type = BPF_CGROUP_GETSOCKOPT,
143 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
144
145 .get_level = 123,
146
147 .get_optlen = 1,
148 },
149 {
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(),
157 },
158 .attach_type = BPF_CGROUP_GETSOCKOPT,
159 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
160
161 .error = DENY_LOAD,
162 },
163 {
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)),
169
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(),
184 },
185 .attach_type = BPF_CGROUP_GETSOCKOPT,
186 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
187
188 .get_optname = 123,
189
190 .get_optlen = 1,
191 },
192 {
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)),
198
199 /* return 1 */
200 BPF_MOV64_IMM(BPF_REG_0, 1),
201 BPF_EXIT_INSN(),
202 },
203 .attach_type = BPF_CGROUP_GETSOCKOPT,
204 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
205
206 .get_level = SOL_IP,
207 .get_optname = IP_TOS,
208 .get_optlen = 1,
209 },
210 {
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(),
218 },
219 .attach_type = BPF_CGROUP_GETSOCKOPT,
220 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
221
222 .error = DENY_LOAD,
223 },
224 {
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)),
230
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(),
245 },
246 .attach_type = BPF_CGROUP_GETSOCKOPT,
247 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
248
249 .get_optlen = 64,
250 },
251 {
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)),
258
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)),
263
264 /* return 1 */
265 BPF_MOV64_IMM(BPF_REG_0, 1),
266 BPF_EXIT_INSN(),
267 },
268 .attach_type = BPF_CGROUP_GETSOCKOPT,
269 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
270
271 .get_optlen = 64,
272
273 .error = EFAULT_GETSOCKOPT,
274 },
275 {
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)),
282
283 /* return 1 */
284 BPF_MOV64_IMM(BPF_REG_0, 1),
285 BPF_EXIT_INSN(),
286 },
287 .attach_type = BPF_CGROUP_GETSOCKOPT,
288 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
289
290 .get_optlen = 64,
291
292 .error = EFAULT_GETSOCKOPT,
293 },
294 {
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(),
308 },
309 .attach_type = BPF_CGROUP_GETSOCKOPT,
310 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
311
312 .get_optlen = 64,
313 .get_optlen_ret = 32,
314 },
315 {
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(),
323 },
324 .attach_type = BPF_CGROUP_GETSOCKOPT,
325 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
326
327 .error = DENY_LOAD,
328 },
329 {
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(),
337 },
338 .attach_type = BPF_CGROUP_GETSOCKOPT,
339 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
340
341 .error = DENY_LOAD,
342 },
343 {
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),
353
354 /* r7 = ctx->optval_end */
355 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
356 offsetof(struct bpf_sockopt, optval_end)),
357
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 /* } */
363
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)),
368
369 /* return 1*/
370 BPF_MOV64_IMM(BPF_REG_0, 1),
371 BPF_EXIT_INSN(),
372 },
373 .attach_type = BPF_CGROUP_GETSOCKOPT,
374 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
375
376 .get_level = SOL_IP,
377 .get_optname = IP_TOS,
378
379 .get_optval = { 0xF0 },
380 .get_optlen = 1,
381 },
382
383 /* ==================== setsockopt ==================== */
384
385 {
386 .descr = "setsockopt: no expected_attach_type",
387 .insns = {
388 /* return 1 */
389 BPF_MOV64_IMM(BPF_REG_0, 1),
390 BPF_EXIT_INSN(),
391
392 },
393 .attach_type = BPF_CGROUP_SETSOCKOPT,
394 .expected_attach_type = 0,
395 .error = DENY_LOAD,
396 },
397 {
398 .descr = "setsockopt: wrong expected_attach_type",
399 .insns = {
400 /* return 1 */
401 BPF_MOV64_IMM(BPF_REG_0, 1),
402 BPF_EXIT_INSN(),
403
404 },
405 .attach_type = BPF_CGROUP_SETSOCKOPT,
406 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
407 .error = DENY_ATTACH,
408 },
409 {
410 .descr = "setsockopt: bypass bpf hook",
411 .insns = {
412 /* return 1 */
413 BPF_MOV64_IMM(BPF_REG_0, 1),
414 BPF_EXIT_INSN(),
415 },
416 .attach_type = BPF_CGROUP_SETSOCKOPT,
417 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
418
419 .get_level = SOL_IP,
420 .set_level = SOL_IP,
421
422 .get_optname = IP_TOS,
423 .set_optname = IP_TOS,
424
425 .set_optval = { 1 << 3 },
426 .set_optlen = 1,
427
428 .get_optval = { 1 << 3 },
429 .get_optlen = 1,
430 },
431 {
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(),
437 },
438 .attach_type = BPF_CGROUP_SETSOCKOPT,
439 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
440
441 .set_level = SOL_IP,
442 .set_optname = IP_TOS,
443
444 .set_optlen = 1,
445 .error = EPERM_SETSOCKOPT,
446 },
447 {
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)),
453
454 /* r0 = ctx->optval[0] */
455 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
456
457 /* return 1 */
458 BPF_MOV64_IMM(BPF_REG_0, 1),
459 BPF_EXIT_INSN(),
460 },
461 .attach_type = BPF_CGROUP_SETSOCKOPT,
462 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
463 .error = DENY_LOAD,
464 },
465 {
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)),
471
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(),
486 },
487 .attach_type = BPF_CGROUP_SETSOCKOPT,
488 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
489
490 .set_level = 123,
491
492 .set_optlen = 1,
493 },
494 {
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(),
504 },
505 .attach_type = BPF_CGROUP_SETSOCKOPT,
506 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
507
508 .get_level = SOL_IP,
509 .set_level = 234, /* should be rewritten to SOL_IP */
510
511 .get_optname = IP_TOS,
512 .set_optname = IP_TOS,
513
514 .set_optval = { 1 << 3 },
515 .set_optlen = 1,
516 .get_optval = { 1 << 3 },
517 .get_optlen = 1,
518 },
519 {
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)),
525
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(),
540 },
541 .attach_type = BPF_CGROUP_SETSOCKOPT,
542 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
543
544 .set_optname = 123,
545
546 .set_optlen = 1,
547 },
548 {
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(),
558 },
559 .attach_type = BPF_CGROUP_SETSOCKOPT,
560 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
561
562 .get_level = SOL_IP,
563 .set_level = SOL_IP,
564
565 .get_optname = IP_TOS,
566 .set_optname = 456, /* should be rewritten to IP_TOS */
567
568 .set_optval = { 1 << 3 },
569 .set_optlen = 1,
570 .get_optval = { 1 << 3 },
571 .get_optlen = 1,
572 },
573 {
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)),
579
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(),
594 },
595 .attach_type = BPF_CGROUP_SETSOCKOPT,
596 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
597
598 .set_optlen = 64,
599 },
600 {
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(),
610 },
611 .attach_type = BPF_CGROUP_SETSOCKOPT,
612 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
613
614 .set_optlen = 64,
615 },
616 {
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(),
626 },
627 .attach_type = BPF_CGROUP_SETSOCKOPT,
628 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
629
630 .set_optlen = 4,
631
632 .error = EFAULT_SETSOCKOPT,
633 },
634 {
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(),
643 },
644 .attach_type = BPF_CGROUP_SETSOCKOPT,
645 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
646
647 .set_optlen = 64,
648
649 .error = EFAULT_SETSOCKOPT,
650 },
651 {
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),
661
662 /* r7 = ctx->optval_end */
663 BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
664 offsetof(struct bpf_sockopt, optval_end)),
665
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 /* } */
671
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)),
676
677 /* return 1*/
678 BPF_MOV64_IMM(BPF_REG_0, 1),
679 BPF_EXIT_INSN(),
680 },
681 .attach_type = BPF_CGROUP_SETSOCKOPT,
682 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
683
684 .get_level = SOL_IP,
685 .set_level = SOL_IP,
686
687 .get_optname = IP_TOS,
688 .set_optname = IP_TOS,
689
690 .set_optval = { 1, 1, 1, 1 },
691 .set_optlen = 4,
692 .get_optval = { 1 << 3 },
693 .get_optlen = 1,
694 },
695 {
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)),
702
703 /* return 1 */
704 BPF_MOV64_IMM(BPF_REG_0, 1),
705 BPF_EXIT_INSN(),
706 },
707 .attach_type = BPF_CGROUP_SETSOCKOPT,
708 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
709
710 .error = DENY_LOAD,
711 },
712 {
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)),
718
719 /* return 1 */
720 BPF_MOV64_IMM(BPF_REG_0, 1),
721 BPF_EXIT_INSN(),
722 },
723 .attach_type = BPF_CGROUP_SETSOCKOPT,
724 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
725
726 .error = DENY_LOAD,
727 },
728 {
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(),
736 },
737 .attach_type = BPF_CGROUP_SETSOCKOPT,
738 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
739
740 .error = DENY_LOAD,
741 },
742 {
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(),
750 },
751 .attach_type = BPF_CGROUP_SETSOCKOPT,
752 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
753
754 .error = DENY_LOAD,
755 },
756 {
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),
765
766 /* r8 = ctx->optval_end */
767 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
768 offsetof(struct bpf_sockopt, optval_end)),
769
770 /* if (ctx->optval + 1 <= ctx->optval_end) { */
771 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
772
773 /* r9 = ctx->optval[0] */
774 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
775
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 /* } */
781
782 /* } else { */
783 BPF_MOV64_IMM(BPF_REG_0, 0),
784 /* } */
785
786 BPF_EXIT_INSN(),
787 },
788 .attach_type = BPF_CGROUP_SETSOCKOPT,
789 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
790
791 .get_level = SOL_IP,
792 .set_level = SOL_IP,
793
794 .get_optname = IP_TOS,
795 .set_optname = IP_TOS,
796
797 .set_optval = { 0x80 },
798 .set_optlen = 1,
799 .get_optval = { 0x80 },
800 .get_optlen = 1,
801 },
802 {
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),
811
812 /* r8 = ctx->optval_end */
813 BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
814 offsetof(struct bpf_sockopt, optval_end)),
815
816 /* if (ctx->optval + 1 <= ctx->optval_end) { */
817 BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
818
819 /* r9 = ctx->optval[0] */
820 BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
821
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 /* } */
827
828 /* } else { */
829 BPF_MOV64_IMM(BPF_REG_0, 0),
830 /* } */
831
832 BPF_EXIT_INSN(),
833 },
834 .attach_type = BPF_CGROUP_SETSOCKOPT,
835 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
836
837 .get_level = SOL_IP,
838 .set_level = SOL_IP,
839
840 .get_optname = IP_TOS,
841 .set_optname = IP_TOS,
842
843 .set_optval = { 0x81 },
844 .set_optlen = 1,
845 .get_optval = { 0x00 },
846 .get_optlen = 1,
847
848 .error = EPERM_SETSOCKOPT,
849 },
850};
851
852static int load_prog(const struct bpf_insn *insns,
853 enum bpf_attach_type expected_attach_type)
854{
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,
861 };
862 int fd;
863
864 for (;
865 insns[attr.insns_cnt].code != (BPF_JMP | BPF_EXIT);
866 attr.insns_cnt++) {
867 }
868 attr.insns_cnt++;
869
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);
873
874 return fd;
875}
876
877static int run_test(int cgroup_fd, struct sockopt_test *test)
878{
879 int sock_fd, err, prog_fd;
880 void *optval = NULL;
881 int ret = 0;
882
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;
887
888 log_err("Failed to load BPF program");
889 return -1;
890 }
891
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;
896
897 log_err("Failed to attach BPF program");
898 ret = -1;
899 goto close_prog_fd;
900 }
901
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;
907 }
908
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;
917
918 log_err("Failed to call setsockopt");
919 ret = -1;
920 goto close_sock_fd;
921 }
922 }
923
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;
929
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;
937
938 log_err("Failed to call getsockopt");
939 ret = -1;
940 goto free_optval;
941 }
942
943 if (optlen != expected_get_optlen) {
944 errno = 0;
945 log_err("getsockopt returned unexpected optlen");
946 ret = -1;
947 goto free_optval;
948 }
949
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;
955 }
956 }
957
958 ret = test->error != OK;
959
960free_optval:
961 free(optval);
962close_sock_fd:
963 close(sock_fd);
964detach_prog:
965 bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);
966close_prog_fd:
967 close(prog_fd);
968 return ret;
969}
970
971void test_sockopt(void)
972{
973 int cgroup_fd, i;
974
975 cgroup_fd = test__join_cgroup("/sockopt");
976 if (CHECK_FAIL(cgroup_fd < 0))
977 return;
978
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]));
982 }
983
984 close(cgroup_fd);
985}