Commit 79b3604d authored by Martin KaFai Lau's avatar Martin KaFai Lau

Merge branch 'bpf: Don't EFAULT for {g,s}setsockopt with wrong optlen'

Stanislav Fomichev says:

====================
optval larger than PAGE_SIZE leads to EFAULT if the BPF program
isn't careful enough. This is often overlooked and might break
completely unrelated socket options. Instead of EFAULT,
let's ignore BPF program buffer changes. See the first patch for
more info.

In addition, clearly document this corner case and reset optlen
in our selftests (in case somebody copy-pastes from them).

v6:
- no changes; resending due to screwing up v5 series with the unrelated
  patch

v5:
- goto in the selftest (Martin)
- set IP_TOS to zero to avoid endianness complications (Martin)

v4:
- ignore retval as well when optlen > PAGE_SIZE (Martin)

v3:
- don't hard-code PAGE_SIZE (Martin)
- reset orig_optlen in getsockopt when kernel part succeeds (Martin)
====================
Signed-off-by: default avatarMartin KaFai Lau <martin.lau@kernel.org>
parents bdeeed34 6b6a23d5
...@@ -98,10 +98,65 @@ can access only the first ``PAGE_SIZE`` of that data. So it has to options: ...@@ -98,10 +98,65 @@ can access only the first ``PAGE_SIZE`` of that data. So it has to options:
indicates that the kernel should use BPF's trimmed ``optval``. indicates that the kernel should use BPF's trimmed ``optval``.
When the BPF program returns with the ``optlen`` greater than When the BPF program returns with the ``optlen`` greater than
``PAGE_SIZE``, the userspace will receive ``EFAULT`` errno. ``PAGE_SIZE``, the userspace will receive original kernel
buffers without any modifications that the BPF program might have
applied.
Example Example
======= =======
Recommended way to handle BPF programs is as follows:
.. code-block:: c
SEC("cgroup/getsockopt")
int getsockopt(struct bpf_sockopt *ctx)
{
/* Custom socket option. */
if (ctx->level == MY_SOL && ctx->optname == MY_OPTNAME) {
ctx->retval = 0;
optval[0] = ...;
ctx->optlen = 1;
return 1;
}
/* Modify kernel's socket option. */
if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) {
ctx->retval = 0;
optval[0] = ...;
ctx->optlen = 1;
return 1;
}
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > PAGE_SIZE)
ctx->optlen = 0;
return 1;
}
SEC("cgroup/setsockopt")
int setsockopt(struct bpf_sockopt *ctx)
{
/* Custom socket option. */
if (ctx->level == MY_SOL && ctx->optname == MY_OPTNAME) {
/* do something */
ctx->optlen = -1;
return 1;
}
/* Modify kernel's socket option. */
if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) {
optval[0] = ...;
return 1;
}
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > PAGE_SIZE)
ctx->optlen = 0;
return 1;
}
See ``tools/testing/selftests/bpf/progs/sockopt_sk.c`` for an example See ``tools/testing/selftests/bpf/progs/sockopt_sk.c`` for an example
of BPF program that handles socket options. of BPF program that handles socket options.
...@@ -1826,6 +1826,12 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level, ...@@ -1826,6 +1826,12 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level,
ret = 1; ret = 1;
} else if (ctx.optlen > max_optlen || ctx.optlen < -1) { } else if (ctx.optlen > max_optlen || ctx.optlen < -1) {
/* optlen is out of bounds */ /* optlen is out of bounds */
if (*optlen > PAGE_SIZE && ctx.optlen >= 0) {
pr_info_once("bpf setsockopt: ignoring program buffer with optlen=%d (max_optlen=%d)\n",
ctx.optlen, max_optlen);
ret = 0;
goto out;
}
ret = -EFAULT; ret = -EFAULT;
} else { } else {
/* optlen within bounds, run kernel handler */ /* optlen within bounds, run kernel handler */
...@@ -1881,8 +1887,10 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, ...@@ -1881,8 +1887,10 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level,
.optname = optname, .optname = optname,
.current_task = current, .current_task = current,
}; };
int orig_optlen;
int ret; int ret;
orig_optlen = max_optlen;
ctx.optlen = max_optlen; ctx.optlen = max_optlen;
max_optlen = sockopt_alloc_buf(&ctx, max_optlen, &buf); max_optlen = sockopt_alloc_buf(&ctx, max_optlen, &buf);
if (max_optlen < 0) if (max_optlen < 0)
...@@ -1905,6 +1913,7 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, ...@@ -1905,6 +1913,7 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level,
ret = -EFAULT; ret = -EFAULT;
goto out; goto out;
} }
orig_optlen = ctx.optlen;
if (copy_from_user(ctx.optval, optval, if (copy_from_user(ctx.optval, optval,
min(ctx.optlen, max_optlen)) != 0) { min(ctx.optlen, max_optlen)) != 0) {
...@@ -1922,6 +1931,12 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level, ...@@ -1922,6 +1931,12 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level,
goto out; goto out;
if (optval && (ctx.optlen > max_optlen || ctx.optlen < 0)) { if (optval && (ctx.optlen > max_optlen || ctx.optlen < 0)) {
if (orig_optlen > PAGE_SIZE && ctx.optlen >= 0) {
pr_info_once("bpf getsockopt: ignoring program buffer with optlen=%d (max_optlen=%d)\n",
ctx.optlen, max_optlen);
ret = retval;
goto out;
}
ret = -EFAULT; ret = -EFAULT;
goto out; goto out;
} }
......
...@@ -25,6 +25,8 @@ static void test_setsockopt_set(int cgroup_fd, int sock_fd) ...@@ -25,6 +25,8 @@ static void test_setsockopt_set(int cgroup_fd, int sock_fd)
if (!ASSERT_OK_PTR(obj, "skel-load")) if (!ASSERT_OK_PTR(obj, "skel-load"))
return; return;
obj->bss->page_size = sysconf(_SC_PAGESIZE);
/* Attach setsockopt that sets EUNATCH, assert that /* Attach setsockopt that sets EUNATCH, assert that
* we actually get that error when we run setsockopt() * we actually get that error when we run setsockopt()
*/ */
...@@ -59,6 +61,8 @@ static void test_setsockopt_set_and_get(int cgroup_fd, int sock_fd) ...@@ -59,6 +61,8 @@ static void test_setsockopt_set_and_get(int cgroup_fd, int sock_fd)
if (!ASSERT_OK_PTR(obj, "skel-load")) if (!ASSERT_OK_PTR(obj, "skel-load"))
return; return;
obj->bss->page_size = sysconf(_SC_PAGESIZE);
/* Attach setsockopt that sets EUNATCH, and one that gets the /* Attach setsockopt that sets EUNATCH, and one that gets the
* previously set errno. Assert that we get the same errno back. * previously set errno. Assert that we get the same errno back.
*/ */
...@@ -100,6 +104,8 @@ static void test_setsockopt_default_zero(int cgroup_fd, int sock_fd) ...@@ -100,6 +104,8 @@ static void test_setsockopt_default_zero(int cgroup_fd, int sock_fd)
if (!ASSERT_OK_PTR(obj, "skel-load")) if (!ASSERT_OK_PTR(obj, "skel-load"))
return; return;
obj->bss->page_size = sysconf(_SC_PAGESIZE);
/* Attach setsockopt that gets the previously set errno. /* Attach setsockopt that gets the previously set errno.
* Assert that, without anything setting one, we get 0. * Assert that, without anything setting one, we get 0.
*/ */
...@@ -134,6 +140,8 @@ static void test_setsockopt_default_zero_and_set(int cgroup_fd, int sock_fd) ...@@ -134,6 +140,8 @@ static void test_setsockopt_default_zero_and_set(int cgroup_fd, int sock_fd)
if (!ASSERT_OK_PTR(obj, "skel-load")) if (!ASSERT_OK_PTR(obj, "skel-load"))
return; return;
obj->bss->page_size = sysconf(_SC_PAGESIZE);
/* Attach setsockopt that gets the previously set errno, and then /* Attach setsockopt that gets the previously set errno, and then
* one that sets the errno to EUNATCH. Assert that the get does not * one that sets the errno to EUNATCH. Assert that the get does not
* see EUNATCH set later, and does not prevent EUNATCH from being set. * see EUNATCH set later, and does not prevent EUNATCH from being set.
...@@ -177,6 +185,8 @@ static void test_setsockopt_override(int cgroup_fd, int sock_fd) ...@@ -177,6 +185,8 @@ static void test_setsockopt_override(int cgroup_fd, int sock_fd)
if (!ASSERT_OK_PTR(obj, "skel-load")) if (!ASSERT_OK_PTR(obj, "skel-load"))
return; return;
obj->bss->page_size = sysconf(_SC_PAGESIZE);
/* Attach setsockopt that sets EUNATCH, then one that sets EISCONN, /* Attach setsockopt that sets EUNATCH, then one that sets EISCONN,
* and then one that gets the exported errno. Assert both the syscall * and then one that gets the exported errno. Assert both the syscall
* and the helper sees the last set errno. * and the helper sees the last set errno.
...@@ -224,6 +234,8 @@ static void test_setsockopt_legacy_eperm(int cgroup_fd, int sock_fd) ...@@ -224,6 +234,8 @@ static void test_setsockopt_legacy_eperm(int cgroup_fd, int sock_fd)
if (!ASSERT_OK_PTR(obj, "skel-load")) if (!ASSERT_OK_PTR(obj, "skel-load"))
return; return;
obj->bss->page_size = sysconf(_SC_PAGESIZE);
/* Attach setsockopt that return a reject without setting errno /* Attach setsockopt that return a reject without setting errno
* (legacy reject), and one that gets the errno. Assert that for * (legacy reject), and one that gets the errno. Assert that for
* backward compatibility the syscall result in EPERM, and this * backward compatibility the syscall result in EPERM, and this
...@@ -268,6 +280,8 @@ static void test_setsockopt_legacy_no_override(int cgroup_fd, int sock_fd) ...@@ -268,6 +280,8 @@ static void test_setsockopt_legacy_no_override(int cgroup_fd, int sock_fd)
if (!ASSERT_OK_PTR(obj, "skel-load")) if (!ASSERT_OK_PTR(obj, "skel-load"))
return; return;
obj->bss->page_size = sysconf(_SC_PAGESIZE);
/* Attach setsockopt that sets EUNATCH, then one that return a reject /* Attach setsockopt that sets EUNATCH, then one that return a reject
* without setting errno, and then one that gets the exported errno. * without setting errno, and then one that gets the exported errno.
* Assert both the syscall and the helper's errno are unaffected by * Assert both the syscall and the helper's errno are unaffected by
...@@ -319,6 +333,8 @@ static void test_getsockopt_get(int cgroup_fd, int sock_fd) ...@@ -319,6 +333,8 @@ static void test_getsockopt_get(int cgroup_fd, int sock_fd)
if (!ASSERT_OK_PTR(obj, "skel-load")) if (!ASSERT_OK_PTR(obj, "skel-load"))
return; return;
obj->bss->page_size = sysconf(_SC_PAGESIZE);
/* Attach getsockopt that gets previously set errno. Assert that the /* Attach getsockopt that gets previously set errno. Assert that the
* error from kernel is in both ctx_retval_value and retval_value. * error from kernel is in both ctx_retval_value and retval_value.
*/ */
...@@ -359,6 +375,8 @@ static void test_getsockopt_override(int cgroup_fd, int sock_fd) ...@@ -359,6 +375,8 @@ static void test_getsockopt_override(int cgroup_fd, int sock_fd)
if (!ASSERT_OK_PTR(obj, "skel-load")) if (!ASSERT_OK_PTR(obj, "skel-load"))
return; return;
obj->bss->page_size = sysconf(_SC_PAGESIZE);
/* Attach getsockopt that sets retval to -EISCONN. Assert that this /* Attach getsockopt that sets retval to -EISCONN. Assert that this
* overrides the value from kernel. * overrides the value from kernel.
*/ */
...@@ -396,6 +414,8 @@ static void test_getsockopt_retval_sync(int cgroup_fd, int sock_fd) ...@@ -396,6 +414,8 @@ static void test_getsockopt_retval_sync(int cgroup_fd, int sock_fd)
if (!ASSERT_OK_PTR(obj, "skel-load")) if (!ASSERT_OK_PTR(obj, "skel-load"))
return; return;
obj->bss->page_size = sysconf(_SC_PAGESIZE);
/* Attach getsockopt that sets retval to -EISCONN, and one that clears /* Attach getsockopt that sets retval to -EISCONN, and one that clears
* ctx retval. Assert that the clearing ctx retval is synced to helper * ctx retval. Assert that the clearing ctx retval is synced to helper
* and clears any errors both from kernel and BPF.. * and clears any errors both from kernel and BPF..
......
...@@ -5,10 +5,15 @@ ...@@ -5,10 +5,15 @@
static char bpf_log_buf[4096]; static char bpf_log_buf[4096];
static bool verbose; static bool verbose;
#ifndef PAGE_SIZE
#define PAGE_SIZE 4096
#endif
enum sockopt_test_error { enum sockopt_test_error {
OK = 0, OK = 0,
DENY_LOAD, DENY_LOAD,
DENY_ATTACH, DENY_ATTACH,
EOPNOTSUPP_GETSOCKOPT,
EPERM_GETSOCKOPT, EPERM_GETSOCKOPT,
EFAULT_GETSOCKOPT, EFAULT_GETSOCKOPT,
EPERM_SETSOCKOPT, EPERM_SETSOCKOPT,
...@@ -273,10 +278,31 @@ static struct sockopt_test { ...@@ -273,10 +278,31 @@ static struct sockopt_test {
.error = EFAULT_GETSOCKOPT, .error = EFAULT_GETSOCKOPT,
}, },
{ {
.descr = "getsockopt: deny arbitrary ctx->retval", .descr = "getsockopt: ignore >PAGE_SIZE optlen",
.insns = { .insns = {
/* ctx->retval = 123 */ /* write 0xFF to the first optval byte */
BPF_MOV64_IMM(BPF_REG_0, 123),
/* 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, 0xFF),
/* } */
/* retval changes are ignored */
/* ctx->retval = 5 */
BPF_MOV64_IMM(BPF_REG_0, 5),
BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
offsetof(struct bpf_sockopt, retval)), offsetof(struct bpf_sockopt, retval)),
...@@ -287,9 +313,11 @@ static struct sockopt_test { ...@@ -287,9 +313,11 @@ static struct sockopt_test {
.attach_type = BPF_CGROUP_GETSOCKOPT, .attach_type = BPF_CGROUP_GETSOCKOPT,
.expected_attach_type = BPF_CGROUP_GETSOCKOPT, .expected_attach_type = BPF_CGROUP_GETSOCKOPT,
.get_optlen = 64, .get_level = 1234,
.get_optname = 5678,
.error = EFAULT_GETSOCKOPT, .get_optval = {}, /* the changes are ignored */
.get_optlen = PAGE_SIZE + 1,
.error = EOPNOTSUPP_GETSOCKOPT,
}, },
{ {
.descr = "getsockopt: support smaller ctx->optlen", .descr = "getsockopt: support smaller ctx->optlen",
...@@ -648,6 +676,45 @@ static struct sockopt_test { ...@@ -648,6 +676,45 @@ static struct sockopt_test {
.error = EFAULT_SETSOCKOPT, .error = EFAULT_SETSOCKOPT,
}, },
{
.descr = "setsockopt: ignore >PAGE_SIZE optlen",
.insns = {
/* write 0xFF to the first optval byte */
/* 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),
/* } */
BPF_MOV64_IMM(BPF_REG_0, 1),
BPF_EXIT_INSN(),
},
.attach_type = BPF_CGROUP_SETSOCKOPT,
.expected_attach_type = BPF_CGROUP_SETSOCKOPT,
.set_level = SOL_IP,
.set_optname = IP_TOS,
.set_optval = {},
.set_optlen = PAGE_SIZE + 1,
.get_level = SOL_IP,
.get_optname = IP_TOS,
.get_optval = {}, /* the changes are ignored */
.get_optlen = 4,
},
{ {
.descr = "setsockopt: allow changing ctx->optlen within bounds", .descr = "setsockopt: allow changing ctx->optlen within bounds",
.insns = { .insns = {
...@@ -906,6 +973,13 @@ static int run_test(int cgroup_fd, struct sockopt_test *test) ...@@ -906,6 +973,13 @@ static int run_test(int cgroup_fd, struct sockopt_test *test)
} }
if (test->set_optlen) { if (test->set_optlen) {
if (test->set_optlen >= PAGE_SIZE) {
int num_pages = test->set_optlen / PAGE_SIZE;
int remainder = test->set_optlen % PAGE_SIZE;
test->set_optlen = num_pages * sysconf(_SC_PAGESIZE) + remainder;
}
err = setsockopt(sock_fd, test->set_level, test->set_optname, err = setsockopt(sock_fd, test->set_level, test->set_optname,
test->set_optval, test->set_optlen); test->set_optval, test->set_optlen);
if (err) { if (err) {
...@@ -921,7 +995,15 @@ static int run_test(int cgroup_fd, struct sockopt_test *test) ...@@ -921,7 +995,15 @@ static int run_test(int cgroup_fd, struct sockopt_test *test)
} }
if (test->get_optlen) { if (test->get_optlen) {
if (test->get_optlen >= PAGE_SIZE) {
int num_pages = test->get_optlen / PAGE_SIZE;
int remainder = test->get_optlen % PAGE_SIZE;
test->get_optlen = num_pages * sysconf(_SC_PAGESIZE) + remainder;
}
optval = malloc(test->get_optlen); optval = malloc(test->get_optlen);
memset(optval, 0, test->get_optlen);
socklen_t optlen = test->get_optlen; socklen_t optlen = test->get_optlen;
socklen_t expected_get_optlen = test->get_optlen_ret ?: socklen_t expected_get_optlen = test->get_optlen_ret ?:
test->get_optlen; test->get_optlen;
...@@ -929,6 +1011,8 @@ static int run_test(int cgroup_fd, struct sockopt_test *test) ...@@ -929,6 +1011,8 @@ static int run_test(int cgroup_fd, struct sockopt_test *test)
err = getsockopt(sock_fd, test->get_level, test->get_optname, err = getsockopt(sock_fd, test->get_level, test->get_optname,
optval, &optlen); optval, &optlen);
if (err) { if (err) {
if (errno == EOPNOTSUPP && test->error == EOPNOTSUPP_GETSOCKOPT)
goto free_optval;
if (errno == EPERM && test->error == EPERM_GETSOCKOPT) if (errno == EPERM && test->error == EPERM_GETSOCKOPT)
goto free_optval; goto free_optval;
if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT) if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT)
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
#include <test_progs.h> #include <test_progs.h>
#include "cgroup_helpers.h" #include "cgroup_helpers.h"
#include "sockopt_inherit.skel.h"
#define SOL_CUSTOM 0xdeadbeef #define SOL_CUSTOM 0xdeadbeef
#define CUSTOM_INHERIT1 0 #define CUSTOM_INHERIT1 0
#define CUSTOM_INHERIT2 1 #define CUSTOM_INHERIT2 1
...@@ -132,58 +134,30 @@ static int start_server(void) ...@@ -132,58 +134,30 @@ static int start_server(void)
return fd; return fd;
} }
static int prog_attach(struct bpf_object *obj, int cgroup_fd, const char *title,
const char *prog_name)
{
enum bpf_attach_type attach_type;
enum bpf_prog_type prog_type;
struct bpf_program *prog;
int err;
err = libbpf_prog_type_by_name(title, &prog_type, &attach_type);
if (err) {
log_err("Failed to deduct types for %s BPF program", prog_name);
return -1;
}
prog = bpf_object__find_program_by_name(obj, prog_name);
if (!prog) {
log_err("Failed to find %s BPF program", prog_name);
return -1;
}
err = bpf_prog_attach(bpf_program__fd(prog), cgroup_fd,
attach_type, 0);
if (err) {
log_err("Failed to attach %s BPF program", prog_name);
return -1;
}
return 0;
}
static void run_test(int cgroup_fd) static void run_test(int cgroup_fd)
{ {
struct bpf_link *link_getsockopt = NULL;
struct bpf_link *link_setsockopt = NULL;
int server_fd = -1, client_fd; int server_fd = -1, client_fd;
struct bpf_object *obj; struct sockopt_inherit *obj;
void *server_err; void *server_err;
pthread_t tid; pthread_t tid;
int err; int err;
obj = bpf_object__open_file("sockopt_inherit.bpf.o", NULL); obj = sockopt_inherit__open_and_load();
if (!ASSERT_OK_PTR(obj, "obj_open")) if (!ASSERT_OK_PTR(obj, "skel-load"))
return; return;
err = bpf_object__load(obj); obj->bss->page_size = sysconf(_SC_PAGESIZE);
if (!ASSERT_OK(err, "obj_load"))
goto close_bpf_object;
err = prog_attach(obj, cgroup_fd, "cgroup/getsockopt", "_getsockopt"); link_getsockopt = bpf_program__attach_cgroup(obj->progs._getsockopt,
if (!ASSERT_OK(err, "prog_attach _getsockopt")) cgroup_fd);
if (!ASSERT_OK_PTR(link_getsockopt, "cg-attach-getsockopt"))
goto close_bpf_object; goto close_bpf_object;
err = prog_attach(obj, cgroup_fd, "cgroup/setsockopt", "_setsockopt"); link_setsockopt = bpf_program__attach_cgroup(obj->progs._setsockopt,
if (!ASSERT_OK(err, "prog_attach _setsockopt")) cgroup_fd);
if (!ASSERT_OK_PTR(link_setsockopt, "cg-attach-setsockopt"))
goto close_bpf_object; goto close_bpf_object;
server_fd = start_server(); server_fd = start_server();
...@@ -217,7 +191,10 @@ static void run_test(int cgroup_fd) ...@@ -217,7 +191,10 @@ static void run_test(int cgroup_fd)
close_server_fd: close_server_fd:
close(server_fd); close(server_fd);
close_bpf_object: close_bpf_object:
bpf_object__close(obj); bpf_link__destroy(link_getsockopt);
bpf_link__destroy(link_setsockopt);
sockopt_inherit__destroy(obj);
} }
void test_sockopt_inherit(void) void test_sockopt_inherit(void)
......
...@@ -2,61 +2,13 @@ ...@@ -2,61 +2,13 @@
#include <test_progs.h> #include <test_progs.h>
#include "cgroup_helpers.h" #include "cgroup_helpers.h"
static int prog_attach(struct bpf_object *obj, int cgroup_fd, const char *title, const char *name) #include "sockopt_multi.skel.h"
{
enum bpf_attach_type attach_type;
enum bpf_prog_type prog_type;
struct bpf_program *prog;
int err;
err = libbpf_prog_type_by_name(title, &prog_type, &attach_type);
if (err) {
log_err("Failed to deduct types for %s BPF program", title);
return -1;
}
prog = bpf_object__find_program_by_name(obj, name);
if (!prog) {
log_err("Failed to find %s BPF program", name);
return -1;
}
err = bpf_prog_attach(bpf_program__fd(prog), cgroup_fd,
attach_type, BPF_F_ALLOW_MULTI);
if (err) {
log_err("Failed to attach %s BPF program", name);
return -1;
}
return 0;
}
static int prog_detach(struct bpf_object *obj, int cgroup_fd, const char *title, const char *name) static int run_getsockopt_test(struct sockopt_multi *obj, int cg_parent,
{
enum bpf_attach_type attach_type;
enum bpf_prog_type prog_type;
struct bpf_program *prog;
int err;
err = libbpf_prog_type_by_name(title, &prog_type, &attach_type);
if (err)
return -1;
prog = bpf_object__find_program_by_name(obj, name);
if (!prog)
return -1;
err = bpf_prog_detach2(bpf_program__fd(prog), cgroup_fd,
attach_type);
if (err)
return -1;
return 0;
}
static int run_getsockopt_test(struct bpf_object *obj, int cg_parent,
int cg_child, int sock_fd) int cg_child, int sock_fd)
{ {
struct bpf_link *link_parent = NULL;
struct bpf_link *link_child = NULL;
socklen_t optlen; socklen_t optlen;
__u8 buf; __u8 buf;
int err; int err;
...@@ -89,8 +41,9 @@ static int run_getsockopt_test(struct bpf_object *obj, int cg_parent, ...@@ -89,8 +41,9 @@ static int run_getsockopt_test(struct bpf_object *obj, int cg_parent,
* - child: 0x80 -> 0x90 * - child: 0x80 -> 0x90
*/ */
err = prog_attach(obj, cg_child, "cgroup/getsockopt", "_getsockopt_child"); link_child = bpf_program__attach_cgroup(obj->progs._getsockopt_child,
if (err) cg_child);
if (!ASSERT_OK_PTR(link_child, "cg-attach-getsockopt_child"))
goto detach; goto detach;
buf = 0x00; buf = 0x00;
...@@ -113,8 +66,9 @@ static int run_getsockopt_test(struct bpf_object *obj, int cg_parent, ...@@ -113,8 +66,9 @@ static int run_getsockopt_test(struct bpf_object *obj, int cg_parent,
* - parent: 0x90 -> 0xA0 * - parent: 0x90 -> 0xA0
*/ */
err = prog_attach(obj, cg_parent, "cgroup/getsockopt", "_getsockopt_parent"); link_parent = bpf_program__attach_cgroup(obj->progs._getsockopt_parent,
if (err) cg_parent);
if (!ASSERT_OK_PTR(link_parent, "cg-attach-getsockopt_parent"))
goto detach; goto detach;
buf = 0x00; buf = 0x00;
...@@ -157,11 +111,8 @@ static int run_getsockopt_test(struct bpf_object *obj, int cg_parent, ...@@ -157,11 +111,8 @@ static int run_getsockopt_test(struct bpf_object *obj, int cg_parent,
* - parent: unexpected 0x40, EPERM * - parent: unexpected 0x40, EPERM
*/ */
err = prog_detach(obj, cg_child, "cgroup/getsockopt", "_getsockopt_child"); bpf_link__destroy(link_child);
if (err) { link_child = NULL;
log_err("Failed to detach child program");
goto detach;
}
buf = 0x00; buf = 0x00;
optlen = 1; optlen = 1;
...@@ -198,15 +149,17 @@ static int run_getsockopt_test(struct bpf_object *obj, int cg_parent, ...@@ -198,15 +149,17 @@ static int run_getsockopt_test(struct bpf_object *obj, int cg_parent,
} }
detach: detach:
prog_detach(obj, cg_child, "cgroup/getsockopt", "_getsockopt_child"); bpf_link__destroy(link_child);
prog_detach(obj, cg_parent, "cgroup/getsockopt", "_getsockopt_parent"); bpf_link__destroy(link_parent);
return err; return err;
} }
static int run_setsockopt_test(struct bpf_object *obj, int cg_parent, static int run_setsockopt_test(struct sockopt_multi *obj, int cg_parent,
int cg_child, int sock_fd) int cg_child, int sock_fd)
{ {
struct bpf_link *link_parent = NULL;
struct bpf_link *link_child = NULL;
socklen_t optlen; socklen_t optlen;
__u8 buf; __u8 buf;
int err; int err;
...@@ -236,8 +189,9 @@ static int run_setsockopt_test(struct bpf_object *obj, int cg_parent, ...@@ -236,8 +189,9 @@ static int run_setsockopt_test(struct bpf_object *obj, int cg_parent,
/* Attach child program and make sure it adds 0x10. */ /* Attach child program and make sure it adds 0x10. */
err = prog_attach(obj, cg_child, "cgroup/setsockopt", "_setsockopt"); link_child = bpf_program__attach_cgroup(obj->progs._setsockopt,
if (err) cg_child);
if (!ASSERT_OK_PTR(link_child, "cg-attach-setsockopt_child"))
goto detach; goto detach;
buf = 0x80; buf = 0x80;
...@@ -263,8 +217,9 @@ static int run_setsockopt_test(struct bpf_object *obj, int cg_parent, ...@@ -263,8 +217,9 @@ static int run_setsockopt_test(struct bpf_object *obj, int cg_parent,
/* Attach parent program and make sure it adds another 0x10. */ /* Attach parent program and make sure it adds another 0x10. */
err = prog_attach(obj, cg_parent, "cgroup/setsockopt", "_setsockopt"); link_parent = bpf_program__attach_cgroup(obj->progs._setsockopt,
if (err) cg_parent);
if (!ASSERT_OK_PTR(link_parent, "cg-attach-setsockopt_parent"))
goto detach; goto detach;
buf = 0x80; buf = 0x80;
...@@ -289,8 +244,8 @@ static int run_setsockopt_test(struct bpf_object *obj, int cg_parent, ...@@ -289,8 +244,8 @@ static int run_setsockopt_test(struct bpf_object *obj, int cg_parent,
} }
detach: detach:
prog_detach(obj, cg_child, "cgroup/setsockopt", "_setsockopt"); bpf_link__destroy(link_child);
prog_detach(obj, cg_parent, "cgroup/setsockopt", "_setsockopt"); bpf_link__destroy(link_parent);
return err; return err;
} }
...@@ -298,9 +253,8 @@ static int run_setsockopt_test(struct bpf_object *obj, int cg_parent, ...@@ -298,9 +253,8 @@ static int run_setsockopt_test(struct bpf_object *obj, int cg_parent,
void test_sockopt_multi(void) void test_sockopt_multi(void)
{ {
int cg_parent = -1, cg_child = -1; int cg_parent = -1, cg_child = -1;
struct bpf_object *obj = NULL; struct sockopt_multi *obj = NULL;
int sock_fd = -1; int sock_fd = -1;
int err = -1;
cg_parent = test__join_cgroup("/parent"); cg_parent = test__join_cgroup("/parent");
if (!ASSERT_GE(cg_parent, 0, "join_cgroup /parent")) if (!ASSERT_GE(cg_parent, 0, "join_cgroup /parent"))
...@@ -310,13 +264,11 @@ void test_sockopt_multi(void) ...@@ -310,13 +264,11 @@ void test_sockopt_multi(void)
if (!ASSERT_GE(cg_child, 0, "join_cgroup /parent/child")) if (!ASSERT_GE(cg_child, 0, "join_cgroup /parent/child"))
goto out; goto out;
obj = bpf_object__open_file("sockopt_multi.bpf.o", NULL); obj = sockopt_multi__open_and_load();
if (!ASSERT_OK_PTR(obj, "obj_load")) if (!ASSERT_OK_PTR(obj, "skel-load"))
goto out; goto out;
err = bpf_object__load(obj); obj->bss->page_size = sysconf(_SC_PAGESIZE);
if (!ASSERT_OK(err, "obj_load"))
goto out;
sock_fd = socket(AF_INET, SOCK_STREAM, 0); sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (!ASSERT_GE(sock_fd, 0, "socket")) if (!ASSERT_GE(sock_fd, 0, "socket"))
...@@ -327,7 +279,7 @@ void test_sockopt_multi(void) ...@@ -327,7 +279,7 @@ void test_sockopt_multi(void)
out: out:
close(sock_fd); close(sock_fd);
bpf_object__close(obj); sockopt_multi__destroy(obj);
close(cg_child); close(cg_child);
close(cg_parent); close(cg_parent);
} }
...@@ -42,6 +42,8 @@ void test_sockopt_qos_to_cc(void) ...@@ -42,6 +42,8 @@ void test_sockopt_qos_to_cc(void)
if (!ASSERT_OK_PTR(skel, "skel")) if (!ASSERT_OK_PTR(skel, "skel"))
goto done; goto done;
skel->bss->page_size = sysconf(_SC_PAGESIZE);
sock_fd = socket(AF_INET6, SOCK_STREAM, 0); sock_fd = socket(AF_INET6, SOCK_STREAM, 0);
if (!ASSERT_GE(sock_fd, 0, "v6 socket open")) if (!ASSERT_GE(sock_fd, 0, "v6 socket open"))
goto done; goto done;
......
...@@ -12,6 +12,7 @@ __u32 invocations = 0; ...@@ -12,6 +12,7 @@ __u32 invocations = 0;
__u32 assertion_error = 0; __u32 assertion_error = 0;
__u32 retval_value = 0; __u32 retval_value = 0;
__u32 ctx_retval_value = 0; __u32 ctx_retval_value = 0;
__u32 page_size = 0;
SEC("cgroup/getsockopt") SEC("cgroup/getsockopt")
int get_retval(struct bpf_sockopt *ctx) int get_retval(struct bpf_sockopt *ctx)
...@@ -20,6 +21,10 @@ int get_retval(struct bpf_sockopt *ctx) ...@@ -20,6 +21,10 @@ int get_retval(struct bpf_sockopt *ctx)
ctx_retval_value = ctx->retval; ctx_retval_value = ctx->retval;
__sync_fetch_and_add(&invocations, 1); __sync_fetch_and_add(&invocations, 1);
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > page_size)
ctx->optlen = 0;
return 1; return 1;
} }
...@@ -31,6 +36,10 @@ int set_eisconn(struct bpf_sockopt *ctx) ...@@ -31,6 +36,10 @@ int set_eisconn(struct bpf_sockopt *ctx)
if (bpf_set_retval(-EISCONN)) if (bpf_set_retval(-EISCONN))
assertion_error = 1; assertion_error = 1;
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > page_size)
ctx->optlen = 0;
return 1; return 1;
} }
...@@ -41,5 +50,9 @@ int clear_retval(struct bpf_sockopt *ctx) ...@@ -41,5 +50,9 @@ int clear_retval(struct bpf_sockopt *ctx)
ctx->retval = 0; ctx->retval = 0;
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > page_size)
ctx->optlen = 0;
return 1; return 1;
} }
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
__u32 invocations = 0; __u32 invocations = 0;
__u32 assertion_error = 0; __u32 assertion_error = 0;
__u32 retval_value = 0; __u32 retval_value = 0;
__u32 page_size = 0;
SEC("cgroup/setsockopt") SEC("cgroup/setsockopt")
int get_retval(struct bpf_sockopt *ctx) int get_retval(struct bpf_sockopt *ctx)
...@@ -18,6 +19,10 @@ int get_retval(struct bpf_sockopt *ctx) ...@@ -18,6 +19,10 @@ int get_retval(struct bpf_sockopt *ctx)
retval_value = bpf_get_retval(); retval_value = bpf_get_retval();
__sync_fetch_and_add(&invocations, 1); __sync_fetch_and_add(&invocations, 1);
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > page_size)
ctx->optlen = 0;
return 1; return 1;
} }
...@@ -29,6 +34,10 @@ int set_eunatch(struct bpf_sockopt *ctx) ...@@ -29,6 +34,10 @@ int set_eunatch(struct bpf_sockopt *ctx)
if (bpf_set_retval(-EUNATCH)) if (bpf_set_retval(-EUNATCH))
assertion_error = 1; assertion_error = 1;
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > page_size)
ctx->optlen = 0;
return 0; return 0;
} }
...@@ -40,6 +49,10 @@ int set_eisconn(struct bpf_sockopt *ctx) ...@@ -40,6 +49,10 @@ int set_eisconn(struct bpf_sockopt *ctx)
if (bpf_set_retval(-EISCONN)) if (bpf_set_retval(-EISCONN))
assertion_error = 1; assertion_error = 1;
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > page_size)
ctx->optlen = 0;
return 0; return 0;
} }
...@@ -48,5 +61,9 @@ int legacy_eperm(struct bpf_sockopt *ctx) ...@@ -48,5 +61,9 @@ int legacy_eperm(struct bpf_sockopt *ctx)
{ {
__sync_fetch_and_add(&invocations, 1); __sync_fetch_and_add(&invocations, 1);
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > page_size)
ctx->optlen = 0;
return 0; return 0;
} }
...@@ -9,6 +9,8 @@ char _license[] SEC("license") = "GPL"; ...@@ -9,6 +9,8 @@ char _license[] SEC("license") = "GPL";
#define CUSTOM_INHERIT2 1 #define CUSTOM_INHERIT2 1
#define CUSTOM_LISTENER 2 #define CUSTOM_LISTENER 2
__u32 page_size = 0;
struct sockopt_inherit { struct sockopt_inherit {
__u8 val; __u8 val;
}; };
...@@ -55,7 +57,7 @@ int _getsockopt(struct bpf_sockopt *ctx) ...@@ -55,7 +57,7 @@ int _getsockopt(struct bpf_sockopt *ctx)
__u8 *optval = ctx->optval; __u8 *optval = ctx->optval;
if (ctx->level != SOL_CUSTOM) if (ctx->level != SOL_CUSTOM)
return 1; /* only interested in SOL_CUSTOM */ goto out; /* only interested in SOL_CUSTOM */
if (optval + 1 > optval_end) if (optval + 1 > optval_end)
return 0; /* EPERM, bounds check */ return 0; /* EPERM, bounds check */
...@@ -70,6 +72,12 @@ int _getsockopt(struct bpf_sockopt *ctx) ...@@ -70,6 +72,12 @@ int _getsockopt(struct bpf_sockopt *ctx)
ctx->optlen = 1; ctx->optlen = 1;
return 1; return 1;
out:
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > page_size)
ctx->optlen = 0;
return 1;
} }
SEC("cgroup/setsockopt") SEC("cgroup/setsockopt")
...@@ -80,7 +88,7 @@ int _setsockopt(struct bpf_sockopt *ctx) ...@@ -80,7 +88,7 @@ int _setsockopt(struct bpf_sockopt *ctx)
__u8 *optval = ctx->optval; __u8 *optval = ctx->optval;
if (ctx->level != SOL_CUSTOM) if (ctx->level != SOL_CUSTOM)
return 1; /* only interested in SOL_CUSTOM */ goto out; /* only interested in SOL_CUSTOM */
if (optval + 1 > optval_end) if (optval + 1 > optval_end)
return 0; /* EPERM, bounds check */ return 0; /* EPERM, bounds check */
...@@ -93,4 +101,10 @@ int _setsockopt(struct bpf_sockopt *ctx) ...@@ -93,4 +101,10 @@ int _setsockopt(struct bpf_sockopt *ctx)
ctx->optlen = -1; ctx->optlen = -1;
return 1; return 1;
out:
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > page_size)
ctx->optlen = 0;
return 1;
} }
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
char _license[] SEC("license") = "GPL"; char _license[] SEC("license") = "GPL";
__u32 page_size = 0;
SEC("cgroup/getsockopt") SEC("cgroup/getsockopt")
int _getsockopt_child(struct bpf_sockopt *ctx) int _getsockopt_child(struct bpf_sockopt *ctx)
{ {
...@@ -12,7 +14,7 @@ int _getsockopt_child(struct bpf_sockopt *ctx) ...@@ -12,7 +14,7 @@ int _getsockopt_child(struct bpf_sockopt *ctx)
__u8 *optval = ctx->optval; __u8 *optval = ctx->optval;
if (ctx->level != SOL_IP || ctx->optname != IP_TOS) if (ctx->level != SOL_IP || ctx->optname != IP_TOS)
return 1; goto out;
if (optval + 1 > optval_end) if (optval + 1 > optval_end)
return 0; /* EPERM, bounds check */ return 0; /* EPERM, bounds check */
...@@ -26,6 +28,12 @@ int _getsockopt_child(struct bpf_sockopt *ctx) ...@@ -26,6 +28,12 @@ int _getsockopt_child(struct bpf_sockopt *ctx)
ctx->optlen = 1; ctx->optlen = 1;
return 1; return 1;
out:
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > page_size)
ctx->optlen = 0;
return 1;
} }
SEC("cgroup/getsockopt") SEC("cgroup/getsockopt")
...@@ -35,7 +43,7 @@ int _getsockopt_parent(struct bpf_sockopt *ctx) ...@@ -35,7 +43,7 @@ int _getsockopt_parent(struct bpf_sockopt *ctx)
__u8 *optval = ctx->optval; __u8 *optval = ctx->optval;
if (ctx->level != SOL_IP || ctx->optname != IP_TOS) if (ctx->level != SOL_IP || ctx->optname != IP_TOS)
return 1; goto out;
if (optval + 1 > optval_end) if (optval + 1 > optval_end)
return 0; /* EPERM, bounds check */ return 0; /* EPERM, bounds check */
...@@ -49,6 +57,12 @@ int _getsockopt_parent(struct bpf_sockopt *ctx) ...@@ -49,6 +57,12 @@ int _getsockopt_parent(struct bpf_sockopt *ctx)
ctx->optlen = 1; ctx->optlen = 1;
return 1; return 1;
out:
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > page_size)
ctx->optlen = 0;
return 1;
} }
SEC("cgroup/setsockopt") SEC("cgroup/setsockopt")
...@@ -58,7 +72,7 @@ int _setsockopt(struct bpf_sockopt *ctx) ...@@ -58,7 +72,7 @@ int _setsockopt(struct bpf_sockopt *ctx)
__u8 *optval = ctx->optval; __u8 *optval = ctx->optval;
if (ctx->level != SOL_IP || ctx->optname != IP_TOS) if (ctx->level != SOL_IP || ctx->optname != IP_TOS)
return 1; goto out;
if (optval + 1 > optval_end) if (optval + 1 > optval_end)
return 0; /* EPERM, bounds check */ return 0; /* EPERM, bounds check */
...@@ -67,4 +81,10 @@ int _setsockopt(struct bpf_sockopt *ctx) ...@@ -67,4 +81,10 @@ int _setsockopt(struct bpf_sockopt *ctx)
ctx->optlen = 1; ctx->optlen = 1;
return 1; return 1;
out:
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > page_size)
ctx->optlen = 0;
return 1;
} }
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
char _license[] SEC("license") = "GPL"; char _license[] SEC("license") = "GPL";
__u32 page_size = 0;
SEC("cgroup/setsockopt") SEC("cgroup/setsockopt")
int sockopt_qos_to_cc(struct bpf_sockopt *ctx) int sockopt_qos_to_cc(struct bpf_sockopt *ctx)
{ {
...@@ -19,7 +21,7 @@ int sockopt_qos_to_cc(struct bpf_sockopt *ctx) ...@@ -19,7 +21,7 @@ int sockopt_qos_to_cc(struct bpf_sockopt *ctx)
char cc_cubic[TCP_CA_NAME_MAX] = "cubic"; char cc_cubic[TCP_CA_NAME_MAX] = "cubic";
if (ctx->level != SOL_IPV6 || ctx->optname != IPV6_TCLASS) if (ctx->level != SOL_IPV6 || ctx->optname != IPV6_TCLASS)
return 1; goto out;
if (optval + 1 > optval_end) if (optval + 1 > optval_end)
return 0; /* EPERM, bounds check */ return 0; /* EPERM, bounds check */
...@@ -36,4 +38,10 @@ int sockopt_qos_to_cc(struct bpf_sockopt *ctx) ...@@ -36,4 +38,10 @@ int sockopt_qos_to_cc(struct bpf_sockopt *ctx)
return 0; return 0;
} }
return 1; return 1;
out:
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > page_size)
ctx->optlen = 0;
return 1;
} }
...@@ -37,7 +37,7 @@ int _getsockopt(struct bpf_sockopt *ctx) ...@@ -37,7 +37,7 @@ int _getsockopt(struct bpf_sockopt *ctx)
/* Bypass AF_NETLINK. */ /* Bypass AF_NETLINK. */
sk = ctx->sk; sk = ctx->sk;
if (sk && sk->family == AF_NETLINK) if (sk && sk->family == AF_NETLINK)
return 1; goto out;
/* Make sure bpf_get_netns_cookie is callable. /* Make sure bpf_get_netns_cookie is callable.
*/ */
...@@ -52,8 +52,7 @@ int _getsockopt(struct bpf_sockopt *ctx) ...@@ -52,8 +52,7 @@ int _getsockopt(struct bpf_sockopt *ctx)
* let next BPF program in the cgroup chain or kernel * let next BPF program in the cgroup chain or kernel
* handle it. * handle it.
*/ */
ctx->optlen = 0; /* bypass optval>PAGE_SIZE */ goto out;
return 1;
} }
if (ctx->level == SOL_SOCKET && ctx->optname == SO_SNDBUF) { if (ctx->level == SOL_SOCKET && ctx->optname == SO_SNDBUF) {
...@@ -61,7 +60,7 @@ int _getsockopt(struct bpf_sockopt *ctx) ...@@ -61,7 +60,7 @@ int _getsockopt(struct bpf_sockopt *ctx)
* let next BPF program in the cgroup chain or kernel * let next BPF program in the cgroup chain or kernel
* handle it. * handle it.
*/ */
return 1; goto out;
} }
if (ctx->level == SOL_TCP && ctx->optname == TCP_CONGESTION) { if (ctx->level == SOL_TCP && ctx->optname == TCP_CONGESTION) {
...@@ -69,7 +68,7 @@ int _getsockopt(struct bpf_sockopt *ctx) ...@@ -69,7 +68,7 @@ int _getsockopt(struct bpf_sockopt *ctx)
* let next BPF program in the cgroup chain or kernel * let next BPF program in the cgroup chain or kernel
* handle it. * handle it.
*/ */
return 1; goto out;
} }
if (ctx->level == SOL_TCP && ctx->optname == TCP_ZEROCOPY_RECEIVE) { if (ctx->level == SOL_TCP && ctx->optname == TCP_ZEROCOPY_RECEIVE) {
...@@ -85,7 +84,7 @@ int _getsockopt(struct bpf_sockopt *ctx) ...@@ -85,7 +84,7 @@ int _getsockopt(struct bpf_sockopt *ctx)
if (((struct tcp_zerocopy_receive *)optval)->address != 0) if (((struct tcp_zerocopy_receive *)optval)->address != 0)
return 0; /* unexpected data */ return 0; /* unexpected data */
return 1; goto out;
} }
if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) { if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) {
...@@ -129,6 +128,12 @@ int _getsockopt(struct bpf_sockopt *ctx) ...@@ -129,6 +128,12 @@ int _getsockopt(struct bpf_sockopt *ctx)
ctx->optlen = 1; ctx->optlen = 1;
return 1; return 1;
out:
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > page_size)
ctx->optlen = 0;
return 1;
} }
SEC("cgroup/setsockopt") SEC("cgroup/setsockopt")
...@@ -142,7 +147,7 @@ int _setsockopt(struct bpf_sockopt *ctx) ...@@ -142,7 +147,7 @@ int _setsockopt(struct bpf_sockopt *ctx)
/* Bypass AF_NETLINK. */ /* Bypass AF_NETLINK. */
sk = ctx->sk; sk = ctx->sk;
if (sk && sk->family == AF_NETLINK) if (sk && sk->family == AF_NETLINK)
return 1; goto out;
/* Make sure bpf_get_netns_cookie is callable. /* Make sure bpf_get_netns_cookie is callable.
*/ */
...@@ -224,4 +229,10 @@ int _setsockopt(struct bpf_sockopt *ctx) ...@@ -224,4 +229,10 @@ int _setsockopt(struct bpf_sockopt *ctx)
*/ */
return 1; return 1;
out:
/* optval larger than PAGE_SIZE use kernel's buffer. */
if (ctx->optlen > page_size)
ctx->optlen = 0;
return 1;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment