Linux Audio

Check our new training course

Loading...
v5.4
  1// SPDX-License-Identifier: GPL-2.0
  2#include <test_progs.h>
  3#include "cgroup_helpers.h"
  4
  5static char bpf_log_buf[4096];
  6static bool verbose;
  7
  8enum 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
 18static 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
852static 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
877static 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
960free_optval:
961	free(optval);
962close_sock_fd:
963	close(sock_fd);
964detach_prog:
965	bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);
966close_prog_fd:
967	close(prog_fd);
968	return ret;
969}
970
971void 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}
v5.9
  1// SPDX-License-Identifier: GPL-2.0
  2#include <test_progs.h>
  3#include "cgroup_helpers.h"
  4
  5static char bpf_log_buf[4096];
  6static bool verbose;
  7
  8enum 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
 18static 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
852static 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
877static 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
960free_optval:
961	free(optval);
962close_sock_fd:
963	close(sock_fd);
964detach_prog:
965	bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);
966close_prog_fd:
967	close(prog_fd);
968	return ret;
969}
970
971void 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}