linux/tools/testing/selftests/bpf/prog_tests/sockopt.c
<<
>>
Prefs
   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}
 986