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