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