Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <test_progs.h>
0003 #include "cgroup_helpers.h"
0004 
0005 static char bpf_log_buf[4096];
0006 static bool verbose;
0007 
0008 enum sockopt_test_error {
0009     OK = 0,
0010     DENY_LOAD,
0011     DENY_ATTACH,
0012     EPERM_GETSOCKOPT,
0013     EFAULT_GETSOCKOPT,
0014     EPERM_SETSOCKOPT,
0015     EFAULT_SETSOCKOPT,
0016 };
0017 
0018 static struct sockopt_test {
0019     const char          *descr;
0020     const struct bpf_insn       insns[64];
0021     enum bpf_attach_type        attach_type;
0022     enum bpf_attach_type        expected_attach_type;
0023 
0024     int             set_optname;
0025     int             set_level;
0026     const char          set_optval[64];
0027     socklen_t           set_optlen;
0028 
0029     int             get_optname;
0030     int             get_level;
0031     const char          get_optval[64];
0032     socklen_t           get_optlen;
0033     socklen_t           get_optlen_ret;
0034 
0035     enum sockopt_test_error     error;
0036 } tests[] = {
0037 
0038     /* ==================== getsockopt ====================  */
0039 
0040     {
0041         .descr = "getsockopt: no expected_attach_type",
0042         .insns = {
0043             /* return 1 */
0044             BPF_MOV64_IMM(BPF_REG_0, 1),
0045             BPF_EXIT_INSN(),
0046 
0047         },
0048         .attach_type = BPF_CGROUP_GETSOCKOPT,
0049         .expected_attach_type = 0,
0050         .error = DENY_LOAD,
0051     },
0052     {
0053         .descr = "getsockopt: wrong expected_attach_type",
0054         .insns = {
0055             /* return 1 */
0056             BPF_MOV64_IMM(BPF_REG_0, 1),
0057             BPF_EXIT_INSN(),
0058 
0059         },
0060         .attach_type = BPF_CGROUP_GETSOCKOPT,
0061         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0062         .error = DENY_ATTACH,
0063     },
0064     {
0065         .descr = "getsockopt: bypass bpf hook",
0066         .insns = {
0067             /* return 1 */
0068             BPF_MOV64_IMM(BPF_REG_0, 1),
0069             BPF_EXIT_INSN(),
0070         },
0071         .attach_type = BPF_CGROUP_GETSOCKOPT,
0072         .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
0073 
0074         .get_level = SOL_IP,
0075         .set_level = SOL_IP,
0076 
0077         .get_optname = IP_TOS,
0078         .set_optname = IP_TOS,
0079 
0080         .set_optval = { 1 << 3 },
0081         .set_optlen = 1,
0082 
0083         .get_optval = { 1 << 3 },
0084         .get_optlen = 1,
0085     },
0086     {
0087         .descr = "getsockopt: return EPERM from bpf hook",
0088         .insns = {
0089             BPF_MOV64_IMM(BPF_REG_0, 0),
0090             BPF_EXIT_INSN(),
0091         },
0092         .attach_type = BPF_CGROUP_GETSOCKOPT,
0093         .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
0094 
0095         .get_level = SOL_IP,
0096         .get_optname = IP_TOS,
0097 
0098         .get_optlen = 1,
0099         .error = EPERM_GETSOCKOPT,
0100     },
0101     {
0102         .descr = "getsockopt: no optval bounds check, deny loading",
0103         .insns = {
0104             /* r6 = ctx->optval */
0105             BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
0106                     offsetof(struct bpf_sockopt, optval)),
0107 
0108             /* ctx->optval[0] = 0x80 */
0109             BPF_MOV64_IMM(BPF_REG_0, 0x80),
0110             BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0),
0111 
0112             /* return 1 */
0113             BPF_MOV64_IMM(BPF_REG_0, 1),
0114             BPF_EXIT_INSN(),
0115         },
0116         .attach_type = BPF_CGROUP_GETSOCKOPT,
0117         .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
0118         .error = DENY_LOAD,
0119     },
0120     {
0121         .descr = "getsockopt: read ctx->level",
0122         .insns = {
0123             /* r6 = ctx->level */
0124             BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
0125                     offsetof(struct bpf_sockopt, level)),
0126 
0127             /* if (ctx->level == 123) { */
0128             BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
0129             /* ctx->retval = 0 */
0130             BPF_MOV64_IMM(BPF_REG_0, 0),
0131             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0132                     offsetof(struct bpf_sockopt, retval)),
0133             /* return 1 */
0134             BPF_MOV64_IMM(BPF_REG_0, 1),
0135             BPF_JMP_A(1),
0136             /* } else { */
0137             /* return 0 */
0138             BPF_MOV64_IMM(BPF_REG_0, 0),
0139             /* } */
0140             BPF_EXIT_INSN(),
0141         },
0142         .attach_type = BPF_CGROUP_GETSOCKOPT,
0143         .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
0144 
0145         .get_level = 123,
0146 
0147         .get_optlen = 1,
0148     },
0149     {
0150         .descr = "getsockopt: deny writing to ctx->level",
0151         .insns = {
0152             /* ctx->level = 1 */
0153             BPF_MOV64_IMM(BPF_REG_0, 1),
0154             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0155                     offsetof(struct bpf_sockopt, level)),
0156             BPF_EXIT_INSN(),
0157         },
0158         .attach_type = BPF_CGROUP_GETSOCKOPT,
0159         .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
0160 
0161         .error = DENY_LOAD,
0162     },
0163     {
0164         .descr = "getsockopt: read ctx->optname",
0165         .insns = {
0166             /* r6 = ctx->optname */
0167             BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
0168                     offsetof(struct bpf_sockopt, optname)),
0169 
0170             /* if (ctx->optname == 123) { */
0171             BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
0172             /* ctx->retval = 0 */
0173             BPF_MOV64_IMM(BPF_REG_0, 0),
0174             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0175                     offsetof(struct bpf_sockopt, retval)),
0176             /* return 1 */
0177             BPF_MOV64_IMM(BPF_REG_0, 1),
0178             BPF_JMP_A(1),
0179             /* } else { */
0180             /* return 0 */
0181             BPF_MOV64_IMM(BPF_REG_0, 0),
0182             /* } */
0183             BPF_EXIT_INSN(),
0184         },
0185         .attach_type = BPF_CGROUP_GETSOCKOPT,
0186         .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
0187 
0188         .get_optname = 123,
0189 
0190         .get_optlen = 1,
0191     },
0192     {
0193         .descr = "getsockopt: read ctx->retval",
0194         .insns = {
0195             /* r6 = ctx->retval */
0196             BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
0197                     offsetof(struct bpf_sockopt, retval)),
0198 
0199             /* return 1 */
0200             BPF_MOV64_IMM(BPF_REG_0, 1),
0201             BPF_EXIT_INSN(),
0202         },
0203         .attach_type = BPF_CGROUP_GETSOCKOPT,
0204         .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
0205 
0206         .get_level = SOL_IP,
0207         .get_optname = IP_TOS,
0208         .get_optlen = 1,
0209     },
0210     {
0211         .descr = "getsockopt: deny writing to ctx->optname",
0212         .insns = {
0213             /* ctx->optname = 1 */
0214             BPF_MOV64_IMM(BPF_REG_0, 1),
0215             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0216                     offsetof(struct bpf_sockopt, optname)),
0217             BPF_EXIT_INSN(),
0218         },
0219         .attach_type = BPF_CGROUP_GETSOCKOPT,
0220         .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
0221 
0222         .error = DENY_LOAD,
0223     },
0224     {
0225         .descr = "getsockopt: read ctx->optlen",
0226         .insns = {
0227             /* r6 = ctx->optlen */
0228             BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
0229                     offsetof(struct bpf_sockopt, optlen)),
0230 
0231             /* if (ctx->optlen == 64) { */
0232             BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
0233             /* ctx->retval = 0 */
0234             BPF_MOV64_IMM(BPF_REG_0, 0),
0235             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0236                     offsetof(struct bpf_sockopt, retval)),
0237             /* return 1 */
0238             BPF_MOV64_IMM(BPF_REG_0, 1),
0239             BPF_JMP_A(1),
0240             /* } else { */
0241             /* return 0 */
0242             BPF_MOV64_IMM(BPF_REG_0, 0),
0243             /* } */
0244             BPF_EXIT_INSN(),
0245         },
0246         .attach_type = BPF_CGROUP_GETSOCKOPT,
0247         .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
0248 
0249         .get_optlen = 64,
0250     },
0251     {
0252         .descr = "getsockopt: deny bigger ctx->optlen",
0253         .insns = {
0254             /* ctx->optlen = 65 */
0255             BPF_MOV64_IMM(BPF_REG_0, 65),
0256             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0257                     offsetof(struct bpf_sockopt, optlen)),
0258 
0259             /* ctx->retval = 0 */
0260             BPF_MOV64_IMM(BPF_REG_0, 0),
0261             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0262                     offsetof(struct bpf_sockopt, retval)),
0263 
0264             /* return 1 */
0265             BPF_MOV64_IMM(BPF_REG_0, 1),
0266             BPF_EXIT_INSN(),
0267         },
0268         .attach_type = BPF_CGROUP_GETSOCKOPT,
0269         .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
0270 
0271         .get_optlen = 64,
0272 
0273         .error = EFAULT_GETSOCKOPT,
0274     },
0275     {
0276         .descr = "getsockopt: deny arbitrary ctx->retval",
0277         .insns = {
0278             /* ctx->retval = 123 */
0279             BPF_MOV64_IMM(BPF_REG_0, 123),
0280             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0281                     offsetof(struct bpf_sockopt, retval)),
0282 
0283             /* return 1 */
0284             BPF_MOV64_IMM(BPF_REG_0, 1),
0285             BPF_EXIT_INSN(),
0286         },
0287         .attach_type = BPF_CGROUP_GETSOCKOPT,
0288         .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
0289 
0290         .get_optlen = 64,
0291 
0292         .error = EFAULT_GETSOCKOPT,
0293     },
0294     {
0295         .descr = "getsockopt: support smaller ctx->optlen",
0296         .insns = {
0297             /* ctx->optlen = 32 */
0298             BPF_MOV64_IMM(BPF_REG_0, 32),
0299             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0300                     offsetof(struct bpf_sockopt, optlen)),
0301             /* ctx->retval = 0 */
0302             BPF_MOV64_IMM(BPF_REG_0, 0),
0303             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0304                     offsetof(struct bpf_sockopt, retval)),
0305             /* return 1 */
0306             BPF_MOV64_IMM(BPF_REG_0, 1),
0307             BPF_EXIT_INSN(),
0308         },
0309         .attach_type = BPF_CGROUP_GETSOCKOPT,
0310         .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
0311 
0312         .get_optlen = 64,
0313         .get_optlen_ret = 32,
0314     },
0315     {
0316         .descr = "getsockopt: deny writing to ctx->optval",
0317         .insns = {
0318             /* ctx->optval = 1 */
0319             BPF_MOV64_IMM(BPF_REG_0, 1),
0320             BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
0321                     offsetof(struct bpf_sockopt, optval)),
0322             BPF_EXIT_INSN(),
0323         },
0324         .attach_type = BPF_CGROUP_GETSOCKOPT,
0325         .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
0326 
0327         .error = DENY_LOAD,
0328     },
0329     {
0330         .descr = "getsockopt: deny writing to ctx->optval_end",
0331         .insns = {
0332             /* ctx->optval_end = 1 */
0333             BPF_MOV64_IMM(BPF_REG_0, 1),
0334             BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
0335                     offsetof(struct bpf_sockopt, optval_end)),
0336             BPF_EXIT_INSN(),
0337         },
0338         .attach_type = BPF_CGROUP_GETSOCKOPT,
0339         .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
0340 
0341         .error = DENY_LOAD,
0342     },
0343     {
0344         .descr = "getsockopt: rewrite value",
0345         .insns = {
0346             /* r6 = ctx->optval */
0347             BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
0348                     offsetof(struct bpf_sockopt, optval)),
0349             /* r2 = ctx->optval */
0350             BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
0351             /* r6 = ctx->optval + 1 */
0352             BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
0353 
0354             /* r7 = ctx->optval_end */
0355             BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
0356                     offsetof(struct bpf_sockopt, optval_end)),
0357 
0358             /* if (ctx->optval + 1 <= ctx->optval_end) { */
0359             BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
0360             /* ctx->optval[0] = 0xF0 */
0361             BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0),
0362             /* } */
0363 
0364             /* ctx->retval = 0 */
0365             BPF_MOV64_IMM(BPF_REG_0, 0),
0366             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0367                     offsetof(struct bpf_sockopt, retval)),
0368 
0369             /* return 1*/
0370             BPF_MOV64_IMM(BPF_REG_0, 1),
0371             BPF_EXIT_INSN(),
0372         },
0373         .attach_type = BPF_CGROUP_GETSOCKOPT,
0374         .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
0375 
0376         .get_level = SOL_IP,
0377         .get_optname = IP_TOS,
0378 
0379         .get_optval = { 0xF0 },
0380         .get_optlen = 1,
0381     },
0382 
0383     /* ==================== setsockopt ====================  */
0384 
0385     {
0386         .descr = "setsockopt: no expected_attach_type",
0387         .insns = {
0388             /* return 1 */
0389             BPF_MOV64_IMM(BPF_REG_0, 1),
0390             BPF_EXIT_INSN(),
0391 
0392         },
0393         .attach_type = BPF_CGROUP_SETSOCKOPT,
0394         .expected_attach_type = 0,
0395         .error = DENY_LOAD,
0396     },
0397     {
0398         .descr = "setsockopt: wrong expected_attach_type",
0399         .insns = {
0400             /* return 1 */
0401             BPF_MOV64_IMM(BPF_REG_0, 1),
0402             BPF_EXIT_INSN(),
0403 
0404         },
0405         .attach_type = BPF_CGROUP_SETSOCKOPT,
0406         .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
0407         .error = DENY_ATTACH,
0408     },
0409     {
0410         .descr = "setsockopt: bypass bpf hook",
0411         .insns = {
0412             /* return 1 */
0413             BPF_MOV64_IMM(BPF_REG_0, 1),
0414             BPF_EXIT_INSN(),
0415         },
0416         .attach_type = BPF_CGROUP_SETSOCKOPT,
0417         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0418 
0419         .get_level = SOL_IP,
0420         .set_level = SOL_IP,
0421 
0422         .get_optname = IP_TOS,
0423         .set_optname = IP_TOS,
0424 
0425         .set_optval = { 1 << 3 },
0426         .set_optlen = 1,
0427 
0428         .get_optval = { 1 << 3 },
0429         .get_optlen = 1,
0430     },
0431     {
0432         .descr = "setsockopt: return EPERM from bpf hook",
0433         .insns = {
0434             /* return 0 */
0435             BPF_MOV64_IMM(BPF_REG_0, 0),
0436             BPF_EXIT_INSN(),
0437         },
0438         .attach_type = BPF_CGROUP_SETSOCKOPT,
0439         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0440 
0441         .set_level = SOL_IP,
0442         .set_optname = IP_TOS,
0443 
0444         .set_optlen = 1,
0445         .error = EPERM_SETSOCKOPT,
0446     },
0447     {
0448         .descr = "setsockopt: no optval bounds check, deny loading",
0449         .insns = {
0450             /* r6 = ctx->optval */
0451             BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
0452                     offsetof(struct bpf_sockopt, optval)),
0453 
0454             /* r0 = ctx->optval[0] */
0455             BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0),
0456 
0457             /* return 1 */
0458             BPF_MOV64_IMM(BPF_REG_0, 1),
0459             BPF_EXIT_INSN(),
0460         },
0461         .attach_type = BPF_CGROUP_SETSOCKOPT,
0462         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0463         .error = DENY_LOAD,
0464     },
0465     {
0466         .descr = "setsockopt: read ctx->level",
0467         .insns = {
0468             /* r6 = ctx->level */
0469             BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
0470                     offsetof(struct bpf_sockopt, level)),
0471 
0472             /* if (ctx->level == 123) { */
0473             BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
0474             /* ctx->optlen = -1 */
0475             BPF_MOV64_IMM(BPF_REG_0, -1),
0476             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0477                     offsetof(struct bpf_sockopt, optlen)),
0478             /* return 1 */
0479             BPF_MOV64_IMM(BPF_REG_0, 1),
0480             BPF_JMP_A(1),
0481             /* } else { */
0482             /* return 0 */
0483             BPF_MOV64_IMM(BPF_REG_0, 0),
0484             /* } */
0485             BPF_EXIT_INSN(),
0486         },
0487         .attach_type = BPF_CGROUP_SETSOCKOPT,
0488         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0489 
0490         .set_level = 123,
0491 
0492         .set_optlen = 1,
0493     },
0494     {
0495         .descr = "setsockopt: allow changing ctx->level",
0496         .insns = {
0497             /* ctx->level = SOL_IP */
0498             BPF_MOV64_IMM(BPF_REG_0, SOL_IP),
0499             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0500                     offsetof(struct bpf_sockopt, level)),
0501             /* return 1 */
0502             BPF_MOV64_IMM(BPF_REG_0, 1),
0503             BPF_EXIT_INSN(),
0504         },
0505         .attach_type = BPF_CGROUP_SETSOCKOPT,
0506         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0507 
0508         .get_level = SOL_IP,
0509         .set_level = 234, /* should be rewritten to SOL_IP */
0510 
0511         .get_optname = IP_TOS,
0512         .set_optname = IP_TOS,
0513 
0514         .set_optval = { 1 << 3 },
0515         .set_optlen = 1,
0516         .get_optval = { 1 << 3 },
0517         .get_optlen = 1,
0518     },
0519     {
0520         .descr = "setsockopt: read ctx->optname",
0521         .insns = {
0522             /* r6 = ctx->optname */
0523             BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
0524                     offsetof(struct bpf_sockopt, optname)),
0525 
0526             /* if (ctx->optname == 123) { */
0527             BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4),
0528             /* ctx->optlen = -1 */
0529             BPF_MOV64_IMM(BPF_REG_0, -1),
0530             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0531                     offsetof(struct bpf_sockopt, optlen)),
0532             /* return 1 */
0533             BPF_MOV64_IMM(BPF_REG_0, 1),
0534             BPF_JMP_A(1),
0535             /* } else { */
0536             /* return 0 */
0537             BPF_MOV64_IMM(BPF_REG_0, 0),
0538             /* } */
0539             BPF_EXIT_INSN(),
0540         },
0541         .attach_type = BPF_CGROUP_SETSOCKOPT,
0542         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0543 
0544         .set_optname = 123,
0545 
0546         .set_optlen = 1,
0547     },
0548     {
0549         .descr = "setsockopt: allow changing ctx->optname",
0550         .insns = {
0551             /* ctx->optname = IP_TOS */
0552             BPF_MOV64_IMM(BPF_REG_0, IP_TOS),
0553             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0554                     offsetof(struct bpf_sockopt, optname)),
0555             /* return 1 */
0556             BPF_MOV64_IMM(BPF_REG_0, 1),
0557             BPF_EXIT_INSN(),
0558         },
0559         .attach_type = BPF_CGROUP_SETSOCKOPT,
0560         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0561 
0562         .get_level = SOL_IP,
0563         .set_level = SOL_IP,
0564 
0565         .get_optname = IP_TOS,
0566         .set_optname = 456, /* should be rewritten to IP_TOS */
0567 
0568         .set_optval = { 1 << 3 },
0569         .set_optlen = 1,
0570         .get_optval = { 1 << 3 },
0571         .get_optlen = 1,
0572     },
0573     {
0574         .descr = "setsockopt: read ctx->optlen",
0575         .insns = {
0576             /* r6 = ctx->optlen */
0577             BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
0578                     offsetof(struct bpf_sockopt, optlen)),
0579 
0580             /* if (ctx->optlen == 64) { */
0581             BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4),
0582             /* ctx->optlen = -1 */
0583             BPF_MOV64_IMM(BPF_REG_0, -1),
0584             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0585                     offsetof(struct bpf_sockopt, optlen)),
0586             /* return 1 */
0587             BPF_MOV64_IMM(BPF_REG_0, 1),
0588             BPF_JMP_A(1),
0589             /* } else { */
0590             /* return 0 */
0591             BPF_MOV64_IMM(BPF_REG_0, 0),
0592             /* } */
0593             BPF_EXIT_INSN(),
0594         },
0595         .attach_type = BPF_CGROUP_SETSOCKOPT,
0596         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0597 
0598         .set_optlen = 64,
0599     },
0600     {
0601         .descr = "setsockopt: ctx->optlen == -1 is ok",
0602         .insns = {
0603             /* ctx->optlen = -1 */
0604             BPF_MOV64_IMM(BPF_REG_0, -1),
0605             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0606                     offsetof(struct bpf_sockopt, optlen)),
0607             /* return 1 */
0608             BPF_MOV64_IMM(BPF_REG_0, 1),
0609             BPF_EXIT_INSN(),
0610         },
0611         .attach_type = BPF_CGROUP_SETSOCKOPT,
0612         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0613 
0614         .set_optlen = 64,
0615     },
0616     {
0617         .descr = "setsockopt: deny ctx->optlen < 0 (except -1)",
0618         .insns = {
0619             /* ctx->optlen = -2 */
0620             BPF_MOV64_IMM(BPF_REG_0, -2),
0621             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0622                     offsetof(struct bpf_sockopt, optlen)),
0623             /* return 1 */
0624             BPF_MOV64_IMM(BPF_REG_0, 1),
0625             BPF_EXIT_INSN(),
0626         },
0627         .attach_type = BPF_CGROUP_SETSOCKOPT,
0628         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0629 
0630         .set_optlen = 4,
0631 
0632         .error = EFAULT_SETSOCKOPT,
0633     },
0634     {
0635         .descr = "setsockopt: deny ctx->optlen > input optlen",
0636         .insns = {
0637             /* ctx->optlen = 65 */
0638             BPF_MOV64_IMM(BPF_REG_0, 65),
0639             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0640                     offsetof(struct bpf_sockopt, optlen)),
0641             BPF_MOV64_IMM(BPF_REG_0, 1),
0642             BPF_EXIT_INSN(),
0643         },
0644         .attach_type = BPF_CGROUP_SETSOCKOPT,
0645         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0646 
0647         .set_optlen = 64,
0648 
0649         .error = EFAULT_SETSOCKOPT,
0650     },
0651     {
0652         .descr = "setsockopt: allow changing ctx->optlen within bounds",
0653         .insns = {
0654             /* r6 = ctx->optval */
0655             BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
0656                     offsetof(struct bpf_sockopt, optval)),
0657             /* r2 = ctx->optval */
0658             BPF_MOV64_REG(BPF_REG_2, BPF_REG_6),
0659             /* r6 = ctx->optval + 1 */
0660             BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1),
0661 
0662             /* r7 = ctx->optval_end */
0663             BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1,
0664                     offsetof(struct bpf_sockopt, optval_end)),
0665 
0666             /* if (ctx->optval + 1 <= ctx->optval_end) { */
0667             BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1),
0668             /* ctx->optval[0] = 1 << 3 */
0669             BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 1 << 3),
0670             /* } */
0671 
0672             /* ctx->optlen = 1 */
0673             BPF_MOV64_IMM(BPF_REG_0, 1),
0674             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0675                     offsetof(struct bpf_sockopt, optlen)),
0676 
0677             /* return 1*/
0678             BPF_MOV64_IMM(BPF_REG_0, 1),
0679             BPF_EXIT_INSN(),
0680         },
0681         .attach_type = BPF_CGROUP_SETSOCKOPT,
0682         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0683 
0684         .get_level = SOL_IP,
0685         .set_level = SOL_IP,
0686 
0687         .get_optname = IP_TOS,
0688         .set_optname = IP_TOS,
0689 
0690         .set_optval = { 1, 1, 1, 1 },
0691         .set_optlen = 4,
0692         .get_optval = { 1 << 3 },
0693         .get_optlen = 1,
0694     },
0695     {
0696         .descr = "setsockopt: deny write ctx->retval",
0697         .insns = {
0698             /* ctx->retval = 0 */
0699             BPF_MOV64_IMM(BPF_REG_0, 0),
0700             BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
0701                     offsetof(struct bpf_sockopt, retval)),
0702 
0703             /* return 1 */
0704             BPF_MOV64_IMM(BPF_REG_0, 1),
0705             BPF_EXIT_INSN(),
0706         },
0707         .attach_type = BPF_CGROUP_SETSOCKOPT,
0708         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0709 
0710         .error = DENY_LOAD,
0711     },
0712     {
0713         .descr = "setsockopt: deny read ctx->retval",
0714         .insns = {
0715             /* r6 = ctx->retval */
0716             BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,
0717                     offsetof(struct bpf_sockopt, retval)),
0718 
0719             /* return 1 */
0720             BPF_MOV64_IMM(BPF_REG_0, 1),
0721             BPF_EXIT_INSN(),
0722         },
0723         .attach_type = BPF_CGROUP_SETSOCKOPT,
0724         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0725 
0726         .error = DENY_LOAD,
0727     },
0728     {
0729         .descr = "setsockopt: deny writing to ctx->optval",
0730         .insns = {
0731             /* ctx->optval = 1 */
0732             BPF_MOV64_IMM(BPF_REG_0, 1),
0733             BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
0734                     offsetof(struct bpf_sockopt, optval)),
0735             BPF_EXIT_INSN(),
0736         },
0737         .attach_type = BPF_CGROUP_SETSOCKOPT,
0738         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0739 
0740         .error = DENY_LOAD,
0741     },
0742     {
0743         .descr = "setsockopt: deny writing to ctx->optval_end",
0744         .insns = {
0745             /* ctx->optval_end = 1 */
0746             BPF_MOV64_IMM(BPF_REG_0, 1),
0747             BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0,
0748                     offsetof(struct bpf_sockopt, optval_end)),
0749             BPF_EXIT_INSN(),
0750         },
0751         .attach_type = BPF_CGROUP_SETSOCKOPT,
0752         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0753 
0754         .error = DENY_LOAD,
0755     },
0756     {
0757         .descr = "setsockopt: allow IP_TOS <= 128",
0758         .insns = {
0759             /* r6 = ctx->optval */
0760             BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
0761                     offsetof(struct bpf_sockopt, optval)),
0762             /* r7 = ctx->optval + 1 */
0763             BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
0764             BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
0765 
0766             /* r8 = ctx->optval_end */
0767             BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
0768                     offsetof(struct bpf_sockopt, optval_end)),
0769 
0770             /* if (ctx->optval + 1 <= ctx->optval_end) { */
0771             BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
0772 
0773             /* r9 = ctx->optval[0] */
0774             BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
0775 
0776             /* if (ctx->optval[0] < 128) */
0777             BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
0778             BPF_MOV64_IMM(BPF_REG_0, 1),
0779             BPF_JMP_A(1),
0780             /* } */
0781 
0782             /* } else { */
0783             BPF_MOV64_IMM(BPF_REG_0, 0),
0784             /* } */
0785 
0786             BPF_EXIT_INSN(),
0787         },
0788         .attach_type = BPF_CGROUP_SETSOCKOPT,
0789         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0790 
0791         .get_level = SOL_IP,
0792         .set_level = SOL_IP,
0793 
0794         .get_optname = IP_TOS,
0795         .set_optname = IP_TOS,
0796 
0797         .set_optval = { 0x80 },
0798         .set_optlen = 1,
0799         .get_optval = { 0x80 },
0800         .get_optlen = 1,
0801     },
0802     {
0803         .descr = "setsockopt: deny IP_TOS > 128",
0804         .insns = {
0805             /* r6 = ctx->optval */
0806             BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1,
0807                     offsetof(struct bpf_sockopt, optval)),
0808             /* r7 = ctx->optval + 1 */
0809             BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
0810             BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1),
0811 
0812             /* r8 = ctx->optval_end */
0813             BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1,
0814                     offsetof(struct bpf_sockopt, optval_end)),
0815 
0816             /* if (ctx->optval + 1 <= ctx->optval_end) { */
0817             BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4),
0818 
0819             /* r9 = ctx->optval[0] */
0820             BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0),
0821 
0822             /* if (ctx->optval[0] < 128) */
0823             BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2),
0824             BPF_MOV64_IMM(BPF_REG_0, 1),
0825             BPF_JMP_A(1),
0826             /* } */
0827 
0828             /* } else { */
0829             BPF_MOV64_IMM(BPF_REG_0, 0),
0830             /* } */
0831 
0832             BPF_EXIT_INSN(),
0833         },
0834         .attach_type = BPF_CGROUP_SETSOCKOPT,
0835         .expected_attach_type = BPF_CGROUP_SETSOCKOPT,
0836 
0837         .get_level = SOL_IP,
0838         .set_level = SOL_IP,
0839 
0840         .get_optname = IP_TOS,
0841         .set_optname = IP_TOS,
0842 
0843         .set_optval = { 0x81 },
0844         .set_optlen = 1,
0845         .get_optval = { 0x00 },
0846         .get_optlen = 1,
0847 
0848         .error = EPERM_SETSOCKOPT,
0849     },
0850 };
0851 
0852 static int load_prog(const struct bpf_insn *insns,
0853              enum bpf_attach_type expected_attach_type)
0854 {
0855     LIBBPF_OPTS(bpf_prog_load_opts, opts,
0856         .expected_attach_type = expected_attach_type,
0857         .log_level = 2,
0858         .log_buf = bpf_log_buf,
0859         .log_size = sizeof(bpf_log_buf),
0860     );
0861     int fd, insns_cnt = 0;
0862 
0863     for (;
0864          insns[insns_cnt].code != (BPF_JMP | BPF_EXIT);
0865          insns_cnt++) {
0866     }
0867     insns_cnt++;
0868 
0869     fd = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCKOPT, NULL, "GPL", insns, insns_cnt, &opts);
0870     if (verbose && fd < 0)
0871         fprintf(stderr, "%s\n", bpf_log_buf);
0872 
0873     return fd;
0874 }
0875 
0876 static int run_test(int cgroup_fd, struct sockopt_test *test)
0877 {
0878     int sock_fd, err, prog_fd;
0879     void *optval = NULL;
0880     int ret = 0;
0881 
0882     prog_fd = load_prog(test->insns, test->expected_attach_type);
0883     if (prog_fd < 0) {
0884         if (test->error == DENY_LOAD)
0885             return 0;
0886 
0887         log_err("Failed to load BPF program");
0888         return -1;
0889     }
0890 
0891     err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0);
0892     if (err < 0) {
0893         if (test->error == DENY_ATTACH)
0894             goto close_prog_fd;
0895 
0896         log_err("Failed to attach BPF program");
0897         ret = -1;
0898         goto close_prog_fd;
0899     }
0900 
0901     sock_fd = socket(AF_INET, SOCK_STREAM, 0);
0902     if (sock_fd < 0) {
0903         log_err("Failed to create AF_INET socket");
0904         ret = -1;
0905         goto detach_prog;
0906     }
0907 
0908     if (test->set_optlen) {
0909         err = setsockopt(sock_fd, test->set_level, test->set_optname,
0910                  test->set_optval, test->set_optlen);
0911         if (err) {
0912             if (errno == EPERM && test->error == EPERM_SETSOCKOPT)
0913                 goto close_sock_fd;
0914             if (errno == EFAULT && test->error == EFAULT_SETSOCKOPT)
0915                 goto free_optval;
0916 
0917             log_err("Failed to call setsockopt");
0918             ret = -1;
0919             goto close_sock_fd;
0920         }
0921     }
0922 
0923     if (test->get_optlen) {
0924         optval = malloc(test->get_optlen);
0925         socklen_t optlen = test->get_optlen;
0926         socklen_t expected_get_optlen = test->get_optlen_ret ?:
0927             test->get_optlen;
0928 
0929         err = getsockopt(sock_fd, test->get_level, test->get_optname,
0930                  optval, &optlen);
0931         if (err) {
0932             if (errno == EPERM && test->error == EPERM_GETSOCKOPT)
0933                 goto free_optval;
0934             if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT)
0935                 goto free_optval;
0936 
0937             log_err("Failed to call getsockopt");
0938             ret = -1;
0939             goto free_optval;
0940         }
0941 
0942         if (optlen != expected_get_optlen) {
0943             errno = 0;
0944             log_err("getsockopt returned unexpected optlen");
0945             ret = -1;
0946             goto free_optval;
0947         }
0948 
0949         if (memcmp(optval, test->get_optval, optlen) != 0) {
0950             errno = 0;
0951             log_err("getsockopt returned unexpected optval");
0952             ret = -1;
0953             goto free_optval;
0954         }
0955     }
0956 
0957     ret = test->error != OK;
0958 
0959 free_optval:
0960     free(optval);
0961 close_sock_fd:
0962     close(sock_fd);
0963 detach_prog:
0964     bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);
0965 close_prog_fd:
0966     close(prog_fd);
0967     return ret;
0968 }
0969 
0970 void test_sockopt(void)
0971 {
0972     int cgroup_fd, i;
0973 
0974     cgroup_fd = test__join_cgroup("/sockopt");
0975     if (CHECK_FAIL(cgroup_fd < 0))
0976         return;
0977 
0978     for (i = 0; i < ARRAY_SIZE(tests); i++) {
0979         test__start_subtest(tests[i].descr);
0980         CHECK_FAIL(run_test(cgroup_fd, &tests[i]));
0981     }
0982 
0983     close(cgroup_fd);
0984 }