Linux Audio

Check our new training course

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