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