All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf-next] bpf: allow ksock kfuncs call from wq
@ 2026-10-13 16:31 Mahe Tardy
  2026-10-13 16:42 ` sashiko-bot
  0 siblings, 1 reply; 3+ messages in thread
From: Mahe Tardy @ 2026-10-13 16:31 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. 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 <mahe.tardy@gmail.com>
---
 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 <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"))
@@ -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 <bpf/bpf_endian.h>
 #include <bpf/bpf_helpers.h>
 #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


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH bpf-next] bpf: allow ksock kfuncs call from wq
  2026-10-13 16:31 [PATCH bpf-next] bpf: allow ksock kfuncs call from wq Mahe Tardy
@ 2026-10-13 16:42 ` sashiko-bot
  2026-10-13 17:10   ` Mahe Tardy
  0 siblings, 1 reply; 3+ messages in thread
From: sashiko-bot @ 2026-10-13 16:42 UTC (permalink / raw)
  To: Mahe Tardy, bpf

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] bpf: bpf_ksock_create causes NULL pointer deref on task exit
- [High] bpf: bpf_ksock_create in workqueue escapes network namespace
--

commit 34ed977b7943eebacf7e605dc8f26f2576ba2664
Author: Mahe Tardy <mahe.tardy@gmail.com>

bpf: allow ksock kfuncs call from wq

This commit relaxes the constraints on ksock kfuncs by removing the check
that ensures they are executed in a user task context. This change was
made to allow ksock kfuncs to be invoked from asynchronous workqueues,
bypassing the original LSM hook considerations.

> diff --git a/net/core/bpf_ksock.c b/net/core/bpf_ksock.c
> index e9943aeeadd32..21d5fd1eb403d 100644
> --- a/net/core/bpf_ksock.c
> +++ b/net/core/bpf_ksock.c
> @@ -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;
> -	}
> -

[Severity: High]
Does removing this check expose us to a NULL pointer dereference during task
exit?

If a sleepable BPF program running via task_work during do_exit() calls
bpf_ksock_create() after exit_nsproxy_namespaces() has cleared
current->nsproxy, it appears this could trigger a kernel panic.

Without the bpf_ksock_has_user_task_context() guard, bpf_ksock_create() will
now unconditionally call sock_create():

net/socket.c:sock_create() {
	return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
}

Is it safe to allow sock_create() to dereference current->nsproxy here without
checking if it is NULL?

[Severity: High]
Additionally, does this change allow containerized workloads to escape their
network namespace?

When bpf_ksock_create() is invoked from a workqueue callback, the executing
context is a kthread.

Because sock_create() uses current->nsproxy->net_ns, a kthread will resolve
this to the host namespace (init_net), causing the socket to be created in the
host namespace rather than the container's netns.

Could this allow BPF programs inside containers to bypass container routing
and firewall policies by creating host-namespace sockets?

>  	if (!opts || opts__sz != sizeof(struct bpf_ksock_create_opts)) {
>  		err = -EINVAL;
>  		goto err_out;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261013163101.205638-1-mahe.tardy@gmail.com?part=1

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH bpf-next] bpf: allow ksock kfuncs call from wq
  2026-10-13 16:42 ` sashiko-bot
@ 2026-10-13 17:10   ` Mahe Tardy
  0 siblings, 0 replies; 3+ messages in thread
From: Mahe Tardy @ 2026-10-13 17:10 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: bpf

On Tue, Oct 13, 2026 at 04:42:13PM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [High] bpf: bpf_ksock_create causes NULL pointer deref on task exit
> - [High] bpf: bpf_ksock_create in workqueue escapes network namespace
> --
> 
> commit 34ed977b7943eebacf7e605dc8f26f2576ba2664
> Author: Mahe Tardy <mahe.tardy@gmail.com>
> 
> bpf: allow ksock kfuncs call from wq
> 
> This commit relaxes the constraints on ksock kfuncs by removing the check
> that ensures they are executed in a user task context. This change was
> made to allow ksock kfuncs to be invoked from asynchronous workqueues,
> bypassing the original LSM hook considerations.
> 
> > diff --git a/net/core/bpf_ksock.c b/net/core/bpf_ksock.c
> > index e9943aeeadd32..21d5fd1eb403d 100644
> > --- a/net/core/bpf_ksock.c
> > +++ b/net/core/bpf_ksock.c
> > @@ -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;
> > -	}
> > -
> 
> [Severity: High]
> Does removing this check expose us to a NULL pointer dereference during task
> exit?
> 
> If a sleepable BPF program running via task_work during do_exit() calls
> bpf_ksock_create() after exit_nsproxy_namespaces() has cleared
> current->nsproxy, it appears this could trigger a kernel panic.
> 
> Without the bpf_ksock_has_user_task_context() guard, bpf_ksock_create() will
> now unconditionally call sock_create():
> 
> net/socket.c:sock_create() {
> 	return __sock_create(current->nsproxy->net_ns, family, type, protocol, res, 0);
> }
> 
> Is it safe to allow sock_create() to dereference current->nsproxy here without
> checking if it is NULL?

Indeed, in do_exit(), it seems we have this ordering:

	exit_nsproxy_namespaces(tsk);
	exit_task_work(tsk);

The first line will put nsproxy to NULL and the second will schedule any
remaining work and call the bpf callback set up with the wq. So this is
valid.

The fix would be to add some NULL check to prevent the panic in this
specific case (or see the next point).

> 
> [Severity: High]
> Additionally, does this change allow containerized workloads to escape their
> network namespace?
> 
> When bpf_ksock_create() is invoked from a workqueue callback, the executing
> context is a kthread.
> 
> Because sock_create() uses current->nsproxy->net_ns, a kthread will resolve
> this to the host namespace (init_net), causing the socket to be created in the
> host namespace rather than the container's netns.
> 
> Could this allow BPF programs inside containers to bypass container routing
> and firewall policies by creating host-namespace sockets?

Also agree, maybe socket creation should still be behind the check, I
think it's still useful to be able to connect and send from a workqueue
but preventing to create via workqueues might be a good limitation.

Let me send a v2 with create still using the runtime check, that should
fix this and the above issue.

> 
> >  	if (!opts || opts__sz != sizeof(struct bpf_ksock_create_opts)) {
> >  		err = -EINVAL;
> >  		goto err_out;
> 
> -- 
> Sashiko AI review · https://sashiko.dev/#/patchset/20261013163101.205638-1-mahe.tardy@gmail.com?part=1

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-10-13 17:10 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-13 16:31 [PATCH bpf-next] bpf: allow ksock kfuncs call from wq Mahe Tardy
2026-10-13 16:42 ` sashiko-bot
2026-10-13 17:10   ` Mahe Tardy

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.