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