xref: /linux/tools/testing/selftests/bpf/prog_tests/sockopt.c (revision 90d32e92011eaae8e70a9169b4e7acf4ca8f9d3a)
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 
6 static char bpf_log_buf[4096];
7 static bool verbose;
8 
9 #ifndef PAGE_SIZE
10 #define PAGE_SIZE 4096
11 #endif
12 
13 enum 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 
24 static 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 
964 static 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  */
992 static 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 
1022 fail:
1023 	io_uring_queue_exit(&ring);
1024 
1025 	return err;
1026 }
1027 
1028 static 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 
1035 static 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 */
1052 static 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 */
1062 static 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 
1071 static 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 
1182 free_optval:
1183 	free(optval);
1184 close_sock_fd:
1185 	close(sock_fd);
1186 detach_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 	}
1193 close_prog_fd:
1194 	close(prog_fd);
1195 	return ret;
1196 }
1197 
1198 void 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 }
1221