* [PATCH bpf-next v3] bpf: allow ksock connect/send calls from wq
@ 2026-10-14 9:14 Mahe Tardy
2026-10-14 9:26 ` sashiko-bot
0 siblings, 1 reply; 3+ messages in thread
From: Mahe Tardy @ 2026-10-14 9:14 UTC (permalink / raw)
To: bpf
Cc: andrii, ast, daniel, davem, eddyz87, edumazet, john.fastabend,
kuba, liamwisehart, martin.lau, pabeni, song, netdev, sdf.kernel,
ameryhung, kuniyu, memxor, jiayuan.chen, Mahe Tardy
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.
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 connect() or 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 connect() and
send() kfuncs are now allowed and work when called from workqueues.
Note that bpf_ksock_create() still checks for workqueue context
execution and makes sure the current->nsproxy is defined as we rely on
the network namespace to be defined (could be NULL in the case do_exit()
was called on the task before the wq was scheduled) and to be the one
from the task that triggered the SYSCALL bpf prog.
[^1]: https://lpc.events/event/20/contributions/2430/
Signed-off-by: Mahe Tardy <mahe.tardy@gmail.com>
---
v2: https://lore.kernel.org/bpf/sashiko-outbox-170341@kernel.org/
net/core/bpf_ksock.c | 27 +------
.../selftests/bpf/prog_tests/ksock_wq.c | 29 +++++--
tools/testing/selftests/bpf/progs/ksock_wq.c | 76 +++++++++++++++----
3 files changed, 91 insertions(+), 41 deletions(-)
diff --git a/net/core/bpf_ksock.c b/net/core/bpf_ksock.c
index e9943aeeadd3..f74968397798 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();
/**
@@ -59,7 +50,7 @@ __bpf_kfunc_start_defs();
* This function may sleep (sock_create), so it can only be used
* in sleepable BPF programs (SYSCALL).
* It cannot be called from a BPF workqueue callback because that callback
- * does not retain the invoking task's namespace or security context.
+ * does not retain the invoking task's namespace.
*
* @opts: Pointer to struct bpf_ksock_create_opts with socket parameters.
* @opts__sz: Size of the opts struct.
@@ -73,13 +64,9 @@ 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;
+ /* sock_create() derives the network namespace from current. */
+ if ((current->flags & PF_KTHREAD) || !current->nsproxy) {
+ err = -EPERM;
goto err_out;
}
@@ -165,9 +152,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 +231,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..cb50f9a1be2e 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 <unistd.h>
#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"))
@@ -28,18 +38,27 @@ void test_ksock_wq(void)
goto out;
for (i = 0; i < CALLBACK_WAIT_RETRIES; i++) {
- if (__atomic_load_n(&skel->bss->callback_done, __ATOMIC_ACQUIRE))
+ if (__atomic_load_n(&skel->bss->callback_done, __ATOMIC_ACQUIRE) == 2)
break;
usleep(CALLBACK_WAIT_US);
}
callback_done = __atomic_load_n(&skel->bss->callback_done,
__ATOMIC_ACQUIRE);
- if (!ASSERT_EQ(callback_done, 1, "workqueue callback completed"))
+ if (!ASSERT_EQ(callback_done, 2, "workqueue callbacks completed"))
goto out;
- ASSERT_EQ(skel->bss->create_err, -EOPNOTSUPP,
- "workqueue create rejected");
+ ASSERT_EQ(skel->bss->create_ret, -EPERM, "workqueue create rejected");
+
+ if (!ASSERT_EQ(skel->bss->connect_send_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..290736e219e1 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 <bpf/bpf_endian.h>
#include <bpf/bpf_helpers.h>
#include "bpf_experimental.h"
#include "bpf_tracing_net.h"
@@ -14,15 +15,19 @@ struct ksock_wq_value {
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
- __uint(max_entries, 1);
+ __uint(max_entries, 2);
__type(key, u32);
__type(value, struct ksock_wq_value);
} work_map SEC(".maps");
-int create_err;
+char send_data[] = "hello from bpf workqueue";
+
+__be16 remote_port;
+int connect_send_ret;
+int create_ret;
u32 callback_done;
-static int ksock_wq_callback(void *map, int *key, void *value)
+static int ksock_wq_create(void *map, int *key, void *value)
{
struct bpf_ksock_create_opts opts = {
.family = AF_INET,
@@ -35,7 +40,32 @@ static int ksock_wq_callback(void *map, int *key, void *value)
ks = bpf_ksock_create(&opts, sizeof(opts), &err);
if (ks)
bpf_ksock_release(ks);
- create_err = err;
+ create_ret = err;
+ __sync_fetch_and_add(&callback_done, 1);
+ return 0;
+}
+
+static int ksock_wq_connect_send(void *map, int *key, void *value)
+{
+ 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 = -ENOENT;
+
+ ks = ksock_ctx_get();
+ 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:
+ connect_send_ret = err;
__sync_fetch_and_add(&callback_done, 1);
return 0;
}
@@ -43,20 +73,40 @@ static int ksock_wq_callback(void *map, int *key, void *value)
SEC("syscall")
int ksock_wq_start(void *ctx)
{
+ struct bpf_ksock_create_opts opts = {
+ .family = AF_INET,
+ .type = SOCK_DGRAM,
+ .protocol = IPPROTO_UDP,
+ };
struct ksock_wq_value *value;
- u32 key = 0;
- int err;
+ struct bpf_ksock *ks;
+ u64 key = 0;
+ int err = 0;
- value = bpf_map_lookup_elem(&work_map, &key);
- if (!value)
- return -ENOENT;
- err = bpf_wq_init(&value->work, &work_map, 0);
- if (err)
+ ks = bpf_ksock_create(&opts, sizeof(opts), &err);
+ if (!ks)
return err;
- err = bpf_wq_set_callback(&value->work, ksock_wq_callback, 0);
+ err = ksock_ctx_insert(ks);
if (err)
return err;
- return bpf_wq_start(&value->work, 0);
+ for (key = 0; key < 2; key++) {
+ value = bpf_map_lookup_elem(&work_map, &key);
+ if (!value)
+ return -ENOENT;
+ err = bpf_wq_init(&value->work, &work_map, 0);
+ if (err)
+ return err;
+ if (key)
+ err = bpf_wq_set_callback(&value->work, ksock_wq_create, 0);
+ else
+ err = bpf_wq_set_callback(&value->work, ksock_wq_connect_send, 0);
+ if (err)
+ return err;
+ err = bpf_wq_start(&value->work, 0);
+ if (err)
+ return err;
+ }
+ return 0;
}
char __license[] SEC("license") = "GPL";
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH bpf-next v3] bpf: allow ksock connect/send calls from wq
2026-10-14 9:14 [PATCH bpf-next v3] bpf: allow ksock connect/send calls from wq Mahe Tardy
@ 2026-10-14 9:26 ` sashiko-bot
2026-10-14 9:30 ` Mahe Tardy
0 siblings, 1 reply; 3+ messages in thread
From: sashiko-bot @ 2026-10-14 9:26 UTC (permalink / raw)
To: Mahe Tardy, bpf
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] bpf: selftests: u64 map key mismatch causes ksock_wq failure on big-endian
--
commit e40a8841b185289348c211758c8754c0959c28bd
Author: Mahe Tardy <mahe.tardy@gmail.com>
bpf: allow ksock connect/send calls from wq
This commit relaxes the constraint on not using the workqueues for ksock
connect() or send() kfuncs. It also updates the selftests to verify this
behavior.
> diff --git a/tools/testing/selftests/bpf/progs/ksock_wq.c b/tools/testing/selftests/bpf/progs/ksock_wq.c
> index 16a1873d132ed..290736e219e1d 100644
> --- a/tools/testing/selftests/bpf/progs/ksock_wq.c
> +++ b/tools/testing/selftests/bpf/progs/ksock_wq.c
> @@ -14,15 +15,19 @@ struct ksock_wq_value {
>
> struct {
> __uint(type, BPF_MAP_TYPE_ARRAY);
> - __uint(max_entries, 1);
> + __uint(max_entries, 2);
> __type(key, u32);
> __type(value, struct ksock_wq_value);
> } work_map SEC(".maps");
[ ... ]
> @@ -43,20 +73,40 @@ static int ksock_wq_callback(void *map, int *key, void *value)
> SEC("syscall")
> int ksock_wq_start(void *ctx)
> {
> + struct bpf_ksock_create_opts opts = {
> + .family = AF_INET,
> + .type = SOCK_DGRAM,
> + .protocol = IPPROTO_UDP,
> + };
> struct ksock_wq_value *value;
> - u32 key = 0;
> - int err;
> + struct bpf_ksock *ks;
> + u64 key = 0;
> + int err = 0;
>
> - value = bpf_map_lookup_elem(&work_map, &key);
> - if (!value)
> - return -ENOENT;
> - err = bpf_wq_init(&value->work, &work_map, 0);
> - if (err)
> + ks = bpf_ksock_create(&opts, sizeof(opts), &err);
> + if (!ks)
> return err;
> - err = bpf_wq_set_callback(&value->work, ksock_wq_callback, 0);
> + err = ksock_ctx_insert(ks);
> if (err)
> return err;
> - return bpf_wq_start(&value->work, 0);
> + for (key = 0; key < 2; key++) {
> + value = bpf_map_lookup_elem(&work_map, &key);
[Severity: Medium]
Could this cause failures on big-endian systems?
In ksock_wq_start(), the key variable was changed from u32 to u64, but
work_map is defined as a BPF_MAP_TYPE_ARRAY with a u32 key.
When &key is passed to bpf_map_lookup_elem(), big-endian systems will read
the first 4 bytes of the u64 (which will be 0 for both iterations in the
loop).
This would cause the lookup to return the element at index 0 multiple times,
leading to double-initialization of the same workqueue via bpf_wq_init()
and subsequent failures.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261014091450.19107-1-mahe.tardy@gmail.com?part=1
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH bpf-next v3] bpf: allow ksock connect/send calls from wq
2026-10-14 9:26 ` sashiko-bot
@ 2026-10-14 9:30 ` Mahe Tardy
0 siblings, 0 replies; 3+ messages in thread
From: Mahe Tardy @ 2026-10-14 9:30 UTC (permalink / raw)
To: sashiko-reviews; +Cc: bpf
On Wed, Oct 14, 2026 at 09:26:39AM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Medium] bpf: selftests: u64 map key mismatch causes ksock_wq failure on big-endian
> --
>
> commit e40a8841b185289348c211758c8754c0959c28bd
> Author: Mahe Tardy <mahe.tardy@gmail.com>
>
> bpf: allow ksock connect/send calls from wq
>
> This commit relaxes the constraint on not using the workqueues for ksock
> connect() or send() kfuncs. It also updates the selftests to verify this
> behavior.
>
> > diff --git a/tools/testing/selftests/bpf/progs/ksock_wq.c b/tools/testing/selftests/bpf/progs/ksock_wq.c
> > index 16a1873d132ed..290736e219e1d 100644
> > --- a/tools/testing/selftests/bpf/progs/ksock_wq.c
> > +++ b/tools/testing/selftests/bpf/progs/ksock_wq.c
> > @@ -14,15 +15,19 @@ struct ksock_wq_value {
> >
> > struct {
> > __uint(type, BPF_MAP_TYPE_ARRAY);
> > - __uint(max_entries, 1);
> > + __uint(max_entries, 2);
> > __type(key, u32);
> > __type(value, struct ksock_wq_value);
> > } work_map SEC(".maps");
>
> [ ... ]
>
> > @@ -43,20 +73,40 @@ static int ksock_wq_callback(void *map, int *key, void *value)
> > SEC("syscall")
> > int ksock_wq_start(void *ctx)
> > {
> > + struct bpf_ksock_create_opts opts = {
> > + .family = AF_INET,
> > + .type = SOCK_DGRAM,
> > + .protocol = IPPROTO_UDP,
> > + };
> > struct ksock_wq_value *value;
> > - u32 key = 0;
> > - int err;
> > + struct bpf_ksock *ks;
> > + u64 key = 0;
> > + int err = 0;
> >
> > - value = bpf_map_lookup_elem(&work_map, &key);
> > - if (!value)
> > - return -ENOENT;
> > - err = bpf_wq_init(&value->work, &work_map, 0);
> > - if (err)
> > + ks = bpf_ksock_create(&opts, sizeof(opts), &err);
> > + if (!ks)
> > return err;
> > - err = bpf_wq_set_callback(&value->work, ksock_wq_callback, 0);
> > + err = ksock_ctx_insert(ks);
> > if (err)
> > return err;
> > - return bpf_wq_start(&value->work, 0);
> > + for (key = 0; key < 2; key++) {
> > + value = bpf_map_lookup_elem(&work_map, &key);
>
> [Severity: Medium]
> Could this cause failures on big-endian systems?
>
> In ksock_wq_start(), the key variable was changed from u32 to u64, but
> work_map is defined as a BPF_MAP_TYPE_ARRAY with a u32 key.
>
> When &key is passed to bpf_map_lookup_elem(), big-endian systems will read
> the first 4 bytes of the u64 (which will be 0 for both iterations in the
> loop).
>
> This would cause the lookup to return the element at index 0 multiple times,
> leading to double-initialization of the same workqueue via bpf_wq_init()
> and subsequent failures.
Oh no :facepalm:, I sent the v2 again instead of the v3 with the u32
change... Sorry for the spam, I'll send the proper v3 as v4 in a while
to avoid sending too many emails on the same patchset at once.
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20261014091450.19107-1-mahe.tardy@gmail.com?part=1
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-14 9:30 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-14 9:14 [PATCH bpf-next v3] bpf: allow ksock connect/send calls from wq Mahe Tardy
2026-10-14 9:26 ` sashiko-bot
2026-10-14 9:30 ` Mahe Tardy
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox