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 
852 static int load_prog(const struct bpf_insn *insns,
853 		     enum bpf_attach_type expected_attach_type)
854 {
855 	LIBBPF_OPTS(bpf_prog_load_opts, opts,
856 		.expected_attach_type = expected_attach_type,
857 		.log_level = 2,
858 		.log_buf = bpf_log_buf,
859 		.log_size = sizeof(bpf_log_buf),
860 	);
861 	int fd, insns_cnt = 0;
862 
863 	for (;
864 	     insns[insns_cnt].code != (BPF_JMP | BPF_EXIT);
865 	     insns_cnt++) {
866 	}
867 	insns_cnt++;
868 
869 	fd = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCKOPT, NULL, "GPL", insns, insns_cnt, &opts);
870 	if (verbose && fd < 0)
871 		fprintf(stderr, "%s\n", bpf_log_buf);
872 
873 	return fd;
874 }
875 
876 static int run_test(int cgroup_fd, struct sockopt_test *test)
877 {
878 	int sock_fd, err, prog_fd;
879 	void *optval = NULL;
880 	int ret = 0;
881 
882 	prog_fd = load_prog(test->insns, test->expected_attach_type);
883 	if (prog_fd < 0) {
884 		if (test->error == DENY_LOAD)
885 			return 0;
886 
887 		log_err("Failed to load BPF program");
888 		return -1;
889 	}
890 
891 	err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0);
892 	if (err < 0) {
893 		if (test->error == DENY_ATTACH)
894 			goto close_prog_fd;
895 
896 		log_err("Failed to attach BPF program");
897 		ret = -1;
898 		goto close_prog_fd;
899 	}
900 
901 	sock_fd = socket(AF_INET, SOCK_STREAM, 0);
902 	if (sock_fd < 0) {
903 		log_err("Failed to create AF_INET socket");
904 		ret = -1;
905 		goto detach_prog;
906 	}
907 
908 	if (test->set_optlen) {
909 		err = setsockopt(sock_fd, test->set_level, test->set_optname,
910 				 test->set_optval, test->set_optlen);
911 		if (err) {
912 			if (errno == EPERM && test->error == EPERM_SETSOCKOPT)
913 				goto close_sock_fd;
914 			if (errno == EFAULT && test->error == EFAULT_SETSOCKOPT)
915 				goto free_optval;
916 
917 			log_err("Failed to call setsockopt");
918 			ret = -1;
919 			goto close_sock_fd;
920 		}
921 	}
922 
923 	if (test->get_optlen) {
924 		optval = malloc(test->get_optlen);
925 		socklen_t optlen = test->get_optlen;
926 		socklen_t expected_get_optlen = test->get_optlen_ret ?:
927 			test->get_optlen;
928 
929 		err = getsockopt(sock_fd, test->get_level, test->get_optname,
930 				 optval, &optlen);
931 		if (err) {
932 			if (errno == EPERM && test->error == EPERM_GETSOCKOPT)
933 				goto free_optval;
934 			if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT)
935 				goto free_optval;
936 
937 			log_err("Failed to call getsockopt");
938 			ret = -1;
939 			goto free_optval;
940 		}
941 
942 		if (optlen != expected_get_optlen) {
943 			errno = 0;
944 			log_err("getsockopt returned unexpected optlen");
945 			ret = -1;
946 			goto free_optval;
947 		}
948 
949 		if (memcmp(optval, test->get_optval, optlen) != 0) {
950 			errno = 0;
951 			log_err("getsockopt returned unexpected optval");
952 			ret = -1;
953 			goto free_optval;
954 		}
955 	}
956 
957 	ret = test->error != OK;
958 
959 free_optval:
960 	free(optval);
961 close_sock_fd:
962 	close(sock_fd);
963 detach_prog:
964 	bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);
965 close_prog_fd:
966 	close(prog_fd);
967 	return ret;
968 }
969 
970 void test_sockopt(void)
971 {
972 	int cgroup_fd, i;
973 
974 	cgroup_fd = test__join_cgroup("/sockopt");
975 	if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup"))
976 		return;
977 
978 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
979 		test__start_subtest(tests[i].descr);
980 		ASSERT_OK(run_test(cgroup_fd, &tests[i]), tests[i].descr);
981 	}
982 
983 	close(cgroup_fd);
984 }
985