selftests/bpf/sockopt: Add io_uring support
[linux-block.git] / tools / testing / selftests / bpf / prog_tests / sockopt.c
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 }