From 4a64742168ce9e9972dab3f684629845c6e6dc67 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Wed, 4 Sep 2019 09:25:05 -0700 Subject: selftests/bpf: test_progs: convert test_sockopt Move the files, adjust includes, remove entry from Makefile & .gitignore Signed-off-by: Stanislav Fomichev Signed-off-by: Alexei Starovoitov --- tools/testing/selftests/bpf/.gitignore | 1 - tools/testing/selftests/bpf/Makefile | 3 +- tools/testing/selftests/bpf/prog_tests/sockopt.c | 985 +++++++++++++++++++++ tools/testing/selftests/bpf/test_sockopt.c | 1021 ---------------------- 4 files changed, 986 insertions(+), 1024 deletions(-) create mode 100644 tools/testing/selftests/bpf/prog_tests/sockopt.c delete mode 100644 tools/testing/selftests/bpf/test_sockopt.c (limited to 'tools/testing/selftests') diff --git a/tools/testing/selftests/bpf/.gitignore b/tools/testing/selftests/bpf/.gitignore index 60c9338cd9b4..0315120eac8f 100644 --- a/tools/testing/selftests/bpf/.gitignore +++ b/tools/testing/selftests/bpf/.gitignore @@ -39,7 +39,6 @@ libbpf.so.* test_hashmap test_btf_dump xdping -test_sockopt test_sockopt_sk test_sockopt_multi test_sockopt_inherit diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index ce9650bad453..7151cbc4676e 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -28,7 +28,7 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test test_sock test_btf test_sockmap get_cgroup_id_user test_socket_cookie \ test_cgroup_storage test_select_reuseport test_section_names \ test_netcnt test_tcpnotify_user test_sock_fields test_sysctl test_hashmap \ - test_btf_dump test_cgroup_attach xdping test_sockopt test_sockopt_sk \ + test_btf_dump test_cgroup_attach xdping test_sockopt_sk \ test_sockopt_multi test_sockopt_inherit test_tcp_rtt BPF_OBJ_FILES = $(patsubst %.c,%.o, $(notdir $(wildcard progs/*.c))) @@ -113,7 +113,6 @@ $(OUTPUT)/test_netcnt: cgroup_helpers.c $(OUTPUT)/test_sock_fields: cgroup_helpers.c $(OUTPUT)/test_sysctl: cgroup_helpers.c $(OUTPUT)/test_cgroup_attach: cgroup_helpers.c -$(OUTPUT)/test_sockopt: cgroup_helpers.c $(OUTPUT)/test_sockopt_sk: cgroup_helpers.c $(OUTPUT)/test_sockopt_multi: cgroup_helpers.c $(OUTPUT)/test_sockopt_inherit: cgroup_helpers.c diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt.c b/tools/testing/selftests/bpf/prog_tests/sockopt.c new file mode 100644 index 000000000000..3e8517a8395a --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/sockopt.c @@ -0,0 +1,985 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include "cgroup_helpers.h" + +static char bpf_log_buf[4096]; +static bool verbose; + +enum sockopt_test_error { + OK = 0, + DENY_LOAD, + DENY_ATTACH, + EPERM_GETSOCKOPT, + EFAULT_GETSOCKOPT, + EPERM_SETSOCKOPT, + EFAULT_SETSOCKOPT, +}; + +static struct sockopt_test { + const char *descr; + const struct bpf_insn insns[64]; + enum bpf_attach_type attach_type; + enum bpf_attach_type expected_attach_type; + + int set_optname; + int set_level; + const char set_optval[64]; + socklen_t set_optlen; + + int get_optname; + int get_level; + const char get_optval[64]; + socklen_t get_optlen; + socklen_t get_optlen_ret; + + enum sockopt_test_error error; +} tests[] = { + + /* ==================== getsockopt ==================== */ + + { + .descr = "getsockopt: no expected_attach_type", + .insns = { + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = 0, + .error = DENY_LOAD, + }, + { + .descr = "getsockopt: wrong expected_attach_type", + .insns = { + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + .error = DENY_ATTACH, + }, + { + .descr = "getsockopt: bypass bpf hook", + .insns = { + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .get_level = SOL_IP, + .set_level = SOL_IP, + + .get_optname = IP_TOS, + .set_optname = IP_TOS, + + .set_optval = { 1 << 3 }, + .set_optlen = 1, + + .get_optval = { 1 << 3 }, + .get_optlen = 1, + }, + { + .descr = "getsockopt: return EPERM from bpf hook", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .get_level = SOL_IP, + .get_optname = IP_TOS, + + .get_optlen = 1, + .error = EPERM_GETSOCKOPT, + }, + { + .descr = "getsockopt: no optval bounds check, deny loading", + .insns = { + /* r6 = ctx->optval */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optval)), + + /* ctx->optval[0] = 0x80 */ + BPF_MOV64_IMM(BPF_REG_0, 0x80), + BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0), + + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + .error = DENY_LOAD, + }, + { + .descr = "getsockopt: read ctx->level", + .insns = { + /* r6 = ctx->level */ + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, level)), + + /* if (ctx->level == 123) { */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4), + /* ctx->retval = 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, retval)), + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_JMP_A(1), + /* } else { */ + /* return 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + /* } */ + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .get_level = 123, + + .get_optlen = 1, + }, + { + .descr = "getsockopt: deny writing to ctx->level", + .insns = { + /* ctx->level = 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, level)), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .error = DENY_LOAD, + }, + { + .descr = "getsockopt: read ctx->optname", + .insns = { + /* r6 = ctx->optname */ + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optname)), + + /* if (ctx->optname == 123) { */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4), + /* ctx->retval = 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, retval)), + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_JMP_A(1), + /* } else { */ + /* return 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + /* } */ + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .get_optname = 123, + + .get_optlen = 1, + }, + { + .descr = "getsockopt: read ctx->retval", + .insns = { + /* r6 = ctx->retval */ + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, retval)), + + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .get_level = SOL_IP, + .get_optname = IP_TOS, + .get_optlen = 1, + }, + { + .descr = "getsockopt: deny writing to ctx->optname", + .insns = { + /* ctx->optname = 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optname)), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .error = DENY_LOAD, + }, + { + .descr = "getsockopt: read ctx->optlen", + .insns = { + /* r6 = ctx->optlen */ + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optlen)), + + /* if (ctx->optlen == 64) { */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4), + /* ctx->retval = 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, retval)), + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_JMP_A(1), + /* } else { */ + /* return 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + /* } */ + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .get_optlen = 64, + }, + { + .descr = "getsockopt: deny bigger ctx->optlen", + .insns = { + /* ctx->optlen = 65 */ + BPF_MOV64_IMM(BPF_REG_0, 65), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optlen)), + + /* ctx->retval = 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, retval)), + + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .get_optlen = 64, + + .error = EFAULT_GETSOCKOPT, + }, + { + .descr = "getsockopt: deny arbitrary ctx->retval", + .insns = { + /* ctx->retval = 123 */ + BPF_MOV64_IMM(BPF_REG_0, 123), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, retval)), + + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .get_optlen = 64, + + .error = EFAULT_GETSOCKOPT, + }, + { + .descr = "getsockopt: support smaller ctx->optlen", + .insns = { + /* ctx->optlen = 32 */ + BPF_MOV64_IMM(BPF_REG_0, 32), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optlen)), + /* ctx->retval = 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, retval)), + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .get_optlen = 64, + .get_optlen_ret = 32, + }, + { + .descr = "getsockopt: deny writing to ctx->optval", + .insns = { + /* ctx->optval = 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optval)), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .error = DENY_LOAD, + }, + { + .descr = "getsockopt: deny writing to ctx->optval_end", + .insns = { + /* ctx->optval_end = 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optval_end)), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .error = DENY_LOAD, + }, + { + .descr = "getsockopt: rewrite value", + .insns = { + /* r6 = ctx->optval */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optval)), + /* r2 = ctx->optval */ + BPF_MOV64_REG(BPF_REG_2, BPF_REG_6), + /* r6 = ctx->optval + 1 */ + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), + + /* r7 = ctx->optval_end */ + BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1, + offsetof(struct bpf_sockopt, optval_end)), + + /* if (ctx->optval + 1 <= ctx->optval_end) { */ + BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1), + /* ctx->optval[0] = 0xF0 */ + BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0), + /* } */ + + /* ctx->retval = 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, retval)), + + /* return 1*/ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_GETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + + .get_level = SOL_IP, + .get_optname = IP_TOS, + + .get_optval = { 0xF0 }, + .get_optlen = 1, + }, + + /* ==================== setsockopt ==================== */ + + { + .descr = "setsockopt: no expected_attach_type", + .insns = { + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = 0, + .error = DENY_LOAD, + }, + { + .descr = "setsockopt: wrong expected_attach_type", + .insns = { + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_GETSOCKOPT, + .error = DENY_ATTACH, + }, + { + .descr = "setsockopt: bypass bpf hook", + .insns = { + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .get_level = SOL_IP, + .set_level = SOL_IP, + + .get_optname = IP_TOS, + .set_optname = IP_TOS, + + .set_optval = { 1 << 3 }, + .set_optlen = 1, + + .get_optval = { 1 << 3 }, + .get_optlen = 1, + }, + { + .descr = "setsockopt: return EPERM from bpf hook", + .insns = { + /* return 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .set_level = SOL_IP, + .set_optname = IP_TOS, + + .set_optlen = 1, + .error = EPERM_SETSOCKOPT, + }, + { + .descr = "setsockopt: no optval bounds check, deny loading", + .insns = { + /* r6 = ctx->optval */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optval)), + + /* r0 = ctx->optval[0] */ + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0), + + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + .error = DENY_LOAD, + }, + { + .descr = "setsockopt: read ctx->level", + .insns = { + /* r6 = ctx->level */ + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, level)), + + /* if (ctx->level == 123) { */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4), + /* ctx->optlen = -1 */ + BPF_MOV64_IMM(BPF_REG_0, -1), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optlen)), + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_JMP_A(1), + /* } else { */ + /* return 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + /* } */ + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .set_level = 123, + + .set_optlen = 1, + }, + { + .descr = "setsockopt: allow changing ctx->level", + .insns = { + /* ctx->level = SOL_IP */ + BPF_MOV64_IMM(BPF_REG_0, SOL_IP), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, level)), + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .get_level = SOL_IP, + .set_level = 234, /* should be rewritten to SOL_IP */ + + .get_optname = IP_TOS, + .set_optname = IP_TOS, + + .set_optval = { 1 << 3 }, + .set_optlen = 1, + .get_optval = { 1 << 3 }, + .get_optlen = 1, + }, + { + .descr = "setsockopt: read ctx->optname", + .insns = { + /* r6 = ctx->optname */ + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optname)), + + /* if (ctx->optname == 123) { */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4), + /* ctx->optlen = -1 */ + BPF_MOV64_IMM(BPF_REG_0, -1), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optlen)), + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_JMP_A(1), + /* } else { */ + /* return 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + /* } */ + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .set_optname = 123, + + .set_optlen = 1, + }, + { + .descr = "setsockopt: allow changing ctx->optname", + .insns = { + /* ctx->optname = IP_TOS */ + BPF_MOV64_IMM(BPF_REG_0, IP_TOS), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optname)), + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .get_level = SOL_IP, + .set_level = SOL_IP, + + .get_optname = IP_TOS, + .set_optname = 456, /* should be rewritten to IP_TOS */ + + .set_optval = { 1 << 3 }, + .set_optlen = 1, + .get_optval = { 1 << 3 }, + .get_optlen = 1, + }, + { + .descr = "setsockopt: read ctx->optlen", + .insns = { + /* r6 = ctx->optlen */ + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optlen)), + + /* if (ctx->optlen == 64) { */ + BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4), + /* ctx->optlen = -1 */ + BPF_MOV64_IMM(BPF_REG_0, -1), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optlen)), + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_JMP_A(1), + /* } else { */ + /* return 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + /* } */ + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .set_optlen = 64, + }, + { + .descr = "setsockopt: ctx->optlen == -1 is ok", + .insns = { + /* ctx->optlen = -1 */ + BPF_MOV64_IMM(BPF_REG_0, -1), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optlen)), + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .set_optlen = 64, + }, + { + .descr = "setsockopt: deny ctx->optlen < 0 (except -1)", + .insns = { + /* ctx->optlen = -2 */ + BPF_MOV64_IMM(BPF_REG_0, -2), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optlen)), + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .set_optlen = 4, + + .error = EFAULT_SETSOCKOPT, + }, + { + .descr = "setsockopt: deny ctx->optlen > input optlen", + .insns = { + /* ctx->optlen = 65 */ + BPF_MOV64_IMM(BPF_REG_0, 65), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optlen)), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .set_optlen = 64, + + .error = EFAULT_SETSOCKOPT, + }, + { + .descr = "setsockopt: allow changing ctx->optlen within bounds", + .insns = { + /* r6 = ctx->optval */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optval)), + /* r2 = ctx->optval */ + BPF_MOV64_REG(BPF_REG_2, BPF_REG_6), + /* r6 = ctx->optval + 1 */ + BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), + + /* r7 = ctx->optval_end */ + BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1, + offsetof(struct bpf_sockopt, optval_end)), + + /* if (ctx->optval + 1 <= ctx->optval_end) { */ + BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1), + /* ctx->optval[0] = 1 << 3 */ + BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 1 << 3), + /* } */ + + /* ctx->optlen = 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optlen)), + + /* return 1*/ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .get_level = SOL_IP, + .set_level = SOL_IP, + + .get_optname = IP_TOS, + .set_optname = IP_TOS, + + .set_optval = { 1, 1, 1, 1 }, + .set_optlen = 4, + .get_optval = { 1 << 3 }, + .get_optlen = 1, + }, + { + .descr = "setsockopt: deny write ctx->retval", + .insns = { + /* ctx->retval = 0 */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, retval)), + + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .error = DENY_LOAD, + }, + { + .descr = "setsockopt: deny read ctx->retval", + .insns = { + /* r6 = ctx->retval */ + BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, retval)), + + /* return 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .error = DENY_LOAD, + }, + { + .descr = "setsockopt: deny writing to ctx->optval", + .insns = { + /* ctx->optval = 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optval)), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .error = DENY_LOAD, + }, + { + .descr = "setsockopt: deny writing to ctx->optval_end", + .insns = { + /* ctx->optval_end = 1 */ + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct bpf_sockopt, optval_end)), + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .error = DENY_LOAD, + }, + { + .descr = "setsockopt: allow IP_TOS <= 128", + .insns = { + /* r6 = ctx->optval */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optval)), + /* r7 = ctx->optval + 1 */ + BPF_MOV64_REG(BPF_REG_7, BPF_REG_6), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1), + + /* r8 = ctx->optval_end */ + BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1, + offsetof(struct bpf_sockopt, optval_end)), + + /* if (ctx->optval + 1 <= ctx->optval_end) { */ + BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4), + + /* r9 = ctx->optval[0] */ + BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0), + + /* if (ctx->optval[0] < 128) */ + BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_JMP_A(1), + /* } */ + + /* } else { */ + BPF_MOV64_IMM(BPF_REG_0, 0), + /* } */ + + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .get_level = SOL_IP, + .set_level = SOL_IP, + + .get_optname = IP_TOS, + .set_optname = IP_TOS, + + .set_optval = { 0x80 }, + .set_optlen = 1, + .get_optval = { 0x80 }, + .get_optlen = 1, + }, + { + .descr = "setsockopt: deny IP_TOS > 128", + .insns = { + /* r6 = ctx->optval */ + BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, + offsetof(struct bpf_sockopt, optval)), + /* r7 = ctx->optval + 1 */ + BPF_MOV64_REG(BPF_REG_7, BPF_REG_6), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1), + + /* r8 = ctx->optval_end */ + BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1, + offsetof(struct bpf_sockopt, optval_end)), + + /* if (ctx->optval + 1 <= ctx->optval_end) { */ + BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4), + + /* r9 = ctx->optval[0] */ + BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0), + + /* if (ctx->optval[0] < 128) */ + BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2), + BPF_MOV64_IMM(BPF_REG_0, 1), + BPF_JMP_A(1), + /* } */ + + /* } else { */ + BPF_MOV64_IMM(BPF_REG_0, 0), + /* } */ + + BPF_EXIT_INSN(), + }, + .attach_type = BPF_CGROUP_SETSOCKOPT, + .expected_attach_type = BPF_CGROUP_SETSOCKOPT, + + .get_level = SOL_IP, + .set_level = SOL_IP, + + .get_optname = IP_TOS, + .set_optname = IP_TOS, + + .set_optval = { 0x81 }, + .set_optlen = 1, + .get_optval = { 0x00 }, + .get_optlen = 1, + + .error = EPERM_SETSOCKOPT, + }, +}; + +static int load_prog(const struct bpf_insn *insns, + enum bpf_attach_type expected_attach_type) +{ + struct bpf_load_program_attr attr = { + .prog_type = BPF_PROG_TYPE_CGROUP_SOCKOPT, + .expected_attach_type = expected_attach_type, + .insns = insns, + .license = "GPL", + .log_level = 2, + }; + int fd; + + for (; + insns[attr.insns_cnt].code != (BPF_JMP | BPF_EXIT); + attr.insns_cnt++) { + } + attr.insns_cnt++; + + fd = bpf_load_program_xattr(&attr, bpf_log_buf, sizeof(bpf_log_buf)); + if (verbose && fd < 0) + fprintf(stderr, "%s\n", bpf_log_buf); + + return fd; +} + +static int run_test(int cgroup_fd, struct sockopt_test *test) +{ + int sock_fd, err, prog_fd; + void *optval = NULL; + int ret = 0; + + prog_fd = load_prog(test->insns, test->expected_attach_type); + if (prog_fd < 0) { + if (test->error == DENY_LOAD) + return 0; + + log_err("Failed to load BPF program"); + return -1; + } + + err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0); + if (err < 0) { + if (test->error == DENY_ATTACH) + goto close_prog_fd; + + log_err("Failed to attach BPF program"); + ret = -1; + goto close_prog_fd; + } + + sock_fd = socket(AF_INET, SOCK_STREAM, 0); + if (sock_fd < 0) { + log_err("Failed to create AF_INET socket"); + ret = -1; + goto detach_prog; + } + + if (test->set_optlen) { + err = setsockopt(sock_fd, test->set_level, test->set_optname, + test->set_optval, test->set_optlen); + if (err) { + if (errno == EPERM && test->error == EPERM_SETSOCKOPT) + goto close_sock_fd; + if (errno == EFAULT && test->error == EFAULT_SETSOCKOPT) + goto free_optval; + + log_err("Failed to call setsockopt"); + ret = -1; + goto close_sock_fd; + } + } + + if (test->get_optlen) { + optval = malloc(test->get_optlen); + socklen_t optlen = test->get_optlen; + socklen_t expected_get_optlen = test->get_optlen_ret ?: + test->get_optlen; + + err = getsockopt(sock_fd, test->get_level, test->get_optname, + optval, &optlen); + if (err) { + if (errno == EPERM && test->error == EPERM_GETSOCKOPT) + goto free_optval; + if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT) + goto free_optval; + + log_err("Failed to call getsockopt"); + ret = -1; + goto free_optval; + } + + if (optlen != expected_get_optlen) { + errno = 0; + log_err("getsockopt returned unexpected optlen"); + ret = -1; + goto free_optval; + } + + if (memcmp(optval, test->get_optval, optlen) != 0) { + errno = 0; + log_err("getsockopt returned unexpected optval"); + ret = -1; + goto free_optval; + } + } + + ret = test->error != OK; + +free_optval: + free(optval); +close_sock_fd: + close(sock_fd); +detach_prog: + bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type); +close_prog_fd: + close(prog_fd); + return ret; +} + +void test_sockopt(void) +{ + int cgroup_fd, i; + + cgroup_fd = test__join_cgroup("/sockopt"); + if (CHECK_FAIL(cgroup_fd < 0)) + return; + + for (i = 0; i < ARRAY_SIZE(tests); i++) { + test__start_subtest(tests[i].descr); + CHECK_FAIL(run_test(cgroup_fd, &tests[i])); + } + + close(cgroup_fd); +} diff --git a/tools/testing/selftests/bpf/test_sockopt.c b/tools/testing/selftests/bpf/test_sockopt.c deleted file mode 100644 index 23bd0819382d..000000000000 --- a/tools/testing/selftests/bpf/test_sockopt.c +++ /dev/null @@ -1,1021 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "bpf_rlimit.h" -#include "bpf_util.h" -#include "cgroup_helpers.h" - -#define CG_PATH "/sockopt" - -static char bpf_log_buf[4096]; -static bool verbose; - -enum sockopt_test_error { - OK = 0, - DENY_LOAD, - DENY_ATTACH, - EPERM_GETSOCKOPT, - EFAULT_GETSOCKOPT, - EPERM_SETSOCKOPT, - EFAULT_SETSOCKOPT, -}; - -static struct sockopt_test { - const char *descr; - const struct bpf_insn insns[64]; - enum bpf_attach_type attach_type; - enum bpf_attach_type expected_attach_type; - - int set_optname; - int set_level; - const char set_optval[64]; - socklen_t set_optlen; - - int get_optname; - int get_level; - const char get_optval[64]; - socklen_t get_optlen; - socklen_t get_optlen_ret; - - enum sockopt_test_error error; -} tests[] = { - - /* ==================== getsockopt ==================== */ - - { - .descr = "getsockopt: no expected_attach_type", - .insns = { - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = 0, - .error = DENY_LOAD, - }, - { - .descr = "getsockopt: wrong expected_attach_type", - .insns = { - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - .error = DENY_ATTACH, - }, - { - .descr = "getsockopt: bypass bpf hook", - .insns = { - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = BPF_CGROUP_GETSOCKOPT, - - .get_level = SOL_IP, - .set_level = SOL_IP, - - .get_optname = IP_TOS, - .set_optname = IP_TOS, - - .set_optval = { 1 << 3 }, - .set_optlen = 1, - - .get_optval = { 1 << 3 }, - .get_optlen = 1, - }, - { - .descr = "getsockopt: return EPERM from bpf hook", - .insns = { - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = BPF_CGROUP_GETSOCKOPT, - - .get_level = SOL_IP, - .get_optname = IP_TOS, - - .get_optlen = 1, - .error = EPERM_GETSOCKOPT, - }, - { - .descr = "getsockopt: no optval bounds check, deny loading", - .insns = { - /* r6 = ctx->optval */ - BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, - offsetof(struct bpf_sockopt, optval)), - - /* ctx->optval[0] = 0x80 */ - BPF_MOV64_IMM(BPF_REG_0, 0x80), - BPF_STX_MEM(BPF_W, BPF_REG_6, BPF_REG_0, 0), - - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = BPF_CGROUP_GETSOCKOPT, - .error = DENY_LOAD, - }, - { - .descr = "getsockopt: read ctx->level", - .insns = { - /* r6 = ctx->level */ - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct bpf_sockopt, level)), - - /* if (ctx->level == 123) { */ - BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4), - /* ctx->retval = 0 */ - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, retval)), - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_JMP_A(1), - /* } else { */ - /* return 0 */ - BPF_MOV64_IMM(BPF_REG_0, 0), - /* } */ - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = BPF_CGROUP_GETSOCKOPT, - - .get_level = 123, - - .get_optlen = 1, - }, - { - .descr = "getsockopt: deny writing to ctx->level", - .insns = { - /* ctx->level = 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, level)), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = BPF_CGROUP_GETSOCKOPT, - - .error = DENY_LOAD, - }, - { - .descr = "getsockopt: read ctx->optname", - .insns = { - /* r6 = ctx->optname */ - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct bpf_sockopt, optname)), - - /* if (ctx->optname == 123) { */ - BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4), - /* ctx->retval = 0 */ - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, retval)), - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_JMP_A(1), - /* } else { */ - /* return 0 */ - BPF_MOV64_IMM(BPF_REG_0, 0), - /* } */ - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = BPF_CGROUP_GETSOCKOPT, - - .get_optname = 123, - - .get_optlen = 1, - }, - { - .descr = "getsockopt: read ctx->retval", - .insns = { - /* r6 = ctx->retval */ - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct bpf_sockopt, retval)), - - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = BPF_CGROUP_GETSOCKOPT, - - .get_level = SOL_IP, - .get_optname = IP_TOS, - .get_optlen = 1, - }, - { - .descr = "getsockopt: deny writing to ctx->optname", - .insns = { - /* ctx->optname = 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, optname)), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = BPF_CGROUP_GETSOCKOPT, - - .error = DENY_LOAD, - }, - { - .descr = "getsockopt: read ctx->optlen", - .insns = { - /* r6 = ctx->optlen */ - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct bpf_sockopt, optlen)), - - /* if (ctx->optlen == 64) { */ - BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4), - /* ctx->retval = 0 */ - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, retval)), - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_JMP_A(1), - /* } else { */ - /* return 0 */ - BPF_MOV64_IMM(BPF_REG_0, 0), - /* } */ - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = BPF_CGROUP_GETSOCKOPT, - - .get_optlen = 64, - }, - { - .descr = "getsockopt: deny bigger ctx->optlen", - .insns = { - /* ctx->optlen = 65 */ - BPF_MOV64_IMM(BPF_REG_0, 65), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, optlen)), - - /* ctx->retval = 0 */ - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, retval)), - - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = BPF_CGROUP_GETSOCKOPT, - - .get_optlen = 64, - - .error = EFAULT_GETSOCKOPT, - }, - { - .descr = "getsockopt: deny arbitrary ctx->retval", - .insns = { - /* ctx->retval = 123 */ - BPF_MOV64_IMM(BPF_REG_0, 123), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, retval)), - - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = BPF_CGROUP_GETSOCKOPT, - - .get_optlen = 64, - - .error = EFAULT_GETSOCKOPT, - }, - { - .descr = "getsockopt: support smaller ctx->optlen", - .insns = { - /* ctx->optlen = 32 */ - BPF_MOV64_IMM(BPF_REG_0, 32), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, optlen)), - /* ctx->retval = 0 */ - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, retval)), - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = BPF_CGROUP_GETSOCKOPT, - - .get_optlen = 64, - .get_optlen_ret = 32, - }, - { - .descr = "getsockopt: deny writing to ctx->optval", - .insns = { - /* ctx->optval = 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, optval)), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = BPF_CGROUP_GETSOCKOPT, - - .error = DENY_LOAD, - }, - { - .descr = "getsockopt: deny writing to ctx->optval_end", - .insns = { - /* ctx->optval_end = 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, optval_end)), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = BPF_CGROUP_GETSOCKOPT, - - .error = DENY_LOAD, - }, - { - .descr = "getsockopt: rewrite value", - .insns = { - /* r6 = ctx->optval */ - BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, - offsetof(struct bpf_sockopt, optval)), - /* r2 = ctx->optval */ - BPF_MOV64_REG(BPF_REG_2, BPF_REG_6), - /* r6 = ctx->optval + 1 */ - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), - - /* r7 = ctx->optval_end */ - BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1, - offsetof(struct bpf_sockopt, optval_end)), - - /* if (ctx->optval + 1 <= ctx->optval_end) { */ - BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1), - /* ctx->optval[0] = 0xF0 */ - BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 0xF0), - /* } */ - - /* ctx->retval = 0 */ - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, retval)), - - /* return 1*/ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_GETSOCKOPT, - .expected_attach_type = BPF_CGROUP_GETSOCKOPT, - - .get_level = SOL_IP, - .get_optname = IP_TOS, - - .get_optval = { 0xF0 }, - .get_optlen = 1, - }, - - /* ==================== setsockopt ==================== */ - - { - .descr = "setsockopt: no expected_attach_type", - .insns = { - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = 0, - .error = DENY_LOAD, - }, - { - .descr = "setsockopt: wrong expected_attach_type", - .insns = { - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_GETSOCKOPT, - .error = DENY_ATTACH, - }, - { - .descr = "setsockopt: bypass bpf hook", - .insns = { - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .get_level = SOL_IP, - .set_level = SOL_IP, - - .get_optname = IP_TOS, - .set_optname = IP_TOS, - - .set_optval = { 1 << 3 }, - .set_optlen = 1, - - .get_optval = { 1 << 3 }, - .get_optlen = 1, - }, - { - .descr = "setsockopt: return EPERM from bpf hook", - .insns = { - /* return 0 */ - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .set_level = SOL_IP, - .set_optname = IP_TOS, - - .set_optlen = 1, - .error = EPERM_SETSOCKOPT, - }, - { - .descr = "setsockopt: no optval bounds check, deny loading", - .insns = { - /* r6 = ctx->optval */ - BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, - offsetof(struct bpf_sockopt, optval)), - - /* r0 = ctx->optval[0] */ - BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_6, 0), - - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - .error = DENY_LOAD, - }, - { - .descr = "setsockopt: read ctx->level", - .insns = { - /* r6 = ctx->level */ - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct bpf_sockopt, level)), - - /* if (ctx->level == 123) { */ - BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4), - /* ctx->optlen = -1 */ - BPF_MOV64_IMM(BPF_REG_0, -1), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, optlen)), - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_JMP_A(1), - /* } else { */ - /* return 0 */ - BPF_MOV64_IMM(BPF_REG_0, 0), - /* } */ - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .set_level = 123, - - .set_optlen = 1, - }, - { - .descr = "setsockopt: allow changing ctx->level", - .insns = { - /* ctx->level = SOL_IP */ - BPF_MOV64_IMM(BPF_REG_0, SOL_IP), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, level)), - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .get_level = SOL_IP, - .set_level = 234, /* should be rewritten to SOL_IP */ - - .get_optname = IP_TOS, - .set_optname = IP_TOS, - - .set_optval = { 1 << 3 }, - .set_optlen = 1, - .get_optval = { 1 << 3 }, - .get_optlen = 1, - }, - { - .descr = "setsockopt: read ctx->optname", - .insns = { - /* r6 = ctx->optname */ - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct bpf_sockopt, optname)), - - /* if (ctx->optname == 123) { */ - BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 123, 4), - /* ctx->optlen = -1 */ - BPF_MOV64_IMM(BPF_REG_0, -1), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, optlen)), - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_JMP_A(1), - /* } else { */ - /* return 0 */ - BPF_MOV64_IMM(BPF_REG_0, 0), - /* } */ - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .set_optname = 123, - - .set_optlen = 1, - }, - { - .descr = "setsockopt: allow changing ctx->optname", - .insns = { - /* ctx->optname = IP_TOS */ - BPF_MOV64_IMM(BPF_REG_0, IP_TOS), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, optname)), - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .get_level = SOL_IP, - .set_level = SOL_IP, - - .get_optname = IP_TOS, - .set_optname = 456, /* should be rewritten to IP_TOS */ - - .set_optval = { 1 << 3 }, - .set_optlen = 1, - .get_optval = { 1 << 3 }, - .get_optlen = 1, - }, - { - .descr = "setsockopt: read ctx->optlen", - .insns = { - /* r6 = ctx->optlen */ - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct bpf_sockopt, optlen)), - - /* if (ctx->optlen == 64) { */ - BPF_JMP_IMM(BPF_JNE, BPF_REG_6, 64, 4), - /* ctx->optlen = -1 */ - BPF_MOV64_IMM(BPF_REG_0, -1), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, optlen)), - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_JMP_A(1), - /* } else { */ - /* return 0 */ - BPF_MOV64_IMM(BPF_REG_0, 0), - /* } */ - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .set_optlen = 64, - }, - { - .descr = "setsockopt: ctx->optlen == -1 is ok", - .insns = { - /* ctx->optlen = -1 */ - BPF_MOV64_IMM(BPF_REG_0, -1), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, optlen)), - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .set_optlen = 64, - }, - { - .descr = "setsockopt: deny ctx->optlen < 0 (except -1)", - .insns = { - /* ctx->optlen = -2 */ - BPF_MOV64_IMM(BPF_REG_0, -2), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, optlen)), - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .set_optlen = 4, - - .error = EFAULT_SETSOCKOPT, - }, - { - .descr = "setsockopt: deny ctx->optlen > input optlen", - .insns = { - /* ctx->optlen = 65 */ - BPF_MOV64_IMM(BPF_REG_0, 65), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, optlen)), - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .set_optlen = 64, - - .error = EFAULT_SETSOCKOPT, - }, - { - .descr = "setsockopt: allow changing ctx->optlen within bounds", - .insns = { - /* r6 = ctx->optval */ - BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, - offsetof(struct bpf_sockopt, optval)), - /* r2 = ctx->optval */ - BPF_MOV64_REG(BPF_REG_2, BPF_REG_6), - /* r6 = ctx->optval + 1 */ - BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), - - /* r7 = ctx->optval_end */ - BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_1, - offsetof(struct bpf_sockopt, optval_end)), - - /* if (ctx->optval + 1 <= ctx->optval_end) { */ - BPF_JMP_REG(BPF_JGT, BPF_REG_6, BPF_REG_7, 1), - /* ctx->optval[0] = 1 << 3 */ - BPF_ST_MEM(BPF_B, BPF_REG_2, 0, 1 << 3), - /* } */ - - /* ctx->optlen = 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, optlen)), - - /* return 1*/ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .get_level = SOL_IP, - .set_level = SOL_IP, - - .get_optname = IP_TOS, - .set_optname = IP_TOS, - - .set_optval = { 1, 1, 1, 1 }, - .set_optlen = 4, - .get_optval = { 1 << 3 }, - .get_optlen = 1, - }, - { - .descr = "setsockopt: deny write ctx->retval", - .insns = { - /* ctx->retval = 0 */ - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, retval)), - - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .error = DENY_LOAD, - }, - { - .descr = "setsockopt: deny read ctx->retval", - .insns = { - /* r6 = ctx->retval */ - BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, - offsetof(struct bpf_sockopt, retval)), - - /* return 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .error = DENY_LOAD, - }, - { - .descr = "setsockopt: deny writing to ctx->optval", - .insns = { - /* ctx->optval = 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, optval)), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .error = DENY_LOAD, - }, - { - .descr = "setsockopt: deny writing to ctx->optval_end", - .insns = { - /* ctx->optval_end = 1 */ - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, - offsetof(struct bpf_sockopt, optval_end)), - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .error = DENY_LOAD, - }, - { - .descr = "setsockopt: allow IP_TOS <= 128", - .insns = { - /* r6 = ctx->optval */ - BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, - offsetof(struct bpf_sockopt, optval)), - /* r7 = ctx->optval + 1 */ - BPF_MOV64_REG(BPF_REG_7, BPF_REG_6), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1), - - /* r8 = ctx->optval_end */ - BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1, - offsetof(struct bpf_sockopt, optval_end)), - - /* if (ctx->optval + 1 <= ctx->optval_end) { */ - BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4), - - /* r9 = ctx->optval[0] */ - BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0), - - /* if (ctx->optval[0] < 128) */ - BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2), - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_JMP_A(1), - /* } */ - - /* } else { */ - BPF_MOV64_IMM(BPF_REG_0, 0), - /* } */ - - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .get_level = SOL_IP, - .set_level = SOL_IP, - - .get_optname = IP_TOS, - .set_optname = IP_TOS, - - .set_optval = { 0x80 }, - .set_optlen = 1, - .get_optval = { 0x80 }, - .get_optlen = 1, - }, - { - .descr = "setsockopt: deny IP_TOS > 128", - .insns = { - /* r6 = ctx->optval */ - BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_1, - offsetof(struct bpf_sockopt, optval)), - /* r7 = ctx->optval + 1 */ - BPF_MOV64_REG(BPF_REG_7, BPF_REG_6), - BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 1), - - /* r8 = ctx->optval_end */ - BPF_LDX_MEM(BPF_DW, BPF_REG_8, BPF_REG_1, - offsetof(struct bpf_sockopt, optval_end)), - - /* if (ctx->optval + 1 <= ctx->optval_end) { */ - BPF_JMP_REG(BPF_JGT, BPF_REG_7, BPF_REG_8, 4), - - /* r9 = ctx->optval[0] */ - BPF_LDX_MEM(BPF_B, BPF_REG_9, BPF_REG_6, 0), - - /* if (ctx->optval[0] < 128) */ - BPF_JMP_IMM(BPF_JGT, BPF_REG_9, 128, 2), - BPF_MOV64_IMM(BPF_REG_0, 1), - BPF_JMP_A(1), - /* } */ - - /* } else { */ - BPF_MOV64_IMM(BPF_REG_0, 0), - /* } */ - - BPF_EXIT_INSN(), - }, - .attach_type = BPF_CGROUP_SETSOCKOPT, - .expected_attach_type = BPF_CGROUP_SETSOCKOPT, - - .get_level = SOL_IP, - .set_level = SOL_IP, - - .get_optname = IP_TOS, - .set_optname = IP_TOS, - - .set_optval = { 0x81 }, - .set_optlen = 1, - .get_optval = { 0x00 }, - .get_optlen = 1, - - .error = EPERM_SETSOCKOPT, - }, -}; - -static int load_prog(const struct bpf_insn *insns, - enum bpf_attach_type expected_attach_type) -{ - struct bpf_load_program_attr attr = { - .prog_type = BPF_PROG_TYPE_CGROUP_SOCKOPT, - .expected_attach_type = expected_attach_type, - .insns = insns, - .license = "GPL", - .log_level = 2, - }; - int fd; - - for (; - insns[attr.insns_cnt].code != (BPF_JMP | BPF_EXIT); - attr.insns_cnt++) { - } - attr.insns_cnt++; - - fd = bpf_load_program_xattr(&attr, bpf_log_buf, sizeof(bpf_log_buf)); - if (verbose && fd < 0) - fprintf(stderr, "%s\n", bpf_log_buf); - - return fd; -} - -static int run_test(int cgroup_fd, struct sockopt_test *test) -{ - int sock_fd, err, prog_fd; - void *optval = NULL; - int ret = 0; - - prog_fd = load_prog(test->insns, test->expected_attach_type); - if (prog_fd < 0) { - if (test->error == DENY_LOAD) - return 0; - - log_err("Failed to load BPF program"); - return -1; - } - - err = bpf_prog_attach(prog_fd, cgroup_fd, test->attach_type, 0); - if (err < 0) { - if (test->error == DENY_ATTACH) - goto close_prog_fd; - - log_err("Failed to attach BPF program"); - ret = -1; - goto close_prog_fd; - } - - sock_fd = socket(AF_INET, SOCK_STREAM, 0); - if (sock_fd < 0) { - log_err("Failed to create AF_INET socket"); - ret = -1; - goto detach_prog; - } - - if (test->set_optlen) { - err = setsockopt(sock_fd, test->set_level, test->set_optname, - test->set_optval, test->set_optlen); - if (err) { - if (errno == EPERM && test->error == EPERM_SETSOCKOPT) - goto close_sock_fd; - if (errno == EFAULT && test->error == EFAULT_SETSOCKOPT) - goto free_optval; - - log_err("Failed to call setsockopt"); - ret = -1; - goto close_sock_fd; - } - } - - if (test->get_optlen) { - optval = malloc(test->get_optlen); - socklen_t optlen = test->get_optlen; - socklen_t expected_get_optlen = test->get_optlen_ret ?: - test->get_optlen; - - err = getsockopt(sock_fd, test->get_level, test->get_optname, - optval, &optlen); - if (err) { - if (errno == EPERM && test->error == EPERM_GETSOCKOPT) - goto free_optval; - if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT) - goto free_optval; - - log_err("Failed to call getsockopt"); - ret = -1; - goto free_optval; - } - - if (optlen != expected_get_optlen) { - errno = 0; - log_err("getsockopt returned unexpected optlen"); - ret = -1; - goto free_optval; - } - - if (memcmp(optval, test->get_optval, optlen) != 0) { - errno = 0; - log_err("getsockopt returned unexpected optval"); - ret = -1; - goto free_optval; - } - } - - ret = test->error != OK; - -free_optval: - free(optval); -close_sock_fd: - close(sock_fd); -detach_prog: - bpf_prog_detach2(prog_fd, cgroup_fd, test->attach_type); -close_prog_fd: - close(prog_fd); - return ret; -} - -int main(int args, char **argv) -{ - int err = EXIT_FAILURE, error_cnt = 0; - int cgroup_fd, i; - - if (setup_cgroup_environment()) - goto cleanup_obj; - - cgroup_fd = create_and_get_cgroup(CG_PATH); - if (cgroup_fd < 0) - goto cleanup_cgroup_env; - - if (join_cgroup(CG_PATH)) - goto cleanup_cgroup; - - for (i = 0; i < ARRAY_SIZE(tests); i++) { - int err = run_test(cgroup_fd, &tests[i]); - - if (err) - error_cnt++; - - printf("#%d %s: %s\n", i, err ? "FAIL" : "PASS", - tests[i].descr); - } - - printf("Summary: %ld PASSED, %d FAILED\n", - ARRAY_SIZE(tests) - error_cnt, error_cnt); - err = error_cnt ? EXIT_FAILURE : EXIT_SUCCESS; - -cleanup_cgroup: - close(cgroup_fd); -cleanup_cgroup_env: - cleanup_cgroup_environment(); -cleanup_obj: - return err; -} -- cgit v1.2.3-59-g8ed1b