* [PATCH] bpf: Add network namespace boundary inspection kfuncs
@ 2026-11-05 12:16 Abhishek Khade
2026-11-05 12:30 ` sashiko-bot
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Abhishek Khade @ 2026-11-05 12:16 UTC (permalink / raw)
To: bpf, ast, daniel, andrii; +Cc: linux-kernel, linux-kselftest, Abhishek Khade
eBPF tracing and networking programs frequently need to determine whether
an execution event originates from the initial host network namespace
(init_net) or an isolated container namespace, as well as extract the
namespace inode identifier for correlation.
Currently, dereferencing task->nsproxy->net_ns directly via BPF CO-RE
presents safety and architectural challenges:
1. nsproxy can be NULL during concurrent unshare or task exit teardown.
2. Direct comparison against &init_net exposes host kernel addresses,
complicating verification and KASLR safety.
3. Reading deep pointer chains without RCU synchronization can trigger
verifier rejections or race against namespace cleanup.
Introduce three BPF kfuncs to safely inspect network namespace boundaries:
- bpf_task_is_init_net(struct task_struct *task): Returns 1 if the task
belongs to the initial host network namespace, 0 otherwise.
- bpf_task_get_netns_inum(struct task_struct *task): Returns the inode
number of the task's network namespace, or 0 on failure.
- bpf_sock_is_init_net(struct sock *sk): Returns 1 if the socket belongs
to init_net, 0 otherwise.
These helpers are registered under generic_btf_ids with the KF_RCU flag,
allowing safe invocation within RCU read-side critical sections across
tracing, LSM, and socket programs.
Add automated selftests under tools/testing/selftests/bpf validating host
detection and container isolation via unshare(CLONE_NEWNET).
Signed-off-by: Abhishek Khade <abhishekkhade69@gmail.com>
---
kernel/bpf/helpers.c | 79 +++++++++++++
.../selftests/bpf/prog_tests/netns_kfunc.c | 104 ++++++++++++++++++
.../selftests/bpf/progs/test_netns_kfunc.c | 32 ++++++
3 files changed, 215 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/netns_kfunc.c
create mode 100644 tools/testing/selftests/bpf/progs/test_netns_kfunc.c
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 6788489a2..3584703d1 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -18,6 +18,9 @@
#include <linux/pid_namespace.h>
#include <linux/poison.h>
#include <linux/proc_ns.h>
+#include <linux/nsproxy.h>
+#include <net/net_namespace.h>
+#include <net/sock.h>
#include <linux/sched/task.h>
#include <linux/security.h>
#include <linux/btf_ids.h>
@@ -2991,6 +2994,77 @@ __bpf_kfunc long bpf_task_under_cgroup(struct task_struct *task,
return ret;
}
+#ifdef CONFIG_NET
+/**
+ * bpf_task_is_init_net - check whether task belongs to initial network namespace
+ * @task: the task to be tested
+ *
+ * Returns true if @task belongs to the initial network namespace (init_net),
+ * or false otherwise (or if @task or its nsproxy is NULL).
+ */
+__bpf_kfunc bool bpf_task_is_init_net(const struct task_struct *task)
+{
+ struct nsproxy *nsproxy;
+ struct net *net;
+ bool ret = false;
+
+ if (!task)
+ return false;
+
+ rcu_read_lock();
+ nsproxy = rcu_dereference(task->nsproxy);
+ if (nsproxy) {
+ net = READ_ONCE(nsproxy->net_ns);
+ ret = (net == &init_net);
+ }
+ rcu_read_unlock();
+
+ return ret;
+}
+
+/**
+ * bpf_task_get_netns_inum - get inode number of task's network namespace
+ * @task: the task to inspect
+ *
+ * Returns the inode number (inum) of @task's network namespace, or 0 if
+ * @task, its nsproxy, or net_ns is NULL.
+ */
+__bpf_kfunc u32 bpf_task_get_netns_inum(const struct task_struct *task)
+{
+ struct nsproxy *nsproxy;
+ struct net *net;
+ u32 inum = 0;
+
+ if (!task)
+ return 0;
+
+ rcu_read_lock();
+ nsproxy = rcu_dereference(task->nsproxy);
+ if (nsproxy) {
+ net = READ_ONCE(nsproxy->net_ns);
+ if (net)
+ inum = net->ns.inum;
+ }
+ rcu_read_unlock();
+
+ return inum;
+}
+
+/**
+ * bpf_sock_is_init_net - check whether socket belongs to initial network namespace
+ * @sk: the socket to be tested
+ *
+ * Returns true if @sk belongs to init_net, or false otherwise (or if @sk is NULL).
+ */
+__bpf_kfunc bool bpf_sock_is_init_net(const struct sock *sk)
+{
+ if (!sk)
+ return false;
+
+ return net_eq(sock_net(sk), &init_net);
+}
+#endif /* CONFIG_NET */
+
BPF_CALL_2(bpf_current_task_under_cgroup, struct bpf_map *, map, u32, idx)
{
struct bpf_array *array = container_of(map, struct bpf_array, map);
@@ -5151,6 +5225,11 @@ BTF_ID_FLAGS(func, bpf_cgroup_from_id, KF_ACQUIRE | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_task_under_cgroup, KF_RCU)
BTF_ID_FLAGS(func, bpf_task_get_cgroup1, KF_ACQUIRE | KF_RCU | KF_RET_NULL)
#endif
+#ifdef CONFIG_NET
+BTF_ID_FLAGS(func, bpf_task_is_init_net, KF_RCU)
+BTF_ID_FLAGS(func, bpf_task_get_netns_inum, KF_RCU)
+BTF_ID_FLAGS(func, bpf_sock_is_init_net, KF_RCU)
+#endif
BTF_ID_FLAGS(func, bpf_task_from_pid, KF_ACQUIRE | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_task_from_vpid, KF_ACQUIRE | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_throw)
diff --git a/tools/testing/selftests/bpf/prog_tests/netns_kfunc.c b/tools/testing/selftests/bpf/prog_tests/netns_kfunc.c
new file mode 100644
index 000000000..3072f7ede
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/netns_kfunc.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Abhishek Khade */
+
+#define _GNU_SOURCE
+#include <sched.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <test_progs.h>
+#include "test_netns_kfunc.skel.h"
+
+void test_netns_kfunc(void)
+{
+ struct test_netns_kfunc *skel;
+ int pipefd[2];
+ pid_t pid;
+ int status = 0;
+ u32 host_inum;
+
+ skel = test_netns_kfunc__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "open_and_load"))
+ return;
+
+ if (!ASSERT_OK(test_netns_kfunc__attach(skel), "attach"))
+ goto cleanup;
+
+ /* Test 1: Process running in initial network namespace */
+ if (!ASSERT_OK(pipe(pipefd), "pipe_init"))
+ goto cleanup;
+
+ pid = fork();
+ if (!ASSERT_GE(pid, 0, "fork_host")) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ goto cleanup;
+ }
+
+ if (pid == 0) {
+ close(pipefd[1]);
+ if (read(pipefd[0], &status, sizeof(status)) < 0)
+ exit(1);
+ close(pipefd[0]);
+ execlp("true", "true", NULL);
+ exit(1);
+ }
+
+ skel->bss->target_pid = pid;
+ close(pipefd[0]);
+ if (write(pipefd[1], &status, sizeof(status)) < 0) {
+ close(pipefd[1]);
+ waitpid(pid, NULL, 0);
+ goto cleanup;
+ }
+ close(pipefd[1]);
+ waitpid(pid, &status, 0);
+
+ ASSERT_EQ(skel->bss->inv_is_init_net, 1, "host_task_is_init_net");
+ ASSERT_GT(skel->bss->inv_netns_inum, 0, "host_netns_inum_valid");
+ host_inum = skel->bss->inv_netns_inum;
+
+ /* Reset state */
+ skel->bss->inv_is_init_net = -1;
+ skel->bss->inv_netns_inum = 0;
+
+ /* Test 2: Process running in a new network namespace */
+ if (!ASSERT_OK(pipe(pipefd), "pipe_netns"))
+ goto cleanup;
+
+ pid = fork();
+ if (!ASSERT_GE(pid, 0, "fork_netns")) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ goto cleanup;
+ }
+
+ if (pid == 0) {
+ close(pipefd[1]);
+ if (read(pipefd[0], &status, sizeof(status)) < 0)
+ exit(1);
+ close(pipefd[0]);
+
+ if (unshare(CLONE_NEWNET) != 0)
+ exit(1);
+
+ execlp("true", "true", NULL);
+ exit(1);
+ }
+
+ skel->bss->target_pid = pid;
+ close(pipefd[0]);
+ if (write(pipefd[1], &status, sizeof(status)) < 0) {
+ close(pipefd[1]);
+ waitpid(pid, NULL, 0);
+ goto cleanup;
+ }
+ close(pipefd[1]);
+ waitpid(pid, &status, 0);
+
+ ASSERT_EQ(skel->bss->inv_is_init_net, 0, "netns_task_not_init_net");
+ ASSERT_GT(skel->bss->inv_netns_inum, 0, "child_netns_inum_valid");
+ ASSERT_NEQ(skel->bss->inv_netns_inum, host_inum, "netns_inum_different");
+
+cleanup:
+ test_netns_kfunc__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_netns_kfunc.c b/tools/testing/selftests/bpf/progs/test_netns_kfunc.c
new file mode 100644
index 000000000..3ad49f8ba
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_netns_kfunc.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Abhishek Khade */
+
+#include <vmlinux.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_helpers.h>
+
+#include "bpf_misc.h"
+
+bool bpf_task_is_init_net(const struct task_struct *task) __ksym;
+u32 bpf_task_get_netns_inum(const struct task_struct *task) __ksym;
+
+pid_t target_pid;
+int inv_is_init_net = -1;
+u32 inv_netns_inum;
+
+SEC("tp_btf/sched_process_exec")
+int BPF_PROG(on_exec, struct task_struct *task, u64 old_pid,
+ struct linux_binprm *bprm)
+{
+ pid_t pid = task->tgid;
+
+ if (pid != target_pid)
+ return 0;
+
+ inv_is_init_net = bpf_task_is_init_net(task) ? 1 : 0;
+ inv_netns_inum = bpf_task_get_netns_inum(task);
+
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
--
2.53.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH] bpf: Add network namespace boundary inspection kfuncs
2026-11-05 12:16 [PATCH] bpf: Add network namespace boundary inspection kfuncs Abhishek Khade
@ 2026-11-05 12:30 ` sashiko-bot
2026-11-05 12:54 ` bot+bpf-ci
2026-11-05 16:39 ` Alexei Starovoitov
2 siblings, 0 replies; 4+ messages in thread
From: sashiko-bot @ 2026-11-05 12:30 UTC (permalink / raw)
To: Abhishek Khade, bpf
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Critical] bpf: use-after-free of task->nsproxy in netns task kfuncs
- [Medium] bpf: missing __nullable suffix on netns kfunc pointer parameters
- [Medium] selftests/bpf: incomplete coverage and error checks in test_netns_kfunc()
--
commit becb6e580b969813c102c712d541272b54c4d34c
Author: Abhishek Khade <abhishekkhade69@gmail.com>
bpf: Add network namespace boundary inspection kfuncs
This commit adds three BPF kfuncs (bpf_task_is_init_net,
bpf_task_get_netns_inum, and bpf_sock_is_init_net) to inspect whether a
task or socket belongs to init_net and retrieve a task's network namespace
inode number, along with a BPF selftest.
> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> index 6788489a2608b..3584703d139f1 100644
> --- a/kernel/bpf/helpers.c
> +++ b/kernel/bpf/helpers.c
[ ... ]
> @@ -2991,6 +2994,77 @@ __bpf_kfunc long bpf_task_under_cgroup(struct task_struct *task,
> return ret;
> }
>
> +#ifdef CONFIG_NET
> +/**
> + * bpf_task_is_init_net - check whether task belongs to initial network namespace
> + * @task: the task to be tested
> + *
> + * Returns true if @task belongs to the initial network namespace (init_net),
> + * or false otherwise (or if @task or its nsproxy is NULL).
> + */
> +__bpf_kfunc bool bpf_task_is_init_net(const struct task_struct *task)
> +{
> + struct nsproxy *nsproxy;
> + struct net *net;
> + bool ret = false;
> +
> + if (!task)
> + return false;
> +
> + rcu_read_lock();
> + nsproxy = rcu_dereference(task->nsproxy);
> + if (nsproxy) {
> + net = READ_ONCE(nsproxy->net_ns);
> + ret = (net == &init_net);
> + }
> + rcu_read_unlock();
> +
> + return ret;
> +}
> +
> +/**
> + * bpf_task_get_netns_inum - get inode number of task's network namespace
> + * @task: the task to inspect
> + *
> + * Returns the inode number (inum) of @task's network namespace, or 0 if
> + * @task, its nsproxy, or net_ns is NULL.
> + */
> +__bpf_kfunc u32 bpf_task_get_netns_inum(const struct task_struct *task)
> +{
> + struct nsproxy *nsproxy;
> + struct net *net;
> + u32 inum = 0;
> +
> + if (!task)
> + return 0;
> +
> + rcu_read_lock();
> + nsproxy = rcu_dereference(task->nsproxy);
> + if (nsproxy) {
> + net = READ_ONCE(nsproxy->net_ns);
> + if (net)
> + inum = net->ns.inum;
> + }
> + rcu_read_unlock();
> +
> + return inum;
> +}
[Severity: Critical]
Can bpf_task_is_init_net() and bpf_task_get_netns_inum() trigger a
use-after-free of task->nsproxy (and task->nsproxy->net_ns) when called on
a task other than current?
Both kfuncs are registered with KF_RCU and accept an arbitrary
struct task_struct pointer (for example from bpf_task_from_pid(), a task
iterator, task->real_parent, or a tracing/LSM hook), then read
task->nsproxy under rcu_read_lock() without holding task_lock(task).
However, task_struct::nsproxy is not an __rcu pointer (which also causes
rcu_dereference(task->nsproxy) to emit a Sparse address-space mismatch
warning when building with C=1 and CONFIG_SPARSE_RCU_POINTER), and
include/linux/nsproxy.h notes that accessing task->nsproxy on a
non-current task requires task_lock(task).
When the target task concurrently runs unshare(), setns(),
exec_task_namespaces(), or exits via do_exit() ->
exit_nsproxy_namespaces(), switch_task_namespaces() updates p->nsproxy
under task_lock(p) and immediately drops the old reference without waiting
for an RCU grace period:
kernel/nsproxy.c:switch_task_namespaces() {
task_lock(p);
ns = p->nsproxy;
p->nsproxy = new;
task_unlock(p);
if (ns)
put_nsproxy(ns);
}
put_nsproxy() -> nsproxy_free() then drops ns->net_ns and frees the
nsproxy slab object synchronously:
kernel/nsproxy.c:nsproxy_free() {
...
put_net(ns->net_ns);
kmem_cache_free(nsproxy_cachep, ns);
}
Because nsproxy_cachep is not SLAB_TYPESAFE_BY_RCU and nsproxy_free() does
not use call_rcu(), rcu_read_lock() does not keep nsproxy alive after
loading task->nsproxy. Couldn't nsproxy be freed and poisoned or reused
before READ_ONCE(nsproxy->net_ns) and net->ns.inum are read?
> +
> +/**
> + * bpf_sock_is_init_net - check whether socket belongs to initial network namespace
> + * @sk: the socket to be tested
> + *
> + * Returns true if @sk belongs to init_net, or false otherwise (or if @sk is NULL).
> + */
> +__bpf_kfunc bool bpf_sock_is_init_net(const struct sock *sk)
> +{
> + if (!sk)
> + return false;
> +
> + return net_eq(sock_net(sk), &init_net);
> +}
[Severity: Medium]
Should the pointer arguments of bpf_task_is_init_net(),
bpf_task_get_netns_inum(), and bpf_sock_is_init_net() use the __nullable
suffix (task__nullable and sk__nullable)?
The kernel-doc comments and implementations of all three kfuncs explicitly
handle NULL pointers by checking if (!task) or if (!sk) and returning
false or 0.
However, in kernel/bpf/verifier.c, is_kfunc_arg_nullable() and
get_kfunc_arg_type() only mark a kfunc pointer parameter as PTR_MAYBE_NULL
when the parameter name ends with __nullable. Without that suffix, won't
the verifier reject BPF programs that pass a nullable pointer (such as an
unchecked skb->sk or a PTR_MAYBE_NULL task pointer) to these kfuncs?
> diff --git a/tools/testing/selftests/bpf/prog_tests/netns_kfunc.c b/tools/testing/selftests/bpf/prog_tests/netns_kfunc.c
> new file mode 100644
> index 0000000000000..3072f7ede8124
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/netns_kfunc.c
[ ... ]
> @@ -0,0 +1,104 @@
> + skel->bss->target_pid = pid;
> + close(pipefd[0]);
> + if (write(pipefd[1], &status, sizeof(status)) < 0) {
> + close(pipefd[1]);
> + waitpid(pid, NULL, 0);
> + goto cleanup;
> + }
> + close(pipefd[1]);
> + waitpid(pid, &status, 0);
> +
> + ASSERT_EQ(skel->bss->inv_is_init_net, 1, "host_task_is_init_net");
> + ASSERT_GT(skel->bss->inv_netns_inum, 0, "host_netns_inum_valid");
> + host_inum = skel->bss->inv_netns_inum;
[ ... ]
> + skel->bss->target_pid = pid;
> + close(pipefd[0]);
> + if (write(pipefd[1], &status, sizeof(status)) < 0) {
> + close(pipefd[1]);
> + waitpid(pid, NULL, 0);
> + goto cleanup;
> + }
> + close(pipefd[1]);
> + waitpid(pid, &status, 0);
> +
> + ASSERT_EQ(skel->bss->inv_is_init_net, 0, "netns_task_not_init_net");
> + ASSERT_GT(skel->bss->inv_netns_inum, 0, "child_netns_inum_valid");
> + ASSERT_NEQ(skel->bss->inv_netns_inum, host_inum, "netns_inum_different");
> +
> +cleanup:
> + test_netns_kfunc__destroy(skel);
> +}
> diff --git a/tools/testing/selftests/bpf/progs/test_netns_kfunc.c b/tools/testing/selftests/bpf/progs/test_netns_kfunc.c
> new file mode 100644
> index 0000000000000..3ad49f8ba39a3
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/test_netns_kfunc.c
> @@ -0,0 +1,32 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2026 Abhishek Khade */
> +
> +#include <vmlinux.h>
> +#include <bpf/bpf_tracing.h>
> +#include <bpf/bpf_helpers.h>
> +
> +#include "bpf_misc.h"
> +
> +bool bpf_task_is_init_net(const struct task_struct *task) __ksym;
> +u32 bpf_task_get_netns_inum(const struct task_struct *task) __ksym;
> +
> +pid_t target_pid;
> +int inv_is_init_net = -1;
> +u32 inv_netns_inum;
> +
> +SEC("tp_btf/sched_process_exec")
> +int BPF_PROG(on_exec, struct task_struct *task, u64 old_pid,
> + struct linux_binprm *bprm)
> +{
> + pid_t pid = task->tgid;
> +
> + if (pid != target_pid)
> + return 0;
> +
> + inv_is_init_net = bpf_task_is_init_net(task) ? 1 : 0;
> + inv_netns_inum = bpf_task_get_netns_inum(task);
> +
> + return 0;
> +}
[Severity: Medium]
There are a few issues in test_netns_kfunc() and on_exec():
1. The patch adds bpf_sock_is_init_net(), but neither test_netns_kfunc.c
nor netns_kfunc.c declares or exercises it. Could the selftest also cover
bpf_sock_is_init_net() in both init_net and the unshared network
namespace?
2. In test_netns_kfunc(), both write(pipefd[1], &status, sizeof(status))
error paths jump straight to cleanup without an ASSERT_*() failure, which
would allow the test to pass silently if write() fails, and waitpid(pid,
&status, 0) does not assert that the child exited cleanly via WIFEXITED()
and WEXITSTATUS(). Should those paths use ASSERT_EQ() so child failures in
unshare(CLONE_NEWNET) or execlp() are reported explicitly?
3. In on_exec(), task->tgid (the global PID in init_pid_ns) is compared
against target_pid (the userspace fork() return value in the caller's PID
namespace), and the first subtest assumes the test runner itself starts in
init_net. Would this cause false failures when running test_progs inside a
container or non-initial PID/network namespace?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261105121642.38019-1-abhishekkhade69@gmail.com?part=1
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] bpf: Add network namespace boundary inspection kfuncs
2026-11-05 12:16 [PATCH] bpf: Add network namespace boundary inspection kfuncs Abhishek Khade
2026-11-05 12:30 ` sashiko-bot
@ 2026-11-05 12:54 ` bot+bpf-ci
2026-11-05 16:39 ` Alexei Starovoitov
2 siblings, 0 replies; 4+ messages in thread
From: bot+bpf-ci @ 2026-11-05 12:54 UTC (permalink / raw)
To: abhishekkhade69, bpf, ast, daniel, andrii
Cc: linux-kernel, linux-kselftest, abhishekkhade69, ast, andrii,
daniel, martin.lau, eddyz87, yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2824 bytes --]
> bpf: Add network namespace boundary inspection kfuncs
TL;DR: The new kfuncs sit inside the CONFIG_CGROUPS block but their
BTF_ID_FLAGS entries don't. With CONFIG_NET=y and CONFIG_CGROUPS=n,
resolve_btfids may fail the link or kfunc registration may abort, possibly
dropping other kfuncs.
> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> --- a/kernel/bpf/helpers.c
> +++ b/kernel/bpf/helpers.c
[ ... ]
> @@ -2991,6 +2994,77 @@ __bpf_kfunc long bpf_task_under_cgroup(struct task_struct *task,
> return ret;
> }
>
> +#ifdef CONFIG_NET
> +/**
> + * bpf_task_is_init_net - check whether task belongs to initial network namespace
[ ... ]
> +__bpf_kfunc bool bpf_sock_is_init_net(const struct sock *sk)
> +{
> + if (!sk)
> + return false;
> +
> + return net_eq(sock_net(sk), &init_net);
> +}
> +#endif /* CONFIG_NET */
> +
[ ... ]
> @@ -5151,6 +5225,11 @@ BTF_ID_FLAGS(func, bpf_cgroup_from_id, KF_ACQUIRE | KF_RET_NULL)
> BTF_ID_FLAGS(func, bpf_task_under_cgroup, KF_RCU)
> BTF_ID_FLAGS(func, bpf_task_get_cgroup1, KF_ACQUIRE | KF_RCU | KF_RET_NULL)
> #endif
> +#ifdef CONFIG_NET
> +BTF_ID_FLAGS(func, bpf_task_is_init_net, KF_RCU)
> +BTF_ID_FLAGS(func, bpf_task_get_netns_inum, KF_RCU)
> +BTF_ID_FLAGS(func, bpf_sock_is_init_net, KF_RCU)
> +#endif
Are these three kfunc definitions compiled when CONFIG_CGROUPS is not set?
The definitions sit inside the existing CONFIG_CGROUPS block, which is
closed by the "#endif /* CONFIG_CGROUPS */" after bpf_task_get_cgroup1()
and friends, but the BTF_ID_FLAGS() entries are guarded only by
CONFIG_NET and come after the CONFIG_CGROUPS #endif.
With CONFIG_NET=y, CONFIG_CGROUPS=n and CONFIG_DEBUG_INFO_BTF=y, the
functions are not built but generic_btf_ids still names them. Then
resolve_btfids prints "unresolved symbol bpf_task_is_init_net" for each
of them, and the vmlinux link can fail when CONFIG_WERROR is set.
Without CONFIG_WERROR, the set8 entries keep BTF id 0. At boot,
kfunc_init() calls register_btf_kfunc_id_set(BPF_PROG_TYPE_TRACING,
&generic_kfunc_set), and btf_check_kfunc_protos() sees btf_type_by_id(btf, 0)
as the void type and returns -EINVAL:
if (!btf_type_is_func(func))
return -EINVAL;
The "ret ?:" chain in kfunc_init() then skips every later registration
(SCHED_CLS, XDP, STRUCT_OPS, SYSCALL, CGROUP_SKB, the dtors and
common_kfunc_set). Could that drop kfuncs such as bpf_obj_new(),
bpf_task_acquire() and bpf_rcu_read_lock() on such configs?
Should the new definitions be moved after the CONFIG_CGROUPS #endif, so
they match the scope of the BTF_ID_FLAGS() entries?
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/42028225169
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] bpf: Add network namespace boundary inspection kfuncs
2026-11-05 12:16 [PATCH] bpf: Add network namespace boundary inspection kfuncs Abhishek Khade
2026-11-05 12:30 ` sashiko-bot
2026-11-05 12:54 ` bot+bpf-ci
@ 2026-11-05 16:39 ` Alexei Starovoitov
2 siblings, 0 replies; 4+ messages in thread
From: Alexei Starovoitov @ 2026-11-05 16:39 UTC (permalink / raw)
To: Abhishek Khade, bpf, daniel, andrii; +Cc: linux-kernel, linux-kselftest
On Thu, Nov 05, 2026 at 04:16 AM Abhishek Khade <abhishekkhade69@gmail.com> wrote:
> Currently, dereferencing task->nsproxy->net_ns directly via BPF CO-RE
> presents safety and architectural challenges:
No. The prog does all of it today without new kfuncs:
inum = task->nsproxy->net_ns->ns.inum;
is_init_net = inum == NET_NS_INIT_INO;
The verifier accepts these loads in tp_btf, fentry and lsm progs.
A load that faults reads zero. It doesn't crash.
NET_NS_INIT_INO is uapi. No need for the address of init_net.
For a socket it's sk->__sk_common.skc_net.net->ns.inum.
Networking progs also have bpf_get_netns_cookie().
> + rcu_read_lock();
> + nsproxy = rcu_dereference(task->nsproxy);
> + if (nsproxy) {
> + net = READ_ONCE(nsproxy->net_ns);
> + if (net)
> + inum = net->ns.inum;
This is a use-after-free when task != current.
So much for 'this is safer than CO-RE' :(
pw-bot: cr
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-11-05 16:39 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05 12:16 [PATCH] bpf: Add network namespace boundary inspection kfuncs Abhishek Khade
2026-11-05 12:30 ` sashiko-bot
2026-11-05 12:54 ` bot+bpf-ci
2026-11-05 16:39 ` Alexei Starovoitov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox