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