Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
   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}