In the initial implementation in '7ae4eb14c5f9 ("bpf: Add ksock kfuncs")', it was decided to let the ksock kfuncs go through the LSM hooks instead of by-passing them, and thus to disallow the use of the kfuncs from asynchronous workqueues as the credentials would be different than the task context that triggered the kfuncs. After some discussions, in particular at Plumbers 2026 at the presentation introducing this new ksock API[^1], it was agreed that by-passing the LSM from socket created by BPF programs should be okay and thus that the process context switch from kthread executing the workqueues wouldn't be an issue. Note also that in some BPF prog execution context in which we could extend the ksock kfuncs, like networking executing via softirq, the current task might be some unrelated kernel thread. For now, the ksock kfuncs still trigger the LSM hooks but we relax the constraint on not using the workqueues as it will be useful for users triggering send() from non-sleepable programs (like tracing, support will be extended in future patches). Also adapt test_ksock_wq() added in 'c93cbdb13f99 ("selftests/bpf: Add ksock test for async callback guard")' to test that the kfuncs are now allowed and work when called from workqueues. [^1]: https://lpc.events/event/20/contributions/2430/ Signed-off-by: Mahe Tardy --- net/core/bpf_ksock.c | 25 ------------------- .../selftests/bpf/prog_tests/ksock_wq.c | 23 ++++++++++++++--- tools/testing/selftests/bpf/progs/ksock_wq.c | 24 +++++++++++++++--- 3 files changed, 40 insertions(+), 32 deletions(-) diff --git a/net/core/bpf_ksock.c b/net/core/bpf_ksock.c index e9943aeeadd3..21d5fd1eb403 100644 --- a/net/core/bpf_ksock.c +++ b/net/core/bpf_ksock.c @@ -37,15 +37,6 @@ static void ksock_release_work_fn(struct work_struct *work) kfree(ks); } -static bool bpf_ksock_has_user_task_context(void) -{ - /* - * Task work can run from do_exit() after exit_nsproxy_namespaces() - * cleared current->nsproxy, while current is still not a kthread. - */ - return !(current->flags & PF_KTHREAD) && current->nsproxy; -} - __bpf_kfunc_start_defs(); /** @@ -73,16 +64,6 @@ bpf_ksock_create(const struct bpf_ksock_create_opts *opts, u32 opts__sz, struct bpf_ksock *ks; int err; - /* - * sock_create() derives the network namespace, credentials, and cgroup - * from current. Kernel threads, including BPF workqueue callbacks, do - * not carry the context of the task that invoked the BPF program. - */ - if (!bpf_ksock_has_user_task_context()) { - err = -EOPNOTSUPP; - goto err_out; - } - if (!opts || opts__sz != sizeof(struct bpf_ksock_create_opts)) { err = -EINVAL; goto err_out; @@ -165,9 +146,6 @@ __bpf_kfunc int bpf_ksock_connect(struct bpf_ksock *ks, struct sockaddr_storage sa; int addrlen; - if (!bpf_ksock_has_user_task_context()) - return -EOPNOTSUPP; - if (!addr || addr__sz != sizeof(*addr)) return -EINVAL; @@ -247,9 +225,6 @@ __bpf_kfunc int bpf_ksock_send(struct bpf_ksock *ks, const void *data, }; int ret; - if (!bpf_ksock_has_user_task_context()) - return -EOPNOTSUPP; - ret = kernel_sendmsg(ks->sock, &msg, &iov, 1, data__sz); return ret; diff --git a/tools/testing/selftests/bpf/prog_tests/ksock_wq.c b/tools/testing/selftests/bpf/prog_tests/ksock_wq.c index d6dc20b8f95b..708e42abc57e 100644 --- a/tools/testing/selftests/bpf/prog_tests/ksock_wq.c +++ b/tools/testing/selftests/bpf/prog_tests/ksock_wq.c @@ -4,6 +4,7 @@ #include #include "test_progs.h" +#include "network_helpers.h" #include "ksock_wq.skel.h" #define CALLBACK_WAIT_RETRIES 1000 @@ -13,13 +14,22 @@ void test_ksock_wq(void) { LIBBPF_OPTS(bpf_test_run_opts, opts); struct ksock_wq *skel; + char recv_data[sizeof(skel->data->send_data)]; u32 callback_done; - int err, i; + int err, i, port, rfd; skel = ksock_wq__open_and_load(); if (!ASSERT_OK_PTR(skel, "ksock_wq open and load")) return; + rfd = start_server(AF_INET, SOCK_DGRAM, "127.0.0.1", 0, 1000); + if (!ASSERT_OK_FD(rfd, "receiver socket")) + goto out; + port = get_socket_local_port(rfd); + if (!ASSERT_GE(port, 0, "receiver port")) + goto out; + skel->bss->remote_port = port; + err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.ksock_wq_start), &opts); if (!ASSERT_OK(err, "run ksock_wq_start")) @@ -37,9 +47,16 @@ void test_ksock_wq(void) if (!ASSERT_EQ(callback_done, 1, "workqueue callback completed")) goto out; - ASSERT_EQ(skel->bss->create_err, -EOPNOTSUPP, - "workqueue create rejected"); + if (!ASSERT_EQ(skel->bss->callback_ret, sizeof(recv_data), + "workqueue send bytes")) + goto out; + err = recv(rfd, recv_data, sizeof(recv_data), 0); + if (ASSERT_EQ(err, sizeof(recv_data), "recv len")) + ASSERT_MEMEQ(recv_data, skel->data->send_data, sizeof(recv_data), + "payload match"); out: + if (rfd >= 0) + close(rfd); ksock_wq__destroy(skel); } diff --git a/tools/testing/selftests/bpf/progs/ksock_wq.c b/tools/testing/selftests/bpf/progs/ksock_wq.c index 16a1873d132e..0633ae048518 100644 --- a/tools/testing/selftests/bpf/progs/ksock_wq.c +++ b/tools/testing/selftests/bpf/progs/ksock_wq.c @@ -2,6 +2,7 @@ /* Copyright (c) 2026 Isovalent */ #include "vmlinux.h" +#include #include #include "bpf_experimental.h" #include "bpf_tracing_net.h" @@ -19,7 +20,10 @@ struct { __type(value, struct ksock_wq_value); } work_map SEC(".maps"); -int create_err; +char send_data[] = "hello from bpf workqueue"; + +__be16 remote_port; +int callback_ret; u32 callback_done; static int ksock_wq_callback(void *map, int *key, void *value) @@ -29,13 +33,25 @@ static int ksock_wq_callback(void *map, int *key, void *value) .type = SOCK_DGRAM, .protocol = IPPROTO_UDP, }; + union bpf_ksock_addr addr = { + .sin = { + .sin_family = AF_INET, + .sin_port = remote_port, + .sin_addr.s_addr = bpf_htonl(0x7f000001), + }, + }; struct bpf_ksock *ks; int err = 0; ks = bpf_ksock_create(&opts, sizeof(opts), &err); - if (ks) - bpf_ksock_release(ks); - create_err = err; + if (!ks) + goto out; + err = bpf_ksock_connect(ks, &addr, sizeof(addr)); + if (!err) + err = bpf_ksock_send(ks, send_data, sizeof(send_data)); + bpf_ksock_release(ks); +out: + callback_ret = err; __sync_fetch_and_add(&callback_done, 1); return 0; } -- 2.43.0