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