cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sockopt.c (23447B)


      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	LIBBPF_OPTS(bpf_prog_load_opts, opts,
    856		.expected_attach_type = expected_attach_type,
    857		.log_level = 2,
    858		.log_buf = bpf_log_buf,
    859		.log_size = sizeof(bpf_log_buf),
    860	);
    861	int fd, insns_cnt = 0;
    862
    863	for (;
    864	     insns[insns_cnt].code != (BPF_JMP | BPF_EXIT);
    865	     insns_cnt++) {
    866	}
    867	insns_cnt++;
    868
    869	fd = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCKOPT, NULL, "GPL", insns, insns_cnt, &opts);
    870	if (verbose && fd < 0)
    871		fprintf(stderr, "%s\n", bpf_log_buf);
    872
    873	return fd;
    874}
    875
    876static int run_test(int cgroup_fd, struct sockopt_test *test)
    877{
    878	int sock_fd, err, prog_fd;
    879	void *optval = NULL;
    880	int ret = 0;
    881
    882	prog_fd = load_prog(test->insns, test->expected_attach_type);
    883	if (prog_fd < 0) {
    884		if (test->error == DENY_LOAD)
    885			return 0;
    886
    887		log_err("Failed to load BPF program");
    888		return -1;
    889	}
    890
    891	err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0);
    892	if (err < 0) {
    893		if (test->error == DENY_ATTACH)
    894			goto close_prog_fd;
    895
    896		log_err("Failed to attach BPF program");
    897		ret = -1;
    898		goto close_prog_fd;
    899	}
    900
    901	sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    902	if (sock_fd < 0) {
    903		log_err("Failed to create AF_INET socket");
    904		ret = -1;
    905		goto detach_prog;
    906	}
    907
    908	if (test->set_optlen) {
    909		err = setsockopt(sock_fd, test->set_level, test->set_optname,
    910				 test->set_optval, test->set_optlen);
    911		if (err) {
    912			if (errno == EPERM && test->error == EPERM_SETSOCKOPT)
    913				goto close_sock_fd;
    914			if (errno == EFAULT && test->error == EFAULT_SETSOCKOPT)
    915				goto free_optval;
    916
    917			log_err("Failed to call setsockopt");
    918			ret = -1;
    919			goto close_sock_fd;
    920		}
    921	}
    922
    923	if (test->get_optlen) {
    924		optval = malloc(test->get_optlen);
    925		socklen_t optlen = test->get_optlen;
    926		socklen_t expected_get_optlen = test->get_optlen_ret ?:
    927			test->get_optlen;
    928
    929		err = getsockopt(sock_fd, test->get_level, test->get_optname,
    930				 optval, &optlen);
    931		if (err) {
    932			if (errno == EPERM && test->error == EPERM_GETSOCKOPT)
    933				goto free_optval;
    934			if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT)
    935				goto free_optval;
    936
    937			log_err("Failed to call getsockopt");
    938			ret = -1;
    939			goto free_optval;
    940		}
    941
    942		if (optlen != expected_get_optlen) {
    943			errno = 0;
    944			log_err("getsockopt returned unexpected optlen");
    945			ret = -1;
    946			goto free_optval;
    947		}
    948
    949		if (memcmp(optval, test->get_optval, optlen) != 0) {
    950			errno = 0;
    951			log_err("getsockopt returned unexpected optval");
    952			ret = -1;
    953			goto free_optval;
    954		}
    955	}
    956
    957	ret = test->error != OK;
    958
    959free_optval:
    960	free(optval);
    961close_sock_fd:
    962	close(sock_fd);
    963detach_prog:
    964	bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type);
    965close_prog_fd:
    966	close(prog_fd);
    967	return ret;
    968}
    969
    970void test_sockopt(void)
    971{
    972	int cgroup_fd, i;
    973
    974	cgroup_fd = test__join_cgroup("/sockopt");
    975	if (CHECK_FAIL(cgroup_fd < 0))
    976		return;
    977
    978	for (i = 0; i < ARRAY_SIZE(tests); i++) {
    979		test__start_subtest(tests[i].descr);
    980		CHECK_FAIL(run_test(cgroup_fd, &tests[i]));
    981	}
    982
    983	close(cgroup_fd);
    984}