1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 #include "cgroup_helpers.h"
4
5 static char bpf_log_buf[4096];
6 static bool verbose;
7
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,
16 };
17
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;
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
load_prog(const struct bpf_insn * insns,enum bpf_attach_type expected_attach_type)852 static 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
run_test(int cgroup_fd,struct sockopt_test * test)877 static 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
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;
969 }
970
test_sockopt(void)971 void 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 }
986