We are relying on the fact, that we can pass > sizeof(int) optvals to the SOL_IP+IP_FREEBIND option (the kernel will take first 4 bytes). In the BPF program, we return EPERM if optval is greater than optval_end (implemented via PTR_TO_PACKET/PTR_TO_PACKET_END) and rely on the verifier to enforce the fact that this data can not be touched. Signed-off-by: Stanislav Fomichev <sdf@xxxxxxxxxx> --- .../selftests/bpf/prog_tests/sockopt_sk.c | 26 +++++++++++++++++++ .../testing/selftests/bpf/progs/sockopt_sk.c | 20 ++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c index 2061a6beac0f..eae1c8a1fee0 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c +++ b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c @@ -13,6 +13,7 @@ static int getsetsockopt(void) char cc[16]; /* TCP_CA_NAME_MAX */ } buf = {}; socklen_t optlen; + char *big_buf; fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { @@ -78,6 +79,31 @@ static int getsetsockopt(void) goto err; } + /* IP_FREEBIND - BPF can't access optval when optlen > PAGE_SIZE */ + + optlen = getpagesize() * 2; + big_buf = calloc(1, optlen); + if (!big_buf) { + log_err("Couldn't allocate two pages"); + goto err; + } + + err = setsockopt(fd, SOL_IP, IP_FREEBIND, big_buf, optlen); + if (err != 0) { + log_err("Failed to call setsockopt, ret=%d", err); + free(big_buf); + goto err; + } + + err = getsockopt(fd, SOL_IP, IP_FREEBIND, big_buf, &optlen); + if (err != 0) { + log_err("Failed to call getsockopt, ret=%d", err); + free(big_buf); + goto err; + } + + free(big_buf); + /* SO_SNDBUF is overwritten */ buf.u32 = 0x01010101; diff --git a/tools/testing/selftests/bpf/progs/sockopt_sk.c b/tools/testing/selftests/bpf/progs/sockopt_sk.c index d5a5eeb5fb52..933a2ef9c930 100644 --- a/tools/testing/selftests/bpf/progs/sockopt_sk.c +++ b/tools/testing/selftests/bpf/progs/sockopt_sk.c @@ -51,6 +51,16 @@ int _getsockopt(struct bpf_sockopt *ctx) return 1; } + if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) { + if (optval > optval_end) { + /* For optval > PAGE_SIZE, the actual data + * is not provided. + */ + return 0; /* EPERM, unexpected data size */ + } + return 1; + } + if (ctx->level != SOL_CUSTOM) return 0; /* EPERM, deny everything except custom level */ @@ -112,6 +122,16 @@ int _setsockopt(struct bpf_sockopt *ctx) return 1; } + if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) { + if (optval > optval_end) { + /* For optval > PAGE_SIZE, the actual data + * is not provided. + */ + return 0; /* EPERM, unexpected data size */ + } + return 1; + } + if (ctx->level != SOL_CUSTOM) return 0; /* EPERM, deny everything except custom level */ -- 2.27.0.290.gba653c62da-goog