Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / tools / testing / selftests / bpf / test_sockopt.c
1 // SPDX-License-Identifier: GPL-2.0
2
3 #include <errno.h>
4 #include <stdio.h>
5 #include <unistd.h>
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <netinet/in.h>
9
10 #include <linux/filter.h>
11 #include <bpf/bpf.h>
12 #include <bpf/libbpf.h>
13
14 #include "bpf_rlimit.h"
15 #include "bpf_util.h"
16 #include "cgroup_helpers.h"
17
18 #define CG_PATH                         "/sockopt"
19
20 static char bpf_log_buf[4096];
21 static bool verbose;
22
23 enum sockopt_test_error {
24         OK = 0,
25         DENY_LOAD,
26         DENY_ATTACH,
27         EPERM_GETSOCKOPT,
28         EFAULT_GETSOCKOPT,
29         EPERM_SETSOCKOPT,
30         EFAULT_SETSOCKOPT,
31 };
32
33 static struct sockopt_test {
34         const char                      *descr;
35         const struct bpf_insn           insns[64];
36         enum bpf_attach_type            attach_type;
37         enum bpf_attach_type            expected_attach_type;
38
39         int                             set_optname;
40         int                             set_level;
41         const char                      set_optval[64];
42         socklen_t                       set_optlen;
43
44         int                             get_optname;
45         int                             get_level;
46         const char                      get_optval[64];
47         socklen_t                       get_optlen;
48         socklen_t                       get_optlen_ret;
49
50         enum sockopt_test_error         error;
51 } tests[] = {
52
53         /* ==================== getsockopt ====================  */
54
55         {
56                 .descr = "getsockopt: no expected_attach_type",
57                 .insns = {
58                         /* return 1 */
59                         BPF_MOV64_IMM(BPF_REG_0, 1),
60                         BPF_EXIT_INSN(),
61
62                 },
63                 .attach_type = BPF_CGROUP_GETSOCKOPT,
64                 .expected_attach_type = 0,
65                 .error = DENY_LOAD,
66         },
67         {
68                 .descr = "getsockopt: wrong expected_attach_type",
69                 .insns = {
70                         /* return 1 */
71                         BPF_MOV64_IMM(BPF_REG_0, 1),
72                         BPF_EXIT_INSN(),
73
74                 },
75                 .attach_type = BPF_CGROUP_GETSOCKOPT,
76                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
77                 .error = DENY_ATTACH,
78         },
79         {
80                 .descr = "getsockopt: bypass bpf hook",
81                 .insns = {
82                         /* return 1 */
83                         BPF_MOV64_IMM(BPF_REG_0, 1),
84                         BPF_EXIT_INSN(),
85                 },
86                 .attach_type = BPF_CGROUP_GETSOCKOPT,
87                 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
88
89                 .get_level = SOL_IP,
90                 .set_level = SOL_IP,
91
92                 .get_optname = IP_TOS,
93                 .set_optname = IP_TOS,
94
95                 .set_optval = { 1 << 3 },
96                 .set_optlen = 1,
97
98                 .get_optval = { 1 << 3 },
99                 .get_optlen = 1,
100         },
101         {
102                 .descr = "getsockopt: return EPERM from bpf hook",
103                 .insns = {
104                         BPF_MOV64_IMM(BPF_REG_0, 0),
105                         BPF_EXIT_INSN(),
106                 },
107                 .attach_type = BPF_CGROUP_GETSOCKOPT,
108                 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
109
110                 .get_level = SOL_IP,
111                 .get_optname = IP_TOS,
112
113                 .get_optlen = 1,
114                 .error = EPERM_GETSOCKOPT,
115         },
116         {
117                 .descr = "getsockopt: no optval bounds check, deny loading",
118                 .insns = {
119                         /* r6 = ctx->optval */
120                         BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
121                                     offsetof(struct bpf_sockopt, optval)),
122
123                         /* ctx->optval[0] = 0x80 */
124                         BPF_MOV64_IMM(BPF_REG_0, 0x80),
125                         BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0),
126
127                         /* return 1 */
128                         BPF_MOV64_IMM(BPF_REG_0, 1),
129                         BPF_EXIT_INSN(),
130                 },
131                 .attach_type = BPF_CGROUP_GETSOCKOPT,
132                 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
133                 .error = DENY_LOAD,
134         },
135         {
136                 .descr = "getsockopt: read ctx->level",
137                 .insns = {
138                         /* r6 = ctx->level */
139                         BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
140                                     offsetof(struct bpf_sockopt, level)),
141
142                         /* if (ctx->level == 123) { */
143                         BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
144                         /* ctx->retval = 0 */
145                         BPF_MOV64_IMM(BPF_REG_0, 0),
146                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
147                                     offsetof(struct bpf_sockopt, retval)),
148                         /* return 1 */
149                         BPF_MOV64_IMM(BPF_REG_0, 1),
150                         BPF_JMP_A(1),
151                         /* } else { */
152                         /* return 0 */
153                         BPF_MOV64_IMM(BPF_REG_0, 0),
154                         /* } */
155                         BPF_EXIT_INSN(),
156                 },
157                 .attach_type = BPF_CGROUP_GETSOCKOPT,
158                 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
159
160                 .get_level = 123,
161
162                 .get_optlen = 1,
163         },
164         {
165                 .descr = "getsockopt: deny writing to ctx->level",
166                 .insns = {
167                         /* ctx->level = 1 */
168                         BPF_MOV64_IMM(BPF_REG_0, 1),
169                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
170                                     offsetof(struct bpf_sockopt, level)),
171                         BPF_EXIT_INSN(),
172                 },
173                 .attach_type = BPF_CGROUP_GETSOCKOPT,
174                 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
175
176                 .error = DENY_LOAD,
177         },
178         {
179                 .descr = "getsockopt: read ctx->optname",
180                 .insns = {
181                         /* r6 = ctx->optname */
182                         BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
183                                     offsetof(struct bpf_sockopt, optname)),
184
185                         /* if (ctx->optname == 123) { */
186                         BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
187                         /* ctx->retval = 0 */
188                         BPF_MOV64_IMM(BPF_REG_0, 0),
189                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
190                                     offsetof(struct bpf_sockopt, retval)),
191                         /* return 1 */
192                         BPF_MOV64_IMM(BPF_REG_0, 1),
193                         BPF_JMP_A(1),
194                         /* } else { */
195                         /* return 0 */
196                         BPF_MOV64_IMM(BPF_REG_0, 0),
197                         /* } */
198                         BPF_EXIT_INSN(),
199                 },
200                 .attach_type = BPF_CGROUP_GETSOCKOPT,
201                 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
202
203                 .get_optname = 123,
204
205                 .get_optlen = 1,
206         },
207         {
208                 .descr = "getsockopt: read ctx->retval",
209                 .insns = {
210                         /* r6 = ctx->retval */
211                         BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
212                                     offsetof(struct bpf_sockopt, retval)),
213
214                         /* return 1 */
215                         BPF_MOV64_IMM(BPF_REG_0, 1),
216                         BPF_EXIT_INSN(),
217                 },
218                 .attach_type = BPF_CGROUP_GETSOCKOPT,
219                 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
220
221                 .get_level = SOL_IP,
222                 .get_optname = IP_TOS,
223                 .get_optlen = 1,
224         },
225         {
226                 .descr = "getsockopt: deny writing to ctx->optname",
227                 .insns = {
228                         /* ctx->optname = 1 */
229                         BPF_MOV64_IMM(BPF_REG_0, 1),
230                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
231                                     offsetof(struct bpf_sockopt, optname)),
232                         BPF_EXIT_INSN(),
233                 },
234                 .attach_type = BPF_CGROUP_GETSOCKOPT,
235                 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
236
237                 .error = DENY_LOAD,
238         },
239         {
240                 .descr = "getsockopt: read ctx->optlen",
241                 .insns = {
242                         /* r6 = ctx->optlen */
243                         BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
244                                     offsetof(struct bpf_sockopt, optlen)),
245
246                         /* if (ctx->optlen == 64) { */
247                         BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
248                         /* ctx->retval = 0 */
249                         BPF_MOV64_IMM(BPF_REG_0, 0),
250                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
251                                     offsetof(struct bpf_sockopt, retval)),
252                         /* return 1 */
253                         BPF_MOV64_IMM(BPF_REG_0, 1),
254                         BPF_JMP_A(1),
255                         /* } else { */
256                         /* return 0 */
257                         BPF_MOV64_IMM(BPF_REG_0, 0),
258                         /* } */
259                         BPF_EXIT_INSN(),
260                 },
261                 .attach_type = BPF_CGROUP_GETSOCKOPT,
262                 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
263
264                 .get_optlen = 64,
265         },
266         {
267                 .descr = "getsockopt: deny bigger ctx->optlen",
268                 .insns = {
269                         /* ctx->optlen = 65 */
270                         BPF_MOV64_IMM(BPF_REG_0, 65),
271                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
272                                     offsetof(struct bpf_sockopt, optlen)),
273
274                         /* ctx->retval = 0 */
275                         BPF_MOV64_IMM(BPF_REG_0, 0),
276                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
277                                     offsetof(struct bpf_sockopt, retval)),
278
279                         /* return 1 */
280                         BPF_MOV64_IMM(BPF_REG_0, 1),
281                         BPF_EXIT_INSN(),
282                 },
283                 .attach_type = BPF_CGROUP_GETSOCKOPT,
284                 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
285
286                 .get_optlen = 64,
287
288                 .error = EFAULT_GETSOCKOPT,
289         },
290         {
291                 .descr = "getsockopt: deny arbitrary ctx->retval",
292                 .insns = {
293                         /* ctx->retval = 123 */
294                         BPF_MOV64_IMM(BPF_REG_0, 123),
295                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
296                                     offsetof(struct bpf_sockopt, retval)),
297
298                         /* return 1 */
299                         BPF_MOV64_IMM(BPF_REG_0, 1),
300                         BPF_EXIT_INSN(),
301                 },
302                 .attach_type = BPF_CGROUP_GETSOCKOPT,
303                 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
304
305                 .get_optlen = 64,
306
307                 .error = EFAULT_GETSOCKOPT,
308         },
309         {
310                 .descr = "getsockopt: support smaller ctx->optlen",
311                 .insns = {
312                         /* ctx->optlen = 32 */
313                         BPF_MOV64_IMM(BPF_REG_0, 32),
314                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
315                                     offsetof(struct bpf_sockopt, optlen)),
316                         /* ctx->retval = 0 */
317                         BPF_MOV64_IMM(BPF_REG_0, 0),
318                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
319                                     offsetof(struct bpf_sockopt, retval)),
320                         /* return 1 */
321                         BPF_MOV64_IMM(BPF_REG_0, 1),
322                         BPF_EXIT_INSN(),
323                 },
324                 .attach_type = BPF_CGROUP_GETSOCKOPT,
325                 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
326
327                 .get_optlen = 64,
328                 .get_optlen_ret = 32,
329         },
330         {
331                 .descr = "getsockopt: deny writing to ctx->optval",
332                 .insns = {
333                         /* ctx->optval = 1 */
334                         BPF_MOV64_IMM(BPF_REG_0, 1),
335                         BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
336                                     offsetof(struct bpf_sockopt, optval)),
337                         BPF_EXIT_INSN(),
338                 },
339                 .attach_type = BPF_CGROUP_GETSOCKOPT,
340                 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
341
342                 .error = DENY_LOAD,
343         },
344         {
345                 .descr = "getsockopt: deny writing to ctx->optval_end",
346                 .insns = {
347                         /* ctx->optval_end = 1 */
348                         BPF_MOV64_IMM(BPF_REG_0, 1),
349                         BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
350                                     offsetof(struct bpf_sockopt, optval_end)),
351                         BPF_EXIT_INSN(),
352                 },
353                 .attach_type = BPF_CGROUP_GETSOCKOPT,
354                 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
355
356                 .error = DENY_LOAD,
357         },
358         {
359                 .descr = "getsockopt: rewrite value",
360                 .insns = {
361                         /* r6 = ctx->optval */
362                         BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
363                                     offsetof(struct bpf_sockopt, optval)),
364                         /* r2 = ctx->optval */
365                         BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
366                         /* r6 = ctx->optval + 1 */
367                         BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
368
369                         /* r7 = ctx->optval_end */
370                         BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
371                                     offsetof(struct bpf_sockopt, optval_end)),
372
373                         /* if (ctx->optval + 1 <= ctx->optval_end) { */
374                         BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
375                         /* ctx->optval[0] = 0xF0 */
376                         BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0),
377                         /* } */
378
379                         /* ctx->retval = 0 */
380                         BPF_MOV64_IMM(BPF_REG_0, 0),
381                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
382                                     offsetof(struct bpf_sockopt, retval)),
383
384                         /* return 1*/
385                         BPF_MOV64_IMM(BPF_REG_0, 1),
386                         BPF_EXIT_INSN(),
387                 },
388                 .attach_type = BPF_CGROUP_GETSOCKOPT,
389                 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
390
391                 .get_level = SOL_IP,
392                 .get_optname = IP_TOS,
393
394                 .get_optval = { 0xF0 },
395                 .get_optlen = 1,
396         },
397
398         /* ==================== setsockopt ====================  */
399
400         {
401                 .descr = "setsockopt: no expected_attach_type",
402                 .insns = {
403                         /* return 1 */
404                         BPF_MOV64_IMM(BPF_REG_0, 1),
405                         BPF_EXIT_INSN(),
406
407                 },
408                 .attach_type = BPF_CGROUP_SETSOCKOPT,
409                 .expected_attach_type = 0,
410                 .error = DENY_LOAD,
411         },
412         {
413                 .descr = "setsockopt: wrong expected_attach_type",
414                 .insns = {
415                         /* return 1 */
416                         BPF_MOV64_IMM(BPF_REG_0, 1),
417                         BPF_EXIT_INSN(),
418
419                 },
420                 .attach_type = BPF_CGROUP_SETSOCKOPT,
421                 .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
422                 .error = DENY_ATTACH,
423         },
424         {
425                 .descr = "setsockopt: bypass bpf hook",
426                 .insns = {
427                         /* return 1 */
428                         BPF_MOV64_IMM(BPF_REG_0, 1),
429                         BPF_EXIT_INSN(),
430                 },
431                 .attach_type = BPF_CGROUP_SETSOCKOPT,
432                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
433
434                 .get_level = SOL_IP,
435                 .set_level = SOL_IP,
436
437                 .get_optname = IP_TOS,
438                 .set_optname = IP_TOS,
439
440                 .set_optval = { 1 << 3 },
441                 .set_optlen = 1,
442
443                 .get_optval = { 1 << 3 },
444                 .get_optlen = 1,
445         },
446         {
447                 .descr = "setsockopt: return EPERM from bpf hook",
448                 .insns = {
449                         /* return 0 */
450                         BPF_MOV64_IMM(BPF_REG_0, 0),
451                         BPF_EXIT_INSN(),
452                 },
453                 .attach_type = BPF_CGROUP_SETSOCKOPT,
454                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
455
456                 .set_level = SOL_IP,
457                 .set_optname = IP_TOS,
458
459                 .set_optlen = 1,
460                 .error = EPERM_SETSOCKOPT,
461         },
462         {
463                 .descr = "setsockopt: no optval bounds check, deny loading",
464                 .insns = {
465                         /* r6 = ctx->optval */
466                         BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
467                                     offsetof(struct bpf_sockopt, optval)),
468
469                         /* r0 = ctx->optval[0] */
470                         BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
471
472                         /* return 1 */
473                         BPF_MOV64_IMM(BPF_REG_0, 1),
474                         BPF_EXIT_INSN(),
475                 },
476                 .attach_type = BPF_CGROUP_SETSOCKOPT,
477                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
478                 .error = DENY_LOAD,
479         },
480         {
481                 .descr = "setsockopt: read ctx->level",
482                 .insns = {
483                         /* r6 = ctx->level */
484                         BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
485                                     offsetof(struct bpf_sockopt, level)),
486
487                         /* if (ctx->level == 123) { */
488                         BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
489                         /* ctx->optlen = -1 */
490                         BPF_MOV64_IMM(BPF_REG_0, -1),
491                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
492                                     offsetof(struct bpf_sockopt, optlen)),
493                         /* return 1 */
494                         BPF_MOV64_IMM(BPF_REG_0, 1),
495                         BPF_JMP_A(1),
496                         /* } else { */
497                         /* return 0 */
498                         BPF_MOV64_IMM(BPF_REG_0, 0),
499                         /* } */
500                         BPF_EXIT_INSN(),
501                 },
502                 .attach_type = BPF_CGROUP_SETSOCKOPT,
503                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
504
505                 .set_level = 123,
506
507                 .set_optlen = 1,
508         },
509         {
510                 .descr = "setsockopt: allow changing ctx->level",
511                 .insns = {
512                         /* ctx->level = SOL_IP */
513                         BPF_MOV64_IMM(BPF_REG_0, SOL_IP),
514                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
515                                     offsetof(struct bpf_sockopt, level)),
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
523                 .get_level = SOL_IP,
524                 .set_level = 234, /* should be rewritten to SOL_IP */
525
526                 .get_optname = IP_TOS,
527                 .set_optname = IP_TOS,
528
529                 .set_optval = { 1 << 3 },
530                 .set_optlen = 1,
531                 .get_optval = { 1 << 3 },
532                 .get_optlen = 1,
533         },
534         {
535                 .descr = "setsockopt: read ctx->optname",
536                 .insns = {
537                         /* r6 = ctx->optname */
538                         BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
539                                     offsetof(struct bpf_sockopt, optname)),
540
541                         /* if (ctx->optname == 123) { */
542                         BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
543                         /* ctx->optlen = -1 */
544                         BPF_MOV64_IMM(BPF_REG_0, -1),
545                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
546                                     offsetof(struct bpf_sockopt, optlen)),
547                         /* return 1 */
548                         BPF_MOV64_IMM(BPF_REG_0, 1),
549                         BPF_JMP_A(1),
550                         /* } else { */
551                         /* return 0 */
552                         BPF_MOV64_IMM(BPF_REG_0, 0),
553                         /* } */
554                         BPF_EXIT_INSN(),
555                 },
556                 .attach_type = BPF_CGROUP_SETSOCKOPT,
557                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
558
559                 .set_optname = 123,
560
561                 .set_optlen = 1,
562         },
563         {
564                 .descr = "setsockopt: allow changing ctx->optname",
565                 .insns = {
566                         /* ctx->optname = IP_TOS */
567                         BPF_MOV64_IMM(BPF_REG_0, IP_TOS),
568                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
569                                     offsetof(struct bpf_sockopt, optname)),
570                         /* return 1 */
571                         BPF_MOV64_IMM(BPF_REG_0, 1),
572                         BPF_EXIT_INSN(),
573                 },
574                 .attach_type = BPF_CGROUP_SETSOCKOPT,
575                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
576
577                 .get_level = SOL_IP,
578                 .set_level = SOL_IP,
579
580                 .get_optname = IP_TOS,
581                 .set_optname = 456, /* should be rewritten to IP_TOS */
582
583                 .set_optval = { 1 << 3 },
584                 .set_optlen = 1,
585                 .get_optval = { 1 << 3 },
586                 .get_optlen = 1,
587         },
588         {
589                 .descr = "setsockopt: read ctx->optlen",
590                 .insns = {
591                         /* r6 = ctx->optlen */
592                         BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
593                                     offsetof(struct bpf_sockopt, optlen)),
594
595                         /* if (ctx->optlen == 64) { */
596                         BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
597                         /* ctx->optlen = -1 */
598                         BPF_MOV64_IMM(BPF_REG_0, -1),
599                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
600                                     offsetof(struct bpf_sockopt, optlen)),
601                         /* return 1 */
602                         BPF_MOV64_IMM(BPF_REG_0, 1),
603                         BPF_JMP_A(1),
604                         /* } else { */
605                         /* return 0 */
606                         BPF_MOV64_IMM(BPF_REG_0, 0),
607                         /* } */
608                         BPF_EXIT_INSN(),
609                 },
610                 .attach_type = BPF_CGROUP_SETSOCKOPT,
611                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
612
613                 .set_optlen = 64,
614         },
615         {
616                 .descr = "setsockopt: ctx->optlen == -1 is ok",
617                 .insns = {
618                         /* ctx->optlen = -1 */
619                         BPF_MOV64_IMM(BPF_REG_0, -1),
620                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
621                                     offsetof(struct bpf_sockopt, optlen)),
622                         /* return 1 */
623                         BPF_MOV64_IMM(BPF_REG_0, 1),
624                         BPF_EXIT_INSN(),
625                 },
626                 .attach_type = BPF_CGROUP_SETSOCKOPT,
627                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
628
629                 .set_optlen = 64,
630         },
631         {
632                 .descr = "setsockopt: deny ctx->optlen < 0 (except -1)",
633                 .insns = {
634                         /* ctx->optlen = -2 */
635                         BPF_MOV64_IMM(BPF_REG_0, -2),
636                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
637                                     offsetof(struct bpf_sockopt, optlen)),
638                         /* return 1 */
639                         BPF_MOV64_IMM(BPF_REG_0, 1),
640                         BPF_EXIT_INSN(),
641                 },
642                 .attach_type = BPF_CGROUP_SETSOCKOPT,
643                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
644
645                 .set_optlen = 4,
646
647                 .error = EFAULT_SETSOCKOPT,
648         },
649         {
650                 .descr = "setsockopt: deny ctx->optlen > input optlen",
651                 .insns = {
652                         /* ctx->optlen = 65 */
653                         BPF_MOV64_IMM(BPF_REG_0, 65),
654                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
655                                     offsetof(struct bpf_sockopt, optlen)),
656                         BPF_MOV64_IMM(BPF_REG_0, 1),
657                         BPF_EXIT_INSN(),
658                 },
659                 .attach_type = BPF_CGROUP_SETSOCKOPT,
660                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
661
662                 .set_optlen = 64,
663
664                 .error = EFAULT_SETSOCKOPT,
665         },
666         {
667                 .descr = "setsockopt: allow changing ctx->optlen within bounds",
668                 .insns = {
669                         /* r6 = ctx->optval */
670                         BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
671                                     offsetof(struct bpf_sockopt, optval)),
672                         /* r2 = ctx->optval */
673                         BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
674                         /* r6 = ctx->optval + 1 */
675                         BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
676
677                         /* r7 = ctx->optval_end */
678                         BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
679                                     offsetof(struct bpf_sockopt, optval_end)),
680
681                         /* if (ctx->optval + 1 <= ctx->optval_end) { */
682                         BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
683                         /* ctx->optval[0] = 1 << 3 */
684                         BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 1 << 3),
685                         /* } */
686
687                         /* ctx->optlen = 1 */
688                         BPF_MOV64_IMM(BPF_REG_0, 1),
689                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
690                                     offsetof(struct bpf_sockopt, optlen)),
691
692                         /* return 1*/
693                         BPF_MOV64_IMM(BPF_REG_0, 1),
694                         BPF_EXIT_INSN(),
695                 },
696                 .attach_type = BPF_CGROUP_SETSOCKOPT,
697                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
698
699                 .get_level = SOL_IP,
700                 .set_level = SOL_IP,
701
702                 .get_optname = IP_TOS,
703                 .set_optname = IP_TOS,
704
705                 .set_optval = { 1, 1, 1, 1 },
706                 .set_optlen = 4,
707                 .get_optval = { 1 << 3 },
708                 .get_optlen = 1,
709         },
710         {
711                 .descr = "setsockopt: deny write ctx->retval",
712                 .insns = {
713                         /* ctx->retval = 0 */
714                         BPF_MOV64_IMM(BPF_REG_0, 0),
715                         BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
716                                     offsetof(struct bpf_sockopt, retval)),
717
718                         /* return 1 */
719                         BPF_MOV64_IMM(BPF_REG_0, 1),
720                         BPF_EXIT_INSN(),
721                 },
722                 .attach_type = BPF_CGROUP_SETSOCKOPT,
723                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
724
725                 .error = DENY_LOAD,
726         },
727         {
728                 .descr = "setsockopt: deny read ctx->retval",
729                 .insns = {
730                         /* r6 = ctx->retval */
731                         BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
732                                     offsetof(struct bpf_sockopt, retval)),
733
734                         /* return 1 */
735                         BPF_MOV64_IMM(BPF_REG_0, 1),
736                         BPF_EXIT_INSN(),
737                 },
738                 .attach_type = BPF_CGROUP_SETSOCKOPT,
739                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
740
741                 .error = DENY_LOAD,
742         },
743         {
744                 .descr = "setsockopt: deny writing to ctx->optval",
745                 .insns = {
746                         /* ctx->optval = 1 */
747                         BPF_MOV64_IMM(BPF_REG_0, 1),
748                         BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
749                                     offsetof(struct bpf_sockopt, optval)),
750                         BPF_EXIT_INSN(),
751                 },
752                 .attach_type = BPF_CGROUP_SETSOCKOPT,
753                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
754
755                 .error = DENY_LOAD,
756         },
757         {
758                 .descr = "setsockopt: deny writing to ctx->optval_end",
759                 .insns = {
760                         /* ctx->optval_end = 1 */
761                         BPF_MOV64_IMM(BPF_REG_0, 1),
762                         BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
763                                     offsetof(struct bpf_sockopt, optval_end)),
764                         BPF_EXIT_INSN(),
765                 },
766                 .attach_type = BPF_CGROUP_SETSOCKOPT,
767                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
768
769                 .error = DENY_LOAD,
770         },
771         {
772                 .descr = "setsockopt: allow IP_TOS <= 128",
773                 .insns = {
774                         /* r6 = ctx->optval */
775                         BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
776                                     offsetof(struct bpf_sockopt, optval)),
777                         /* r7 = ctx->optval + 1 */
778                         BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
779                         BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
780
781                         /* r8 = ctx->optval_end */
782                         BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
783                                     offsetof(struct bpf_sockopt, optval_end)),
784
785                         /* if (ctx->optval + 1 <= ctx->optval_end) { */
786                         BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
787
788                         /* r9 = ctx->optval[0] */
789                         BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
790
791                         /* if (ctx->optval[0] < 128) */
792                         BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
793                         BPF_MOV64_IMM(BPF_REG_0, 1),
794                         BPF_JMP_A(1),
795                         /* } */
796
797                         /* } else { */
798                         BPF_MOV64_IMM(BPF_REG_0, 0),
799                         /* } */
800
801                         BPF_EXIT_INSN(),
802                 },
803                 .attach_type = BPF_CGROUP_SETSOCKOPT,
804                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
805
806                 .get_level = SOL_IP,
807                 .set_level = SOL_IP,
808
809                 .get_optname = IP_TOS,
810                 .set_optname = IP_TOS,
811
812                 .set_optval = { 0x80 },
813                 .set_optlen = 1,
814                 .get_optval = { 0x80 },
815                 .get_optlen = 1,
816         },
817         {
818                 .descr = "setsockopt: deny IP_TOS > 128",
819                 .insns = {
820                         /* r6 = ctx->optval */
821                         BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
822                                     offsetof(struct bpf_sockopt, optval)),
823                         /* r7 = ctx->optval + 1 */
824                         BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
825                         BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
826
827                         /* r8 = ctx->optval_end */
828                         BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
829                                     offsetof(struct bpf_sockopt, optval_end)),
830
831                         /* if (ctx->optval + 1 <= ctx->optval_end) { */
832                         BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
833
834                         /* r9 = ctx->optval[0] */
835                         BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
836
837                         /* if (ctx->optval[0] < 128) */
838                         BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
839                         BPF_MOV64_IMM(BPF_REG_0, 1),
840                         BPF_JMP_A(1),
841                         /* } */
842
843                         /* } else { */
844                         BPF_MOV64_IMM(BPF_REG_0, 0),
845                         /* } */
846
847                         BPF_EXIT_INSN(),
848                 },
849                 .attach_type = BPF_CGROUP_SETSOCKOPT,
850                 .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
851
852                 .get_level = SOL_IP,
853                 .set_level = SOL_IP,
854
855                 .get_optname = IP_TOS,
856                 .set_optname = IP_TOS,
857
858                 .set_optval = { 0x81 },
859                 .set_optlen = 1,
860                 .get_optval = { 0x00 },
861                 .get_optlen = 1,
862
863                 .error = EPERM_SETSOCKOPT,
864         },
865 };
866
867 static int load_prog(const struct bpf_insn *insns,
868                      enum bpf_attach_type expected_attach_type)
869 {
870         struct bpf_load_program_attr attr = {
871                 .prog_type = BPF_PROG_TYPE_CGROUP_SOCKOPT,
872                 .expected_attach_type = expected_attach_type,
873                 .insns = insns,
874                 .license = "GPL",
875                 .log_level = 2,
876         };
877         int fd;
878
879         for (;
880              insns[attr.insns_cnt].code != (BPF_JMP | BPF_EXIT);
881              attr.insns_cnt++) {
882         }
883         attr.insns_cnt++;
884
885         fd = bpf_load_program_xattr(&attr, bpf_log_buf, sizeof(bpf_log_buf));
886         if (verbose && fd < 0)
887                 fprintf(stderr, "%s\n", bpf_log_buf);
888
889         return fd;
890 }
891
892 static int run_test(int cgroup_fd, struct sockopt_test *test)
893 {
894         int sock_fd, err, prog_fd;
895         void *optval = NULL;
896         int ret = 0;
897
898         prog_fd = load_prog(test->insns, test->expected_attach_type);
899         if (prog_fd < 0) {
900                 if (test->error == DENY_LOAD)
901                         return 0;
902
903                 log_err("Failed to load BPF program");
904                 return -1;
905         }
906
907         err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0);
908         if (err < 0) {
909                 if (test->error == DENY_ATTACH)
910                         goto close_prog_fd;
911
912                 log_err("Failed to attach BPF program");
913                 ret = -1;
914                 goto close_prog_fd;
915         }
916
917         sock_fd = socket(AF_INET, SOCK_STREAM, 0);
918         if (sock_fd < 0) {
919                 log_err("Failed to create AF_INET socket");
920                 ret = -1;
921                 goto detach_prog;
922         }
923
924         if (test->set_optlen) {
925                 err = setsockopt(sock_fd, test->set_level, test->set_optname,
926                                  test->set_optval, test->set_optlen);
927                 if (err) {
928                         if (errno == EPERM && test->error == EPERM_SETSOCKOPT)
929                                 goto close_sock_fd;
930                         if (errno == EFAULT && test->error == EFAULT_SETSOCKOPT)
931                                 goto free_optval;
932
933                         log_err("Failed to call setsockopt");
934                         ret = -1;
935                         goto close_sock_fd;
936                 }
937         }
938
939         if (test->get_optlen) {
940                 optval = malloc(test->get_optlen);
941                 socklen_t optlen = test->get_optlen;
942                 socklen_t expected_get_optlen = test->get_optlen_ret ?:
943                         test->get_optlen;
944
945                 err = getsockopt(sock_fd, test->get_level, test->get_optname,
946                                  optval, &optlen);
947                 if (err) {
948                         if (errno == EPERM && test->error == EPERM_GETSOCKOPT)
949                                 goto free_optval;
950                         if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT)
951                                 goto free_optval;
952
953                         log_err("Failed to call getsockopt");
954                         ret = -1;
955                         goto free_optval;
956                 }
957
958                 if (optlen != expected_get_optlen) {
959                         errno = 0;
960                         log_err("getsockopt returned unexpected optlen");
961                         ret = -1;
962                         goto free_optval;
963                 }
964
965                 if (memcmp(optval, test->get_optval, optlen) != 0) {
966                         errno = 0;
967                         log_err("getsockopt returned unexpected optval");
968                         ret = -1;
969                         goto free_optval;
970                 }
971         }
972
973         ret = test->error != OK;
974
975 free_optval:
976         free(optval);
977 close_sock_fd:
978         close(sock_fd);
979 detach_prog:
980         bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);
981 close_prog_fd:
982         close(prog_fd);
983         return ret;
984 }
985
986 int main(int args, char **argv)
987 {
988         int err = EXIT_FAILURE, error_cnt = 0;
989         int cgroup_fd, i;
990
991         if (setup_cgroup_environment())
992                 goto cleanup_obj;
993
994         cgroup_fd = create_and_get_cgroup(CG_PATH);
995         if (cgroup_fd < 0)
996                 goto cleanup_cgroup_env;
997
998         if (join_cgroup(CG_PATH))
999                 goto cleanup_cgroup;
1000
1001         for (i = 0; i < ARRAY_SIZE(tests); i++) {
1002                 int err = run_test(cgroup_fd, &tests[i]);
1003
1004                 if (err)
1005                         error_cnt++;
1006
1007                 printf("#%d %s: %s\n", i, err ? "FAIL" : "PASS",
1008                        tests[i].descr);
1009         }
1010
1011         printf("Summary: %ld PASSED, %d FAILED\n",
1012                ARRAY_SIZE(tests) - error_cnt, error_cnt);
1013         err = error_cnt ? EXIT_FAILURE : EXIT_SUCCESS;
1014
1015 cleanup_cgroup:
1016         close(cgroup_fd);
1017 cleanup_cgroup_env:
1018         cleanup_cgroup_environment();
1019 cleanup_obj:
1020         return err;
1021 }