mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-06 02:50:43 +00:00
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: Martin KaFai Lau <martin.lau@kernel.org>
This commit is contained in:
commit
79b3604d62
13 changed files with 328 additions and 140 deletions
|
@ -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``.
|
||||
|
||||
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
|
||||
=======
|
||||
|
||||
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
|
||||
of BPF program that handles socket options.
|
||||
|
|
|
@ -1826,6 +1826,12 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level,
|
|||
ret = 1;
|
||||
} else if (ctx.optlen > max_optlen || ctx.optlen < -1) {
|
||||
/* 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;
|
||||
} else {
|
||||
/* optlen within bounds, run kernel handler */
|
||||
|
@ -1881,8 +1887,10 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level,
|
|||
.optname = optname,
|
||||
.current_task = current,
|
||||
};
|
||||
int orig_optlen;
|
||||
int ret;
|
||||
|
||||
orig_optlen = max_optlen;
|
||||
ctx.optlen = max_optlen;
|
||||
max_optlen = sockopt_alloc_buf(&ctx, max_optlen, &buf);
|
||||
if (max_optlen < 0)
|
||||
|
@ -1905,6 +1913,7 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level,
|
|||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
orig_optlen = ctx.optlen;
|
||||
|
||||
if (copy_from_user(ctx.optval, optval,
|
||||
min(ctx.optlen, max_optlen)) != 0) {
|
||||
|
@ -1922,6 +1931,12 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level,
|
|||
goto out;
|
||||
|
||||
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;
|
||||
goto out;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ static void test_setsockopt_set(int cgroup_fd, int sock_fd)
|
|||
if (!ASSERT_OK_PTR(obj, "skel-load"))
|
||||
return;
|
||||
|
||||
obj->bss->page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
/* Attach setsockopt that sets EUNATCH, assert that
|
||||
* 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)
|
|||
if (!ASSERT_OK_PTR(obj, "skel-load"))
|
||||
return;
|
||||
|
||||
obj->bss->page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
/* Attach setsockopt that sets EUNATCH, and one that gets the
|
||||
* 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)
|
|||
if (!ASSERT_OK_PTR(obj, "skel-load"))
|
||||
return;
|
||||
|
||||
obj->bss->page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
/* Attach setsockopt that gets the previously set errno.
|
||||
* 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)
|
|||
if (!ASSERT_OK_PTR(obj, "skel-load"))
|
||||
return;
|
||||
|
||||
obj->bss->page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
/* Attach setsockopt that gets the previously set errno, and then
|
||||
* 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.
|
||||
|
@ -177,6 +185,8 @@ static void test_setsockopt_override(int cgroup_fd, int sock_fd)
|
|||
if (!ASSERT_OK_PTR(obj, "skel-load"))
|
||||
return;
|
||||
|
||||
obj->bss->page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
/* Attach setsockopt that sets EUNATCH, then one that sets EISCONN,
|
||||
* and then one that gets the exported errno. Assert both the syscall
|
||||
* and the helper sees the last set errno.
|
||||
|
@ -224,6 +234,8 @@ static void test_setsockopt_legacy_eperm(int cgroup_fd, int sock_fd)
|
|||
if (!ASSERT_OK_PTR(obj, "skel-load"))
|
||||
return;
|
||||
|
||||
obj->bss->page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
/* Attach setsockopt that return a reject without setting errno
|
||||
* (legacy reject), and one that gets the errno. Assert that for
|
||||
* 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)
|
|||
if (!ASSERT_OK_PTR(obj, "skel-load"))
|
||||
return;
|
||||
|
||||
obj->bss->page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
/* Attach setsockopt that sets EUNATCH, then one that return a reject
|
||||
* without setting errno, and then one that gets the exported errno.
|
||||
* 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)
|
|||
if (!ASSERT_OK_PTR(obj, "skel-load"))
|
||||
return;
|
||||
|
||||
obj->bss->page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
/* Attach getsockopt that gets previously set errno. Assert that the
|
||||
* 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)
|
|||
if (!ASSERT_OK_PTR(obj, "skel-load"))
|
||||
return;
|
||||
|
||||
obj->bss->page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
/* Attach getsockopt that sets retval to -EISCONN. Assert that this
|
||||
* overrides the value from kernel.
|
||||
*/
|
||||
|
@ -396,6 +414,8 @@ static void test_getsockopt_retval_sync(int cgroup_fd, int sock_fd)
|
|||
if (!ASSERT_OK_PTR(obj, "skel-load"))
|
||||
return;
|
||||
|
||||
obj->bss->page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
/* Attach getsockopt that sets retval to -EISCONN, and one that clears
|
||||
* ctx retval. Assert that the clearing ctx retval is synced to helper
|
||||
* and clears any errors both from kernel and BPF..
|
||||
|
|
|
@ -5,10 +5,15 @@
|
|||
static char bpf_log_buf[4096];
|
||||
static bool verbose;
|
||||
|
||||
#ifndef PAGE_SIZE
|
||||
#define PAGE_SIZE 4096
|
||||
#endif
|
||||
|
||||
enum sockopt_test_error {
|
||||
OK = 0,
|
||||
DENY_LOAD,
|
||||
DENY_ATTACH,
|
||||
EOPNOTSUPP_GETSOCKOPT,
|
||||
EPERM_GETSOCKOPT,
|
||||
EFAULT_GETSOCKOPT,
|
||||
EPERM_SETSOCKOPT,
|
||||
|
@ -273,10 +278,31 @@ static struct sockopt_test {
|
|||
.error = EFAULT_GETSOCKOPT,
|
||||
},
|
||||
{
|
||||
.descr = "getsockopt: deny arbitrary ctx->retval",
|
||||
.descr = "getsockopt: ignore >PAGE_SIZE optlen",
|
||||
.insns = {
|
||||
/* ctx->retval = 123 */
|
||||
BPF_MOV64_IMM(BPF_REG_0, 123),
|
||||
/* 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, 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,
|
||||
offsetof(struct bpf_sockopt, retval)),
|
||||
|
||||
|
@ -287,9 +313,11 @@ static struct sockopt_test {
|
|||
.attach_type = BPF_CGROUP_GETSOCKOPT,
|
||||
.expected_attach_type = BPF_CGROUP_GETSOCKOPT,
|
||||
|
||||
.get_optlen = 64,
|
||||
|
||||
.error = EFAULT_GETSOCKOPT,
|
||||
.get_level = 1234,
|
||||
.get_optname = 5678,
|
||||
.get_optval = {}, /* the changes are ignored */
|
||||
.get_optlen = PAGE_SIZE + 1,
|
||||
.error = EOPNOTSUPP_GETSOCKOPT,
|
||||
},
|
||||
{
|
||||
.descr = "getsockopt: support smaller ctx->optlen",
|
||||
|
@ -648,6 +676,45 @@ static struct sockopt_test {
|
|||
|
||||
.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",
|
||||
.insns = {
|
||||
|
@ -906,6 +973,13 @@ static int run_test(int cgroup_fd, struct sockopt_test *test)
|
|||
}
|
||||
|
||||
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,
|
||||
test->set_optval, test->set_optlen);
|
||||
if (err) {
|
||||
|
@ -921,7 +995,15 @@ static int run_test(int cgroup_fd, struct sockopt_test *test)
|
|||
}
|
||||
|
||||
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);
|
||||
memset(optval, 0, test->get_optlen);
|
||||
socklen_t optlen = test->get_optlen;
|
||||
socklen_t expected_get_optlen = test->get_optlen_ret ?:
|
||||
test->get_optlen;
|
||||
|
@ -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,
|
||||
optval, &optlen);
|
||||
if (err) {
|
||||
if (errno == EOPNOTSUPP && test->error == EOPNOTSUPP_GETSOCKOPT)
|
||||
goto free_optval;
|
||||
if (errno == EPERM && test->error == EPERM_GETSOCKOPT)
|
||||
goto free_optval;
|
||||
if (errno == EFAULT && test->error == EFAULT_GETSOCKOPT)
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#include <test_progs.h>
|
||||
#include "cgroup_helpers.h"
|
||||
|
||||
#include "sockopt_inherit.skel.h"
|
||||
|
||||
#define SOL_CUSTOM 0xdeadbeef
|
||||
#define CUSTOM_INHERIT1 0
|
||||
#define CUSTOM_INHERIT2 1
|
||||
|
@ -132,58 +134,30 @@ static int start_server(void)
|
|||
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)
|
||||
{
|
||||
struct bpf_link *link_getsockopt = NULL;
|
||||
struct bpf_link *link_setsockopt = NULL;
|
||||
int server_fd = -1, client_fd;
|
||||
struct bpf_object *obj;
|
||||
struct sockopt_inherit *obj;
|
||||
void *server_err;
|
||||
pthread_t tid;
|
||||
int err;
|
||||
|
||||
obj = bpf_object__open_file("sockopt_inherit.bpf.o", NULL);
|
||||
if (!ASSERT_OK_PTR(obj, "obj_open"))
|
||||
obj = sockopt_inherit__open_and_load();
|
||||
if (!ASSERT_OK_PTR(obj, "skel-load"))
|
||||
return;
|
||||
|
||||
err = bpf_object__load(obj);
|
||||
if (!ASSERT_OK(err, "obj_load"))
|
||||
obj->bss->page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
link_getsockopt = bpf_program__attach_cgroup(obj->progs._getsockopt,
|
||||
cgroup_fd);
|
||||
if (!ASSERT_OK_PTR(link_getsockopt, "cg-attach-getsockopt"))
|
||||
goto close_bpf_object;
|
||||
|
||||
err = prog_attach(obj, cgroup_fd, "cgroup/getsockopt", "_getsockopt");
|
||||
if (!ASSERT_OK(err, "prog_attach _getsockopt"))
|
||||
goto close_bpf_object;
|
||||
|
||||
err = prog_attach(obj, cgroup_fd, "cgroup/setsockopt", "_setsockopt");
|
||||
if (!ASSERT_OK(err, "prog_attach _setsockopt"))
|
||||
link_setsockopt = bpf_program__attach_cgroup(obj->progs._setsockopt,
|
||||
cgroup_fd);
|
||||
if (!ASSERT_OK_PTR(link_setsockopt, "cg-attach-setsockopt"))
|
||||
goto close_bpf_object;
|
||||
|
||||
server_fd = start_server();
|
||||
|
@ -217,7 +191,10 @@ static void run_test(int cgroup_fd)
|
|||
close_server_fd:
|
||||
close(server_fd);
|
||||
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)
|
||||
|
|
|
@ -2,61 +2,13 @@
|
|||
#include <test_progs.h>
|
||||
#include "cgroup_helpers.h"
|
||||
|
||||
static int prog_attach(struct bpf_object *obj, int cgroup_fd, const char *title, const char *name)
|
||||
{
|
||||
enum bpf_attach_type attach_type;
|
||||
enum bpf_prog_type prog_type;
|
||||
struct bpf_program *prog;
|
||||
int err;
|
||||
#include "sockopt_multi.skel.h"
|
||||
|
||||
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)
|
||||
{
|
||||
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,
|
||||
static int run_getsockopt_test(struct sockopt_multi *obj, int cg_parent,
|
||||
int cg_child, int sock_fd)
|
||||
{
|
||||
struct bpf_link *link_parent = NULL;
|
||||
struct bpf_link *link_child = NULL;
|
||||
socklen_t optlen;
|
||||
__u8 buf;
|
||||
int err;
|
||||
|
@ -89,8 +41,9 @@ static int run_getsockopt_test(struct bpf_object *obj, int cg_parent,
|
|||
* - child: 0x80 -> 0x90
|
||||
*/
|
||||
|
||||
err = prog_attach(obj, cg_child, "cgroup/getsockopt", "_getsockopt_child");
|
||||
if (err)
|
||||
link_child = bpf_program__attach_cgroup(obj->progs._getsockopt_child,
|
||||
cg_child);
|
||||
if (!ASSERT_OK_PTR(link_child, "cg-attach-getsockopt_child"))
|
||||
goto detach;
|
||||
|
||||
buf = 0x00;
|
||||
|
@ -113,8 +66,9 @@ static int run_getsockopt_test(struct bpf_object *obj, int cg_parent,
|
|||
* - parent: 0x90 -> 0xA0
|
||||
*/
|
||||
|
||||
err = prog_attach(obj, cg_parent, "cgroup/getsockopt", "_getsockopt_parent");
|
||||
if (err)
|
||||
link_parent = bpf_program__attach_cgroup(obj->progs._getsockopt_parent,
|
||||
cg_parent);
|
||||
if (!ASSERT_OK_PTR(link_parent, "cg-attach-getsockopt_parent"))
|
||||
goto detach;
|
||||
|
||||
buf = 0x00;
|
||||
|
@ -157,11 +111,8 @@ static int run_getsockopt_test(struct bpf_object *obj, int cg_parent,
|
|||
* - parent: unexpected 0x40, EPERM
|
||||
*/
|
||||
|
||||
err = prog_detach(obj, cg_child, "cgroup/getsockopt", "_getsockopt_child");
|
||||
if (err) {
|
||||
log_err("Failed to detach child program");
|
||||
goto detach;
|
||||
}
|
||||
bpf_link__destroy(link_child);
|
||||
link_child = NULL;
|
||||
|
||||
buf = 0x00;
|
||||
optlen = 1;
|
||||
|
@ -198,15 +149,17 @@ static int run_getsockopt_test(struct bpf_object *obj, int cg_parent,
|
|||
}
|
||||
|
||||
detach:
|
||||
prog_detach(obj, cg_child, "cgroup/getsockopt", "_getsockopt_child");
|
||||
prog_detach(obj, cg_parent, "cgroup/getsockopt", "_getsockopt_parent");
|
||||
bpf_link__destroy(link_child);
|
||||
bpf_link__destroy(link_parent);
|
||||
|
||||
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)
|
||||
{
|
||||
struct bpf_link *link_parent = NULL;
|
||||
struct bpf_link *link_child = NULL;
|
||||
socklen_t optlen;
|
||||
__u8 buf;
|
||||
int err;
|
||||
|
@ -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. */
|
||||
|
||||
err = prog_attach(obj, cg_child, "cgroup/setsockopt", "_setsockopt");
|
||||
if (err)
|
||||
link_child = bpf_program__attach_cgroup(obj->progs._setsockopt,
|
||||
cg_child);
|
||||
if (!ASSERT_OK_PTR(link_child, "cg-attach-setsockopt_child"))
|
||||
goto detach;
|
||||
|
||||
buf = 0x80;
|
||||
|
@ -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. */
|
||||
|
||||
err = prog_attach(obj, cg_parent, "cgroup/setsockopt", "_setsockopt");
|
||||
if (err)
|
||||
link_parent = bpf_program__attach_cgroup(obj->progs._setsockopt,
|
||||
cg_parent);
|
||||
if (!ASSERT_OK_PTR(link_parent, "cg-attach-setsockopt_parent"))
|
||||
goto detach;
|
||||
|
||||
buf = 0x80;
|
||||
|
@ -289,8 +244,8 @@ static int run_setsockopt_test(struct bpf_object *obj, int cg_parent,
|
|||
}
|
||||
|
||||
detach:
|
||||
prog_detach(obj, cg_child, "cgroup/setsockopt", "_setsockopt");
|
||||
prog_detach(obj, cg_parent, "cgroup/setsockopt", "_setsockopt");
|
||||
bpf_link__destroy(link_child);
|
||||
bpf_link__destroy(link_parent);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -298,9 +253,8 @@ static int run_setsockopt_test(struct bpf_object *obj, int cg_parent,
|
|||
void test_sockopt_multi(void)
|
||||
{
|
||||
int cg_parent = -1, cg_child = -1;
|
||||
struct bpf_object *obj = NULL;
|
||||
struct sockopt_multi *obj = NULL;
|
||||
int sock_fd = -1;
|
||||
int err = -1;
|
||||
|
||||
cg_parent = test__join_cgroup("/parent");
|
||||
if (!ASSERT_GE(cg_parent, 0, "join_cgroup /parent"))
|
||||
|
@ -310,13 +264,11 @@ void test_sockopt_multi(void)
|
|||
if (!ASSERT_GE(cg_child, 0, "join_cgroup /parent/child"))
|
||||
goto out;
|
||||
|
||||
obj = bpf_object__open_file("sockopt_multi.bpf.o", NULL);
|
||||
if (!ASSERT_OK_PTR(obj, "obj_load"))
|
||||
obj = sockopt_multi__open_and_load();
|
||||
if (!ASSERT_OK_PTR(obj, "skel-load"))
|
||||
goto out;
|
||||
|
||||
err = bpf_object__load(obj);
|
||||
if (!ASSERT_OK(err, "obj_load"))
|
||||
goto out;
|
||||
obj->bss->page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (!ASSERT_GE(sock_fd, 0, "socket"))
|
||||
|
@ -327,7 +279,7 @@ void test_sockopt_multi(void)
|
|||
|
||||
out:
|
||||
close(sock_fd);
|
||||
bpf_object__close(obj);
|
||||
sockopt_multi__destroy(obj);
|
||||
close(cg_child);
|
||||
close(cg_parent);
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ void test_sockopt_qos_to_cc(void)
|
|||
if (!ASSERT_OK_PTR(skel, "skel"))
|
||||
goto done;
|
||||
|
||||
skel->bss->page_size = sysconf(_SC_PAGESIZE);
|
||||
|
||||
sock_fd = socket(AF_INET6, SOCK_STREAM, 0);
|
||||
if (!ASSERT_GE(sock_fd, 0, "v6 socket open"))
|
||||
goto done;
|
||||
|
|
|
@ -12,6 +12,7 @@ __u32 invocations = 0;
|
|||
__u32 assertion_error = 0;
|
||||
__u32 retval_value = 0;
|
||||
__u32 ctx_retval_value = 0;
|
||||
__u32 page_size = 0;
|
||||
|
||||
SEC("cgroup/getsockopt")
|
||||
int get_retval(struct bpf_sockopt *ctx)
|
||||
|
@ -20,6 +21,10 @@ int get_retval(struct bpf_sockopt *ctx)
|
|||
ctx_retval_value = ctx->retval;
|
||||
__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;
|
||||
}
|
||||
|
||||
|
@ -31,6 +36,10 @@ int set_eisconn(struct bpf_sockopt *ctx)
|
|||
if (bpf_set_retval(-EISCONN))
|
||||
assertion_error = 1;
|
||||
|
||||
/* optval larger than PAGE_SIZE use kernel's buffer. */
|
||||
if (ctx->optlen > page_size)
|
||||
ctx->optlen = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -41,5 +50,9 @@ int clear_retval(struct bpf_sockopt *ctx)
|
|||
|
||||
ctx->retval = 0;
|
||||
|
||||
/* optval larger than PAGE_SIZE use kernel's buffer. */
|
||||
if (ctx->optlen > page_size)
|
||||
ctx->optlen = 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
__u32 invocations = 0;
|
||||
__u32 assertion_error = 0;
|
||||
__u32 retval_value = 0;
|
||||
__u32 page_size = 0;
|
||||
|
||||
SEC("cgroup/setsockopt")
|
||||
int get_retval(struct bpf_sockopt *ctx)
|
||||
|
@ -18,6 +19,10 @@ int get_retval(struct bpf_sockopt *ctx)
|
|||
retval_value = bpf_get_retval();
|
||||
__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;
|
||||
}
|
||||
|
||||
|
@ -29,6 +34,10 @@ int set_eunatch(struct bpf_sockopt *ctx)
|
|||
if (bpf_set_retval(-EUNATCH))
|
||||
assertion_error = 1;
|
||||
|
||||
/* optval larger than PAGE_SIZE use kernel's buffer. */
|
||||
if (ctx->optlen > page_size)
|
||||
ctx->optlen = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -40,6 +49,10 @@ int set_eisconn(struct bpf_sockopt *ctx)
|
|||
if (bpf_set_retval(-EISCONN))
|
||||
assertion_error = 1;
|
||||
|
||||
/* optval larger than PAGE_SIZE use kernel's buffer. */
|
||||
if (ctx->optlen > page_size)
|
||||
ctx->optlen = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -48,5 +61,9 @@ int legacy_eperm(struct bpf_sockopt *ctx)
|
|||
{
|
||||
__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;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ char _license[] SEC("license") = "GPL";
|
|||
#define CUSTOM_INHERIT2 1
|
||||
#define CUSTOM_LISTENER 2
|
||||
|
||||
__u32 page_size = 0;
|
||||
|
||||
struct sockopt_inherit {
|
||||
__u8 val;
|
||||
};
|
||||
|
@ -55,7 +57,7 @@ int _getsockopt(struct bpf_sockopt *ctx)
|
|||
__u8 *optval = ctx->optval;
|
||||
|
||||
if (ctx->level != SOL_CUSTOM)
|
||||
return 1; /* only interested in SOL_CUSTOM */
|
||||
goto out; /* only interested in SOL_CUSTOM */
|
||||
|
||||
if (optval + 1 > optval_end)
|
||||
return 0; /* EPERM, bounds check */
|
||||
|
@ -70,6 +72,12 @@ int _getsockopt(struct bpf_sockopt *ctx)
|
|||
ctx->optlen = 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")
|
||||
|
@ -80,7 +88,7 @@ int _setsockopt(struct bpf_sockopt *ctx)
|
|||
__u8 *optval = ctx->optval;
|
||||
|
||||
if (ctx->level != SOL_CUSTOM)
|
||||
return 1; /* only interested in SOL_CUSTOM */
|
||||
goto out; /* only interested in SOL_CUSTOM */
|
||||
|
||||
if (optval + 1 > optval_end)
|
||||
return 0; /* EPERM, bounds check */
|
||||
|
@ -93,4 +101,10 @@ int _setsockopt(struct bpf_sockopt *ctx)
|
|||
ctx->optlen = -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 @@
|
|||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
__u32 page_size = 0;
|
||||
|
||||
SEC("cgroup/getsockopt")
|
||||
int _getsockopt_child(struct bpf_sockopt *ctx)
|
||||
{
|
||||
|
@ -12,7 +14,7 @@ int _getsockopt_child(struct bpf_sockopt *ctx)
|
|||
__u8 *optval = ctx->optval;
|
||||
|
||||
if (ctx->level != SOL_IP || ctx->optname != IP_TOS)
|
||||
return 1;
|
||||
goto out;
|
||||
|
||||
if (optval + 1 > optval_end)
|
||||
return 0; /* EPERM, bounds check */
|
||||
|
@ -26,6 +28,12 @@ int _getsockopt_child(struct bpf_sockopt *ctx)
|
|||
ctx->optlen = 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")
|
||||
|
@ -35,7 +43,7 @@ int _getsockopt_parent(struct bpf_sockopt *ctx)
|
|||
__u8 *optval = ctx->optval;
|
||||
|
||||
if (ctx->level != SOL_IP || ctx->optname != IP_TOS)
|
||||
return 1;
|
||||
goto out;
|
||||
|
||||
if (optval + 1 > optval_end)
|
||||
return 0; /* EPERM, bounds check */
|
||||
|
@ -49,6 +57,12 @@ int _getsockopt_parent(struct bpf_sockopt *ctx)
|
|||
ctx->optlen = 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")
|
||||
|
@ -58,7 +72,7 @@ int _setsockopt(struct bpf_sockopt *ctx)
|
|||
__u8 *optval = ctx->optval;
|
||||
|
||||
if (ctx->level != SOL_IP || ctx->optname != IP_TOS)
|
||||
return 1;
|
||||
goto out;
|
||||
|
||||
if (optval + 1 > optval_end)
|
||||
return 0; /* EPERM, bounds check */
|
||||
|
@ -67,4 +81,10 @@ int _setsockopt(struct bpf_sockopt *ctx)
|
|||
ctx->optlen = 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 @@
|
|||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
__u32 page_size = 0;
|
||||
|
||||
SEC("cgroup/setsockopt")
|
||||
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";
|
||||
|
||||
if (ctx->level != SOL_IPV6 || ctx->optname != IPV6_TCLASS)
|
||||
return 1;
|
||||
goto out;
|
||||
|
||||
if (optval + 1 > optval_end)
|
||||
return 0; /* EPERM, bounds check */
|
||||
|
@ -36,4 +38,10 @@ int sockopt_qos_to_cc(struct bpf_sockopt *ctx)
|
|||
return 0;
|
||||
}
|
||||
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)
|
|||
/* Bypass AF_NETLINK. */
|
||||
sk = ctx->sk;
|
||||
if (sk && sk->family == AF_NETLINK)
|
||||
return 1;
|
||||
goto out;
|
||||
|
||||
/* Make sure bpf_get_netns_cookie is callable.
|
||||
*/
|
||||
|
@ -52,8 +52,7 @@ int _getsockopt(struct bpf_sockopt *ctx)
|
|||
* let next BPF program in the cgroup chain or kernel
|
||||
* handle it.
|
||||
*/
|
||||
ctx->optlen = 0; /* bypass optval>PAGE_SIZE */
|
||||
return 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ctx->level == SOL_SOCKET && ctx->optname == SO_SNDBUF) {
|
||||
|
@ -61,7 +60,7 @@ int _getsockopt(struct bpf_sockopt *ctx)
|
|||
* let next BPF program in the cgroup chain or kernel
|
||||
* handle it.
|
||||
*/
|
||||
return 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ctx->level == SOL_TCP && ctx->optname == TCP_CONGESTION) {
|
||||
|
@ -69,7 +68,7 @@ int _getsockopt(struct bpf_sockopt *ctx)
|
|||
* let next BPF program in the cgroup chain or kernel
|
||||
* handle it.
|
||||
*/
|
||||
return 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ctx->level == SOL_TCP && ctx->optname == TCP_ZEROCOPY_RECEIVE) {
|
||||
|
@ -85,7 +84,7 @@ int _getsockopt(struct bpf_sockopt *ctx)
|
|||
if (((struct tcp_zerocopy_receive *)optval)->address != 0)
|
||||
return 0; /* unexpected data */
|
||||
|
||||
return 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (ctx->level == SOL_IP && ctx->optname == IP_FREEBIND) {
|
||||
|
@ -129,6 +128,12 @@ int _getsockopt(struct bpf_sockopt *ctx)
|
|||
ctx->optlen = 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")
|
||||
|
@ -142,7 +147,7 @@ int _setsockopt(struct bpf_sockopt *ctx)
|
|||
/* Bypass AF_NETLINK. */
|
||||
sk = ctx->sk;
|
||||
if (sk && sk->family == AF_NETLINK)
|
||||
return 1;
|
||||
goto out;
|
||||
|
||||
/* Make sure bpf_get_netns_cookie is callable.
|
||||
*/
|
||||
|
@ -224,4 +229,10 @@ int _setsockopt(struct bpf_sockopt *ctx)
|
|||
*/
|
||||
|
||||
return 1;
|
||||
|
||||
out:
|
||||
/* optval larger than PAGE_SIZE use kernel's buffer. */
|
||||
if (ctx->optlen > page_size)
|
||||
ctx->optlen = 0;
|
||||
return 1;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue