* [PATCH v2 bpf-next 0/4] bpf path iterator
@ 2025-06-03 6:59 Song Liu
2025-06-03 6:59 ` [PATCH v2 bpf-next 1/4] namei: Introduce new helper function path_walk_parent() Song Liu
` (3 more replies)
0 siblings, 4 replies; 23+ messages in thread
From: Song Liu @ 2025-06-03 6:59 UTC (permalink / raw)
To: bpf, linux-fsdevel, linux-kernel, linux-security-module
Cc: kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton,
josef, mic, gnoack, m, Song Liu
In security use cases, it is common to apply rules to VFS subtrees.
However, filtering files in a subtree is not straightforward [1].
One solution to this problem is to start from a path and walk up the VFS
tree (towards the root). Among in-tree LSMs, Landlock uses this solution.
BPF LSM solutions, such like Tetragon [2], also use similar approaches.
However, due to lack of proper helper/kfunc support, BPF LSM solutions
usually do the path walk with probe read, which is racy.
This patchset introduce a reliable helper path_walk_parent, which walks
path to its VFS parent. The helper is use in Landlock.
A new BPF iterator, path iterator, is introduced to do the path walking.
The BPF path iterator uses the new path_walk_parent help to walk the VFS
tree.
Changes v1 => v2:
1. Rename path_parent => path_walk_parent.
2. Remove path_connected check in path_walk_parent.
3. Fix is_access_to_paths_allowed().
4. Remove mode for path iterator, add a flag instead.
v1: https://lore.kernel.org/bpf/20250528222623.1373000-1-song@kernel.org/
[1] https://lpc.events/event/18/contributions/1940/
[2] https://github.com/cilium/tetragon/
Song Liu (4):
namei: Introduce new helper function path_walk_parent()
landlock: Use path_walk_parent()
bpf: Introduce path iterator
selftests/bpf: Add tests for bpf path iterator
fs/namei.c | 52 +++++++
include/linux/namei.h | 2 +
kernel/bpf/Makefile | 1 +
kernel/bpf/helpers.c | 3 +
kernel/bpf/path_iter.c | 58 ++++++++
kernel/bpf/verifier.c | 5 +
security/landlock/fs.c | 31 ++--
.../testing/selftests/bpf/bpf_experimental.h | 6 +
.../selftests/bpf/prog_tests/path_iter.c | 12 ++
tools/testing/selftests/bpf/progs/path_iter.c | 134 ++++++++++++++++++
10 files changed, 283 insertions(+), 21 deletions(-)
create mode 100644 kernel/bpf/path_iter.c
create mode 100644 tools/testing/selftests/bpf/prog_tests/path_iter.c
create mode 100644 tools/testing/selftests/bpf/progs/path_iter.c
--
2.47.1
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 bpf-next 1/4] namei: Introduce new helper function path_walk_parent()
2025-06-03 6:59 [PATCH v2 bpf-next 0/4] bpf path iterator Song Liu
@ 2025-06-03 6:59 ` Song Liu
2025-06-06 11:10 ` Mickaël Salaün
2025-06-06 14:40 ` Al Viro
2025-06-03 6:59 ` [PATCH v2 bpf-next 2/4] landlock: Use path_walk_parent() Song Liu
` (2 subsequent siblings)
3 siblings, 2 replies; 23+ messages in thread
From: Song Liu @ 2025-06-03 6:59 UTC (permalink / raw)
To: bpf, linux-fsdevel, linux-kernel, linux-security-module
Cc: kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton,
josef, mic, gnoack, m, Song Liu
This helper walks an input path to its parent. Logic are added to handle
walking across mount tree.
This will be used by landlock, and BPF LSM.
Signed-off-by: Song Liu <song@kernel.org>
---
fs/namei.c | 52 +++++++++++++++++++++++++++++++++++++++++++
include/linux/namei.h | 2 ++
2 files changed, 54 insertions(+)
diff --git a/fs/namei.c b/fs/namei.c
index 4bb889fc980b..7d5bf2bb604f 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1424,6 +1424,58 @@ static bool choose_mountpoint(struct mount *m, const struct path *root,
return found;
}
+/**
+ * path_walk_parent - Walk to the parent of path
+ * @path: input and output path.
+ * @root: root of the path walk, do not go beyond this root. If @root is
+ * zero'ed, walk all the way to real root.
+ *
+ * Given a path, find the parent path. Replace @path with the parent path.
+ * If we were already at the real root or a disconnected root, @path is
+ * not changed.
+ *
+ * The logic of path_walk_parent() is similar to follow_dotdot(), except
+ * that path_walk_parent() will continue walking for !path_connected case.
+ * This effectively means we are walking from disconnectedbind mount to the
+ * original mount point. If this behavior is not desired, the caller can
+ * add a check like:
+ *
+ * if (path_walk_parent(&path) && !path_connected(path.mnt, path.dentry)
+ * // continue walking
+ * else
+ * // stop walking
+ *
+ * Returns:
+ * true - if @path is updated to its parent.
+ * false - if @path is already the root (real root or @root).
+ */
+bool path_walk_parent(struct path *path, const struct path *root)
+{
+ struct dentry *parent;
+
+ if (path_equal(path, root))
+ return false;
+
+ if (unlikely(path->dentry == path->mnt->mnt_root)) {
+ struct path p;
+
+ if (!choose_mountpoint(real_mount(path->mnt), root, &p))
+ return false;
+ path_put(path);
+ *path = p;
+ return true;
+ }
+
+ if (unlikely(IS_ROOT(path->dentry)))
+ return false;
+
+ parent = dget_parent(path->dentry);
+ dput(path->dentry);
+ path->dentry = parent;
+ return true;
+}
+EXPORT_SYMBOL_GPL(path_walk_parent);
+
/*
* Perform an automount
* - return -EISDIR to tell follow_managed() to stop and return the path we
diff --git a/include/linux/namei.h b/include/linux/namei.h
index 5d085428e471..cba5373ecf86 100644
--- a/include/linux/namei.h
+++ b/include/linux/namei.h
@@ -85,6 +85,8 @@ extern int follow_down_one(struct path *);
extern int follow_down(struct path *path, unsigned int flags);
extern int follow_up(struct path *);
+bool path_walk_parent(struct path *path, const struct path *root);
+
extern struct dentry *lock_rename(struct dentry *, struct dentry *);
extern struct dentry *lock_rename_child(struct dentry *, struct dentry *);
extern void unlock_rename(struct dentry *, struct dentry *);
--
2.47.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 bpf-next 2/4] landlock: Use path_walk_parent()
2025-06-03 6:59 [PATCH v2 bpf-next 0/4] bpf path iterator Song Liu
2025-06-03 6:59 ` [PATCH v2 bpf-next 1/4] namei: Introduce new helper function path_walk_parent() Song Liu
@ 2025-06-03 6:59 ` Song Liu
2025-06-03 13:46 ` Mickaël Salaün
2025-06-03 6:59 ` [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator Song Liu
2025-06-03 6:59 ` [PATCH v2 bpf-next 4/4] selftests/bpf: Add tests for bpf " Song Liu
3 siblings, 1 reply; 23+ messages in thread
From: Song Liu @ 2025-06-03 6:59 UTC (permalink / raw)
To: bpf, linux-fsdevel, linux-kernel, linux-security-module
Cc: kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton,
josef, mic, gnoack, m, Song Liu
Use path_walk_parent() to walk a path up to its parent.
No functional changes intended.
Signed-off-by: Song Liu <song@kernel.org>
---
security/landlock/fs.c | 31 ++++++++++---------------------
1 file changed, 10 insertions(+), 21 deletions(-)
diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 6fee7c20f64d..3adac544dc9e 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -837,8 +837,8 @@ static bool is_access_to_paths_allowed(
* restriction.
*/
while (true) {
- struct dentry *parent_dentry;
const struct landlock_rule *rule;
+ struct path root = {};
/*
* If at least all accesses allowed on the destination are
@@ -895,34 +895,23 @@ static bool is_access_to_paths_allowed(
/* Stops when a rule from each layer grants access. */
if (allowed_parent1 && allowed_parent2)
break;
-jump_up:
- if (walker_path.dentry == walker_path.mnt->mnt_root) {
- if (follow_up(&walker_path)) {
- /* Ignores hidden mount points. */
- goto jump_up;
- } else {
- /*
- * Stops at the real root. Denies access
- * because not all layers have granted access.
- */
- break;
- }
- }
+
+ if (path_walk_parent(&walker_path, &root))
+ continue;
+
if (unlikely(IS_ROOT(walker_path.dentry))) {
/*
- * Stops at disconnected root directories. Only allows
- * access to internal filesystems (e.g. nsfs, which is
- * reachable through /proc/<pid>/ns/<namespace>).
+ * Stops at disconnected or real root directories.
+ * Only allows access to internal filesystems
+ * (e.g. nsfs, which is reachable through
+ * /proc/<pid>/ns/<namespace>).
*/
if (walker_path.mnt->mnt_flags & MNT_INTERNAL) {
allowed_parent1 = true;
allowed_parent2 = true;
}
- break;
}
- parent_dentry = dget_parent(walker_path.dentry);
- dput(walker_path.dentry);
- walker_path.dentry = parent_dentry;
+ break;
}
path_put(&walker_path);
--
2.47.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator
2025-06-03 6:59 [PATCH v2 bpf-next 0/4] bpf path iterator Song Liu
2025-06-03 6:59 ` [PATCH v2 bpf-next 1/4] namei: Introduce new helper function path_walk_parent() Song Liu
2025-06-03 6:59 ` [PATCH v2 bpf-next 2/4] landlock: Use path_walk_parent() Song Liu
@ 2025-06-03 6:59 ` Song Liu
2025-06-03 15:13 ` Alexei Starovoitov
` (2 more replies)
2025-06-03 6:59 ` [PATCH v2 bpf-next 4/4] selftests/bpf: Add tests for bpf " Song Liu
3 siblings, 3 replies; 23+ messages in thread
From: Song Liu @ 2025-06-03 6:59 UTC (permalink / raw)
To: bpf, linux-fsdevel, linux-kernel, linux-security-module
Cc: kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton,
josef, mic, gnoack, m, Song Liu
Introduce a path iterator, which reliably walk a struct path toward
the root. This path iterator is based on path_walk_parent. A fixed
zero'ed root is passed to path_walk_parent(). Therefore, unless the
user terminates it earlier, the iterator will terminate at the real
root.
Signed-off-by: Song Liu <song@kernel.org>
---
kernel/bpf/Makefile | 1 +
kernel/bpf/helpers.c | 3 +++
kernel/bpf/path_iter.c | 58 ++++++++++++++++++++++++++++++++++++++++++
kernel/bpf/verifier.c | 5 ++++
4 files changed, 67 insertions(+)
create mode 100644 kernel/bpf/path_iter.c
diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
index 3a335c50e6e3..454a650d934e 100644
--- a/kernel/bpf/Makefile
+++ b/kernel/bpf/Makefile
@@ -56,6 +56,7 @@ obj-$(CONFIG_BPF_SYSCALL) += kmem_cache_iter.o
ifeq ($(CONFIG_DMA_SHARED_BUFFER),y)
obj-$(CONFIG_BPF_SYSCALL) += dmabuf_iter.o
endif
+obj-$(CONFIG_BPF_SYSCALL) += path_iter.o
CFLAGS_REMOVE_percpu_freelist.o = $(CC_FLAGS_FTRACE)
CFLAGS_REMOVE_bpf_lru_list.o = $(CC_FLAGS_FTRACE)
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index b71e428ad936..b190c78e40f6 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -3397,6 +3397,9 @@ BTF_ID_FLAGS(func, bpf_iter_dmabuf_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPAB
BTF_ID_FLAGS(func, bpf_iter_dmabuf_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
#endif
BTF_ID_FLAGS(func, __bpf_trap)
+BTF_ID_FLAGS(func, bpf_iter_path_new, KF_ITER_NEW | KF_SLEEPABLE)
+BTF_ID_FLAGS(func, bpf_iter_path_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE)
+BTF_ID_FLAGS(func, bpf_iter_path_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
BTF_KFUNCS_END(common_btf_ids)
static const struct btf_kfunc_id_set common_kfunc_set = {
diff --git a/kernel/bpf/path_iter.c b/kernel/bpf/path_iter.c
new file mode 100644
index 000000000000..0d972ec84beb
--- /dev/null
+++ b/kernel/bpf/path_iter.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
+#include <linux/bpf.h>
+#include <linux/bpf_mem_alloc.h>
+#include <linux/namei.h>
+#include <linux/path.h>
+
+/* open-coded iterator */
+struct bpf_iter_path {
+ __u64 __opaque[3];
+} __aligned(8);
+
+struct bpf_iter_path_kern {
+ struct path path;
+ __u64 flags;
+} __aligned(8);
+
+__bpf_kfunc_start_defs();
+
+__bpf_kfunc int bpf_iter_path_new(struct bpf_iter_path *it,
+ struct path *start,
+ __u64 flags)
+{
+ struct bpf_iter_path_kern *kit = (void *)it;
+
+ BUILD_BUG_ON(sizeof(*kit) > sizeof(*it));
+ BUILD_BUG_ON(__alignof__(*kit) != __alignof__(*it));
+
+ if (flags) {
+ memset(&kit->path, 0, sizeof(struct path));
+ return -EINVAL;
+ }
+
+ kit->path = *start;
+ path_get(&kit->path);
+ kit->flags = flags;
+
+ return 0;
+}
+
+__bpf_kfunc struct path *bpf_iter_path_next(struct bpf_iter_path *it)
+{
+ struct bpf_iter_path_kern *kit = (void *)it;
+ struct path root = {};
+
+ if (!path_walk_parent(&kit->path, &root))
+ return NULL;
+ return &kit->path;
+}
+
+__bpf_kfunc void bpf_iter_path_destroy(struct bpf_iter_path *it)
+{
+ struct bpf_iter_path_kern *kit = (void *)it;
+
+ path_put(&kit->path);
+}
+
+__bpf_kfunc_end_defs();
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index a7d6e0c5928b..45b45cdfb223 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -7036,6 +7036,10 @@ BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket) {
struct sock *sk;
};
+BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct path) {
+ struct dentry *dentry;
+};
+
static bool type_is_rcu(struct bpf_verifier_env *env,
struct bpf_reg_state *reg,
const char *field_name, u32 btf_id)
@@ -7076,6 +7080,7 @@ static bool type_is_trusted_or_null(struct bpf_verifier_env *env,
const char *field_name, u32 btf_id)
{
BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket));
+ BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct path));
return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id,
"__safe_trusted_or_null");
--
2.47.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 bpf-next 4/4] selftests/bpf: Add tests for bpf path iterator
2025-06-03 6:59 [PATCH v2 bpf-next 0/4] bpf path iterator Song Liu
` (2 preceding siblings ...)
2025-06-03 6:59 ` [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator Song Liu
@ 2025-06-03 6:59 ` Song Liu
3 siblings, 0 replies; 23+ messages in thread
From: Song Liu @ 2025-06-03 6:59 UTC (permalink / raw)
To: bpf, linux-fsdevel, linux-kernel, linux-security-module
Cc: kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton,
josef, mic, gnoack, m, Song Liu
Add tests for bpf path iterator, including test cases similar to real
workload (call bpf_path_d_path and bpf_get_dentry_xattr), and test cases
where the verifier rejects invalid use of the iterator.
Signed-off-by: Song Liu <song@kernel.org>
---
.../testing/selftests/bpf/bpf_experimental.h | 6 +
.../selftests/bpf/prog_tests/path_iter.c | 12 ++
tools/testing/selftests/bpf/progs/path_iter.c | 134 ++++++++++++++++++
3 files changed, 152 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/path_iter.c
create mode 100644 tools/testing/selftests/bpf/progs/path_iter.c
diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h
index 5e512a1d09d1..cbb759b473df 100644
--- a/tools/testing/selftests/bpf/bpf_experimental.h
+++ b/tools/testing/selftests/bpf/bpf_experimental.h
@@ -596,4 +596,10 @@ extern int bpf_iter_dmabuf_new(struct bpf_iter_dmabuf *it) __weak __ksym;
extern struct dma_buf *bpf_iter_dmabuf_next(struct bpf_iter_dmabuf *it) __weak __ksym;
extern void bpf_iter_dmabuf_destroy(struct bpf_iter_dmabuf *it) __weak __ksym;
+struct bpf_iter_path;
+extern int bpf_iter_path_new(struct bpf_iter_path *it, struct path *start,
+ __u64 flags) __weak __ksym;
+extern struct path *bpf_iter_path_next(struct bpf_iter_path *it) __weak __ksym;
+extern void bpf_iter_path_destroy(struct bpf_iter_path *it) __weak __ksym;
+
#endif
diff --git a/tools/testing/selftests/bpf/prog_tests/path_iter.c b/tools/testing/selftests/bpf/prog_tests/path_iter.c
new file mode 100644
index 000000000000..3c99c24fbd96
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/path_iter.c
@@ -0,0 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
+
+#include <test_progs.h>
+#include <bpf/libbpf.h>
+#include <bpf/btf.h>
+#include "path_iter.skel.h"
+
+void test_path_iter(void)
+{
+ RUN_TESTS(path_iter);
+}
diff --git a/tools/testing/selftests/bpf/progs/path_iter.c b/tools/testing/selftests/bpf/progs/path_iter.c
new file mode 100644
index 000000000000..be804fb4302c
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/path_iter.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include "bpf_misc.h"
+#include "bpf_experimental.h"
+
+char _license[] SEC("license") = "GPL";
+
+char path_name[256];
+char xattr_val[64];
+
+static __always_inline void access_path_dentry(struct path *p)
+{
+ struct bpf_dynptr ptr;
+ struct dentry *dentry;
+
+ if (!p)
+ return;
+
+ bpf_dynptr_from_mem(xattr_val, sizeof(xattr_val), 0, &ptr);
+ bpf_path_d_path(p, path_name, sizeof(path_name));
+
+ dentry = p->dentry;
+ if (dentry)
+ bpf_get_dentry_xattr(dentry, "user.xattr", &ptr);
+}
+
+SEC("lsm.s/file_open")
+__success
+int BPF_PROG(open_code, struct file *f)
+{
+ struct bpf_iter_path path_it;
+ struct path *p;
+ int ret;
+
+ ret = bpf_iter_path_new(&path_it, &f->f_path, 0);
+ if (ret) {
+ bpf_iter_path_destroy(&path_it);
+ return 0;
+ }
+
+ p = bpf_iter_path_next(&path_it);
+ access_path_dentry(p);
+ bpf_iter_path_destroy(&path_it);
+
+ return 0;
+}
+
+SEC("lsm.s/file_open")
+__success
+int BPF_PROG(for_each, struct file *f)
+{
+ struct path *p;
+
+ bpf_for_each(path, p, &f->f_path, 0)
+ access_path_dentry(p);
+
+ return 0;
+}
+
+SEC("lsm.s/file_open")
+__failure __msg("Unreleased reference")
+int BPF_PROG(missing_destroy, struct file *f)
+{
+ struct bpf_iter_path path_it;
+
+ bpf_iter_path_new(&path_it, &f->f_path, 0);
+
+ return 0;
+}
+
+SEC("lsm.s/file_open")
+__failure __msg("expected an initialized iter_path")
+int BPF_PROG(missing_new, struct file *f)
+{
+ struct bpf_iter_path path_it;
+
+ bpf_iter_path_destroy(&path_it);
+ return 0;
+}
+
+SEC("lsm.s/file_open")
+__failure __msg("expected uninitialized iter_path")
+int BPF_PROG(new_twice, struct file *f)
+{
+ struct bpf_iter_path path_it;
+
+ bpf_iter_path_new(&path_it, &f->f_path, 0);
+ bpf_iter_path_new(&path_it, &f->f_path, 0);
+ bpf_iter_path_destroy(&path_it);
+ return 0;
+}
+
+SEC("lsm.s/file_open")
+__failure __msg("expected an initialized iter_path")
+int BPF_PROG(destroy_twice, struct file *f)
+{
+ struct bpf_iter_path path_it;
+
+ bpf_iter_path_new(&path_it, &f->f_path, 0);
+ bpf_iter_path_destroy(&path_it);
+ bpf_iter_path_destroy(&path_it);
+ return 0;
+}
+
+SEC("lsm.s/file_open")
+__success
+int BPF_PROG(reuse_path_iter, struct file *f)
+{
+ struct bpf_iter_path path_it;
+
+ bpf_iter_path_new(&path_it, &f->f_path, 0);
+ bpf_iter_path_destroy(&path_it);
+ bpf_iter_path_new(&path_it, &f->f_path, 0);
+ bpf_iter_path_destroy(&path_it);
+ return 0;
+}
+
+SEC("lsm.s/file_open")
+__failure __msg("invalid read from stack off")
+int BPF_PROG(invalid_read_path_iter, struct file *f)
+{
+ struct bpf_iter_path path_it;
+ struct bpf_iter_path path_it_2;
+
+
+ bpf_iter_path_new(&path_it, &f->f_path, 0);
+ path_it_2 = path_it;
+ bpf_iter_path_destroy(&path_it_2);
+ return 0;
+}
--
2.47.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 2/4] landlock: Use path_walk_parent()
2025-06-03 6:59 ` [PATCH v2 bpf-next 2/4] landlock: Use path_walk_parent() Song Liu
@ 2025-06-03 13:46 ` Mickaël Salaün
2025-06-04 19:37 ` Song Liu
0 siblings, 1 reply; 23+ messages in thread
From: Mickaël Salaün @ 2025-06-03 13:46 UTC (permalink / raw)
To: Song Liu
Cc: bpf, linux-fsdevel, linux-kernel, linux-security-module,
kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton,
josef, gnoack, m
Landlock tests with hostfs fail:
ok 126 layout3_fs.hostfs.tag_inode_file
# RUN layout3_fs.hostfs.release_inodes ...
# fs_test.c:5555:release_inodes:Expected EACCES (13) == test_open(TMP_DIR, O_RDONLY) (0)
This specific test checks that an access to a (denied) mount point over
an allowed directory is indeed denied.
It's not clear to me the origin of the issue, but it seems to be related
to choose_mountpoint().
You can run these tests with `check-linux.sh build kselftest` from
https://github.com/landlock-lsm/landlock-test-tools
Just in case, please always run clang-format -i security/landlock/*.[ch]
On Mon, Jun 02, 2025 at 11:59:18PM -0700, Song Liu wrote:
> Use path_walk_parent() to walk a path up to its parent.
>
> No functional changes intended.
>
> Signed-off-by: Song Liu <song@kernel.org>
> ---
> security/landlock/fs.c | 31 ++++++++++---------------------
> 1 file changed, 10 insertions(+), 21 deletions(-)
>
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index 6fee7c20f64d..3adac544dc9e 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -837,8 +837,8 @@ static bool is_access_to_paths_allowed(
> * restriction.
> */
> while (true) {
> - struct dentry *parent_dentry;
> const struct landlock_rule *rule;
> + struct path root = {};
>
> /*
> * If at least all accesses allowed on the destination are
> @@ -895,34 +895,23 @@ static bool is_access_to_paths_allowed(
> /* Stops when a rule from each layer grants access. */
> if (allowed_parent1 && allowed_parent2)
> break;
> -jump_up:
> - if (walker_path.dentry == walker_path.mnt->mnt_root) {
> - if (follow_up(&walker_path)) {
> - /* Ignores hidden mount points. */
> - goto jump_up;
> - } else {
> - /*
> - * Stops at the real root. Denies access
> - * because not all layers have granted access.
> - */
> - break;
> - }
> - }
> +
> + if (path_walk_parent(&walker_path, &root))
> + continue;
It would be better to avoid a "continue" statement but to just use an if
block.
> +
> if (unlikely(IS_ROOT(walker_path.dentry))) {
> /*
> - * Stops at disconnected root directories. Only allows
> - * access to internal filesystems (e.g. nsfs, which is
> - * reachable through /proc/<pid>/ns/<namespace>).
> + * Stops at disconnected or real root directories.
> + * Only allows access to internal filesystems
> + * (e.g. nsfs, which is reachable through
> + * /proc/<pid>/ns/<namespace>).
> */
> if (walker_path.mnt->mnt_flags & MNT_INTERNAL) {
> allowed_parent1 = true;
> allowed_parent2 = true;
> }
> - break;
> }
> - parent_dentry = dget_parent(walker_path.dentry);
> - dput(walker_path.dentry);
> - walker_path.dentry = parent_dentry;
> + break;
> }
> path_put(&walker_path);
>
> --
> 2.47.1
>
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator
2025-06-03 6:59 ` [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator Song Liu
@ 2025-06-03 15:13 ` Alexei Starovoitov
2025-06-04 17:22 ` Christian Brauner
2025-06-03 18:40 ` Andrii Nakryiko
2025-06-05 19:27 ` Matt Bobrowski
2 siblings, 1 reply; 23+ messages in thread
From: Alexei Starovoitov @ 2025-06-03 15:13 UTC (permalink / raw)
To: Song Liu
Cc: bpf, Linux-Fsdevel, LKML, LSM List, Kernel Team, Andrii Nakryiko,
Eduard, Alexei Starovoitov, Daniel Borkmann, Martin KaFai Lau,
Alexander Viro, Christian Brauner, Jan Kara, KP Singh,
Matt Bobrowski, Amir Goldstein, repnop, Jeff Layton, Josef Bacik,
Mickaël Salaün, Günther Noack, m
On Mon, Jun 2, 2025 at 11:59 PM Song Liu <song@kernel.org> wrote:
>
> Introduce a path iterator, which reliably walk a struct path toward
> the root. This path iterator is based on path_walk_parent. A fixed
> zero'ed root is passed to path_walk_parent(). Therefore, unless the
> user terminates it earlier, the iterator will terminate at the real
> root.
>
> Signed-off-by: Song Liu <song@kernel.org>
> ---
> kernel/bpf/Makefile | 1 +
> kernel/bpf/helpers.c | 3 +++
> kernel/bpf/path_iter.c | 58 ++++++++++++++++++++++++++++++++++++++++++
> kernel/bpf/verifier.c | 5 ++++
> 4 files changed, 67 insertions(+)
> create mode 100644 kernel/bpf/path_iter.c
>
> diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
> index 3a335c50e6e3..454a650d934e 100644
> --- a/kernel/bpf/Makefile
> +++ b/kernel/bpf/Makefile
> @@ -56,6 +56,7 @@ obj-$(CONFIG_BPF_SYSCALL) += kmem_cache_iter.o
> ifeq ($(CONFIG_DMA_SHARED_BUFFER),y)
> obj-$(CONFIG_BPF_SYSCALL) += dmabuf_iter.o
> endif
> +obj-$(CONFIG_BPF_SYSCALL) += path_iter.o
>
> CFLAGS_REMOVE_percpu_freelist.o = $(CC_FLAGS_FTRACE)
> CFLAGS_REMOVE_bpf_lru_list.o = $(CC_FLAGS_FTRACE)
> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> index b71e428ad936..b190c78e40f6 100644
> --- a/kernel/bpf/helpers.c
> +++ b/kernel/bpf/helpers.c
> @@ -3397,6 +3397,9 @@ BTF_ID_FLAGS(func, bpf_iter_dmabuf_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPAB
> BTF_ID_FLAGS(func, bpf_iter_dmabuf_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
> #endif
> BTF_ID_FLAGS(func, __bpf_trap)
> +BTF_ID_FLAGS(func, bpf_iter_path_new, KF_ITER_NEW | KF_SLEEPABLE)
> +BTF_ID_FLAGS(func, bpf_iter_path_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE)
> +BTF_ID_FLAGS(func, bpf_iter_path_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
> BTF_KFUNCS_END(common_btf_ids)
>
> static const struct btf_kfunc_id_set common_kfunc_set = {
> diff --git a/kernel/bpf/path_iter.c b/kernel/bpf/path_iter.c
> new file mode 100644
> index 000000000000..0d972ec84beb
> --- /dev/null
> +++ b/kernel/bpf/path_iter.c
I think Christian's preference was to keep
everything in fs/bpf_fs_kfuncs.c
Don't add a new file. Just add this iter there.
> @@ -0,0 +1,58 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
> +#include <linux/bpf.h>
> +#include <linux/bpf_mem_alloc.h>
> +#include <linux/namei.h>
> +#include <linux/path.h>
> +
> +/* open-coded iterator */
> +struct bpf_iter_path {
> + __u64 __opaque[3];
> +} __aligned(8);
> +
> +struct bpf_iter_path_kern {
> + struct path path;
> + __u64 flags;
Why? flags is unused. Don't waste space for it.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator
2025-06-03 6:59 ` [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator Song Liu
2025-06-03 15:13 ` Alexei Starovoitov
@ 2025-06-03 18:40 ` Andrii Nakryiko
2025-06-03 20:49 ` Yonghong Song
2025-06-03 21:09 ` Song Liu
2025-06-05 19:27 ` Matt Bobrowski
2 siblings, 2 replies; 23+ messages in thread
From: Andrii Nakryiko @ 2025-06-03 18:40 UTC (permalink / raw)
To: Song Liu
Cc: bpf, linux-fsdevel, linux-kernel, linux-security-module,
kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton,
josef, mic, gnoack, m
On Mon, Jun 2, 2025 at 11:59 PM Song Liu <song@kernel.org> wrote:
>
> Introduce a path iterator, which reliably walk a struct path toward
> the root. This path iterator is based on path_walk_parent. A fixed
> zero'ed root is passed to path_walk_parent(). Therefore, unless the
> user terminates it earlier, the iterator will terminate at the real
> root.
>
> Signed-off-by: Song Liu <song@kernel.org>
> ---
> kernel/bpf/Makefile | 1 +
> kernel/bpf/helpers.c | 3 +++
> kernel/bpf/path_iter.c | 58 ++++++++++++++++++++++++++++++++++++++++++
> kernel/bpf/verifier.c | 5 ++++
> 4 files changed, 67 insertions(+)
> create mode 100644 kernel/bpf/path_iter.c
>
> diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
> index 3a335c50e6e3..454a650d934e 100644
> --- a/kernel/bpf/Makefile
> +++ b/kernel/bpf/Makefile
> @@ -56,6 +56,7 @@ obj-$(CONFIG_BPF_SYSCALL) += kmem_cache_iter.o
> ifeq ($(CONFIG_DMA_SHARED_BUFFER),y)
> obj-$(CONFIG_BPF_SYSCALL) += dmabuf_iter.o
> endif
> +obj-$(CONFIG_BPF_SYSCALL) += path_iter.o
>
> CFLAGS_REMOVE_percpu_freelist.o = $(CC_FLAGS_FTRACE)
> CFLAGS_REMOVE_bpf_lru_list.o = $(CC_FLAGS_FTRACE)
> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> index b71e428ad936..b190c78e40f6 100644
> --- a/kernel/bpf/helpers.c
> +++ b/kernel/bpf/helpers.c
> @@ -3397,6 +3397,9 @@ BTF_ID_FLAGS(func, bpf_iter_dmabuf_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPAB
> BTF_ID_FLAGS(func, bpf_iter_dmabuf_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
> #endif
> BTF_ID_FLAGS(func, __bpf_trap)
> +BTF_ID_FLAGS(func, bpf_iter_path_new, KF_ITER_NEW | KF_SLEEPABLE)
> +BTF_ID_FLAGS(func, bpf_iter_path_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE)
> +BTF_ID_FLAGS(func, bpf_iter_path_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
> BTF_KFUNCS_END(common_btf_ids)
>
> static const struct btf_kfunc_id_set common_kfunc_set = {
> diff --git a/kernel/bpf/path_iter.c b/kernel/bpf/path_iter.c
> new file mode 100644
> index 000000000000..0d972ec84beb
> --- /dev/null
> +++ b/kernel/bpf/path_iter.c
> @@ -0,0 +1,58 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
> +#include <linux/bpf.h>
> +#include <linux/bpf_mem_alloc.h>
> +#include <linux/namei.h>
> +#include <linux/path.h>
> +
> +/* open-coded iterator */
> +struct bpf_iter_path {
> + __u64 __opaque[3];
> +} __aligned(8);
> +
> +struct bpf_iter_path_kern {
> + struct path path;
> + __u64 flags;
> +} __aligned(8);
> +
> +__bpf_kfunc_start_defs();
> +
> +__bpf_kfunc int bpf_iter_path_new(struct bpf_iter_path *it,
> + struct path *start,
> + __u64 flags)
> +{
> + struct bpf_iter_path_kern *kit = (void *)it;
> +
> + BUILD_BUG_ON(sizeof(*kit) > sizeof(*it));
> + BUILD_BUG_ON(__alignof__(*kit) != __alignof__(*it));
> +
> + if (flags) {
> + memset(&kit->path, 0, sizeof(struct path));
> + return -EINVAL;
> + }
> +
> + kit->path = *start;
> + path_get(&kit->path);
> + kit->flags = flags;
> +
> + return 0;
> +}
> +
> +__bpf_kfunc struct path *bpf_iter_path_next(struct bpf_iter_path *it)
> +{
> + struct bpf_iter_path_kern *kit = (void *)it;
> + struct path root = {};
> +
> + if (!path_walk_parent(&kit->path, &root))
> + return NULL;
> + return &kit->path;
> +}
> +
> +__bpf_kfunc void bpf_iter_path_destroy(struct bpf_iter_path *it)
> +{
> + struct bpf_iter_path_kern *kit = (void *)it;
> +
> + path_put(&kit->path);
note, destroy() will be called even if construction of iterator fails
or we exhausted iterator. So you need to make sure that you have
bpf_iter_path state where you can detect that there is no path present
and skip path_put().
> +}
> +
> +__bpf_kfunc_end_defs();
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index a7d6e0c5928b..45b45cdfb223 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -7036,6 +7036,10 @@ BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket) {
> struct sock *sk;
> };
>
> +BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct path) {
> + struct dentry *dentry;
> +};
> +
> static bool type_is_rcu(struct bpf_verifier_env *env,
> struct bpf_reg_state *reg,
> const char *field_name, u32 btf_id)
> @@ -7076,6 +7080,7 @@ static bool type_is_trusted_or_null(struct bpf_verifier_env *env,
> const char *field_name, u32 btf_id)
> {
> BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket));
> + BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct path));
>
> return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id,
> "__safe_trusted_or_null");
> --
> 2.47.1
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator
2025-06-03 18:40 ` Andrii Nakryiko
@ 2025-06-03 20:49 ` Yonghong Song
2025-06-03 21:10 ` Song Liu
2025-06-03 21:09 ` Song Liu
1 sibling, 1 reply; 23+ messages in thread
From: Yonghong Song @ 2025-06-03 20:49 UTC (permalink / raw)
To: Andrii Nakryiko, Song Liu
Cc: bpf, linux-fsdevel, linux-kernel, linux-security-module,
kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton,
josef, mic, gnoack, m
On 6/3/25 11:40 AM, Andrii Nakryiko wrote:
> On Mon, Jun 2, 2025 at 11:59 PM Song Liu <song@kernel.org> wrote:
>> Introduce a path iterator, which reliably walk a struct path toward
>> the root. This path iterator is based on path_walk_parent. A fixed
>> zero'ed root is passed to path_walk_parent(). Therefore, unless the
>> user terminates it earlier, the iterator will terminate at the real
>> root.
>>
>> Signed-off-by: Song Liu <song@kernel.org>
>> ---
>> kernel/bpf/Makefile | 1 +
>> kernel/bpf/helpers.c | 3 +++
>> kernel/bpf/path_iter.c | 58 ++++++++++++++++++++++++++++++++++++++++++
>> kernel/bpf/verifier.c | 5 ++++
>> 4 files changed, 67 insertions(+)
>> create mode 100644 kernel/bpf/path_iter.c
>>
>> diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
>> index 3a335c50e6e3..454a650d934e 100644
>> --- a/kernel/bpf/Makefile
>> +++ b/kernel/bpf/Makefile
>> @@ -56,6 +56,7 @@ obj-$(CONFIG_BPF_SYSCALL) += kmem_cache_iter.o
>> ifeq ($(CONFIG_DMA_SHARED_BUFFER),y)
>> obj-$(CONFIG_BPF_SYSCALL) += dmabuf_iter.o
>> endif
>> +obj-$(CONFIG_BPF_SYSCALL) += path_iter.o
>>
>> CFLAGS_REMOVE_percpu_freelist.o = $(CC_FLAGS_FTRACE)
>> CFLAGS_REMOVE_bpf_lru_list.o = $(CC_FLAGS_FTRACE)
>> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
>> index b71e428ad936..b190c78e40f6 100644
>> --- a/kernel/bpf/helpers.c
>> +++ b/kernel/bpf/helpers.c
>> @@ -3397,6 +3397,9 @@ BTF_ID_FLAGS(func, bpf_iter_dmabuf_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPAB
>> BTF_ID_FLAGS(func, bpf_iter_dmabuf_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
>> #endif
>> BTF_ID_FLAGS(func, __bpf_trap)
>> +BTF_ID_FLAGS(func, bpf_iter_path_new, KF_ITER_NEW | KF_SLEEPABLE)
>> +BTF_ID_FLAGS(func, bpf_iter_path_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE)
>> +BTF_ID_FLAGS(func, bpf_iter_path_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
>> BTF_KFUNCS_END(common_btf_ids)
>>
>> static const struct btf_kfunc_id_set common_kfunc_set = {
>> diff --git a/kernel/bpf/path_iter.c b/kernel/bpf/path_iter.c
>> new file mode 100644
>> index 000000000000..0d972ec84beb
>> --- /dev/null
>> +++ b/kernel/bpf/path_iter.c
>> @@ -0,0 +1,58 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
>> +#include <linux/bpf.h>
>> +#include <linux/bpf_mem_alloc.h>
>> +#include <linux/namei.h>
>> +#include <linux/path.h>
>> +
>> +/* open-coded iterator */
>> +struct bpf_iter_path {
>> + __u64 __opaque[3];
>> +} __aligned(8);
>> +
>> +struct bpf_iter_path_kern {
>> + struct path path;
>> + __u64 flags;
>> +} __aligned(8);
>> +
>> +__bpf_kfunc_start_defs();
>> +
>> +__bpf_kfunc int bpf_iter_path_new(struct bpf_iter_path *it,
>> + struct path *start,
>> + __u64 flags)
>> +{
>> + struct bpf_iter_path_kern *kit = (void *)it;
>> +
>> + BUILD_BUG_ON(sizeof(*kit) > sizeof(*it));
>> + BUILD_BUG_ON(__alignof__(*kit) != __alignof__(*it));
>> +
>> + if (flags) {
>> + memset(&kit->path, 0, sizeof(struct path));
>> + return -EINVAL;
>> + }
>> +
>> + kit->path = *start;
>> + path_get(&kit->path);
>> + kit->flags = flags;
>> +
>> + return 0;
>> +}
>> +
>> +__bpf_kfunc struct path *bpf_iter_path_next(struct bpf_iter_path *it)
>> +{
>> + struct bpf_iter_path_kern *kit = (void *)it;
>> + struct path root = {};
>> +
>> + if (!path_walk_parent(&kit->path, &root))
>> + return NULL;
>> + return &kit->path;
>> +}
>> +
>> +__bpf_kfunc void bpf_iter_path_destroy(struct bpf_iter_path *it)
>> +{
>> + struct bpf_iter_path_kern *kit = (void *)it;
>> +
>> + path_put(&kit->path);
> note, destroy() will be called even if construction of iterator fails
> or we exhausted iterator. So you need to make sure that you have
> bpf_iter_path state where you can detect that there is no path present
> and skip path_put().
In rare cases, it is possible &kit->path address could be destroyed
and reused, right? Maybe we need more state in kit to detect the change?
>
>> +}
>> +
>> +__bpf_kfunc_end_defs();
>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>> index a7d6e0c5928b..45b45cdfb223 100644
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
>> @@ -7036,6 +7036,10 @@ BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket) {
>> struct sock *sk;
>> };
>>
>> +BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct path) {
>> + struct dentry *dentry;
>> +};
>> +
>> static bool type_is_rcu(struct bpf_verifier_env *env,
>> struct bpf_reg_state *reg,
>> const char *field_name, u32 btf_id)
>> @@ -7076,6 +7080,7 @@ static bool type_is_trusted_or_null(struct bpf_verifier_env *env,
>> const char *field_name, u32 btf_id)
>> {
>> BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket));
>> + BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct path));
>>
>> return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id,
>> "__safe_trusted_or_null");
>> --
>> 2.47.1
>>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator
2025-06-03 18:40 ` Andrii Nakryiko
2025-06-03 20:49 ` Yonghong Song
@ 2025-06-03 21:09 ` Song Liu
2025-06-03 21:44 ` Andrii Nakryiko
1 sibling, 1 reply; 23+ messages in thread
From: Song Liu @ 2025-06-03 21:09 UTC (permalink / raw)
To: Andrii Nakryiko
Cc: bpf, linux-fsdevel, linux-kernel, linux-security-module,
kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton,
josef, mic, gnoack, m
On Tue, Jun 3, 2025 at 11:40 AM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
[...]
> > +__bpf_kfunc struct path *bpf_iter_path_next(struct bpf_iter_path *it)
> > +{
> > + struct bpf_iter_path_kern *kit = (void *)it;
> > + struct path root = {};
> > +
> > + if (!path_walk_parent(&kit->path, &root))
> > + return NULL;
> > + return &kit->path;
> > +}
> > +
> > +__bpf_kfunc void bpf_iter_path_destroy(struct bpf_iter_path *it)
> > +{
> > + struct bpf_iter_path_kern *kit = (void *)it;
> > +
> > + path_put(&kit->path);
>
> note, destroy() will be called even if construction of iterator fails
> or we exhausted iterator. So you need to make sure that you have
> bpf_iter_path state where you can detect that there is no path present
> and skip path_put().
In bpf_iter_path_next(), when path_walk_parent() returns false, we
still hold reference to kit->path, then _destroy() will release it. So we
should be fine, no?
Thanks,
Song
>
> > +}
> > +
> > +__bpf_kfunc_end_defs();
> > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > index a7d6e0c5928b..45b45cdfb223 100644
> > --- a/kernel/bpf/verifier.c
> > +++ b/kernel/bpf/verifier.c
> > @@ -7036,6 +7036,10 @@ BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket) {
> > struct sock *sk;
> > };
> >
> > +BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct path) {
> > + struct dentry *dentry;
> > +};
> > +
> > static bool type_is_rcu(struct bpf_verifier_env *env,
> > struct bpf_reg_state *reg,
> > const char *field_name, u32 btf_id)
> > @@ -7076,6 +7080,7 @@ static bool type_is_trusted_or_null(struct bpf_verifier_env *env,
> > const char *field_name, u32 btf_id)
> > {
> > BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket));
> > + BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct path));
> >
> > return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id,
> > "__safe_trusted_or_null");
> > --
> > 2.47.1
> >
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator
2025-06-03 20:49 ` Yonghong Song
@ 2025-06-03 21:10 ` Song Liu
0 siblings, 0 replies; 23+ messages in thread
From: Song Liu @ 2025-06-03 21:10 UTC (permalink / raw)
To: Yonghong Song
Cc: Andrii Nakryiko, bpf, linux-fsdevel, linux-kernel,
linux-security-module, kernel-team, andrii, eddyz87, ast, daniel,
martin.lau, viro, brauner, jack, kpsingh, mattbobrowski, amir73il,
repnop, jlayton, josef, mic, gnoack, m
On Tue, Jun 3, 2025 at 1:50 PM Yonghong Song <yonghong.song@linux.dev> wrote:
>
>
>
> On 6/3/25 11:40 AM, Andrii Nakryiko wrote:
> > On Mon, Jun 2, 2025 at 11:59 PM Song Liu <song@kernel.org> wrote:
> >> Introduce a path iterator, which reliably walk a struct path toward
> >> the root. This path iterator is based on path_walk_parent. A fixed
> >> zero'ed root is passed to path_walk_parent(). Therefore, unless the
> >> user terminates it earlier, the iterator will terminate at the real
> >> root.
> >>
> >> Signed-off-by: Song Liu <song@kernel.org>
> >> ---
> >> kernel/bpf/Makefile | 1 +
> >> kernel/bpf/helpers.c | 3 +++
> >> kernel/bpf/path_iter.c | 58 ++++++++++++++++++++++++++++++++++++++++++
> >> kernel/bpf/verifier.c | 5 ++++
> >> 4 files changed, 67 insertions(+)
> >> create mode 100644 kernel/bpf/path_iter.c
> >>
> >> diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
> >> index 3a335c50e6e3..454a650d934e 100644
> >> --- a/kernel/bpf/Makefile
> >> +++ b/kernel/bpf/Makefile
> >> @@ -56,6 +56,7 @@ obj-$(CONFIG_BPF_SYSCALL) += kmem_cache_iter.o
> >> ifeq ($(CONFIG_DMA_SHARED_BUFFER),y)
> >> obj-$(CONFIG_BPF_SYSCALL) += dmabuf_iter.o
> >> endif
> >> +obj-$(CONFIG_BPF_SYSCALL) += path_iter.o
> >>
> >> CFLAGS_REMOVE_percpu_freelist.o = $(CC_FLAGS_FTRACE)
> >> CFLAGS_REMOVE_bpf_lru_list.o = $(CC_FLAGS_FTRACE)
> >> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> >> index b71e428ad936..b190c78e40f6 100644
> >> --- a/kernel/bpf/helpers.c
> >> +++ b/kernel/bpf/helpers.c
> >> @@ -3397,6 +3397,9 @@ BTF_ID_FLAGS(func, bpf_iter_dmabuf_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPAB
> >> BTF_ID_FLAGS(func, bpf_iter_dmabuf_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
> >> #endif
> >> BTF_ID_FLAGS(func, __bpf_trap)
> >> +BTF_ID_FLAGS(func, bpf_iter_path_new, KF_ITER_NEW | KF_SLEEPABLE)
> >> +BTF_ID_FLAGS(func, bpf_iter_path_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE)
> >> +BTF_ID_FLAGS(func, bpf_iter_path_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
> >> BTF_KFUNCS_END(common_btf_ids)
> >>
> >> static const struct btf_kfunc_id_set common_kfunc_set = {
> >> diff --git a/kernel/bpf/path_iter.c b/kernel/bpf/path_iter.c
> >> new file mode 100644
> >> index 000000000000..0d972ec84beb
> >> --- /dev/null
> >> +++ b/kernel/bpf/path_iter.c
> >> @@ -0,0 +1,58 @@
> >> +// SPDX-License-Identifier: GPL-2.0-only
> >> +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
> >> +#include <linux/bpf.h>
> >> +#include <linux/bpf_mem_alloc.h>
> >> +#include <linux/namei.h>
> >> +#include <linux/path.h>
> >> +
> >> +/* open-coded iterator */
> >> +struct bpf_iter_path {
> >> + __u64 __opaque[3];
> >> +} __aligned(8);
> >> +
> >> +struct bpf_iter_path_kern {
> >> + struct path path;
> >> + __u64 flags;
> >> +} __aligned(8);
> >> +
> >> +__bpf_kfunc_start_defs();
> >> +
> >> +__bpf_kfunc int bpf_iter_path_new(struct bpf_iter_path *it,
> >> + struct path *start,
> >> + __u64 flags)
> >> +{
> >> + struct bpf_iter_path_kern *kit = (void *)it;
> >> +
> >> + BUILD_BUG_ON(sizeof(*kit) > sizeof(*it));
> >> + BUILD_BUG_ON(__alignof__(*kit) != __alignof__(*it));
> >> +
> >> + if (flags) {
> >> + memset(&kit->path, 0, sizeof(struct path));
> >> + return -EINVAL;
> >> + }
> >> +
> >> + kit->path = *start;
> >> + path_get(&kit->path);
> >> + kit->flags = flags;
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +__bpf_kfunc struct path *bpf_iter_path_next(struct bpf_iter_path *it)
> >> +{
> >> + struct bpf_iter_path_kern *kit = (void *)it;
> >> + struct path root = {};
> >> +
> >> + if (!path_walk_parent(&kit->path, &root))
> >> + return NULL;
> >> + return &kit->path;
> >> +}
> >> +
> >> +__bpf_kfunc void bpf_iter_path_destroy(struct bpf_iter_path *it)
> >> +{
> >> + struct bpf_iter_path_kern *kit = (void *)it;
> >> +
> >> + path_put(&kit->path);
> > note, destroy() will be called even if construction of iterator fails
> > or we exhausted iterator. So you need to make sure that you have
> > bpf_iter_path state where you can detect that there is no path present
> > and skip path_put().
>
> In rare cases, it is possible &kit->path address could be destroyed
> and reused, right? Maybe we need more state in kit to detect the change?
kit->path is always referenced, so this should not happen.
Thanks,
Song
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator
2025-06-03 21:09 ` Song Liu
@ 2025-06-03 21:44 ` Andrii Nakryiko
2025-06-03 23:20 ` Song Liu
0 siblings, 1 reply; 23+ messages in thread
From: Andrii Nakryiko @ 2025-06-03 21:44 UTC (permalink / raw)
To: Song Liu
Cc: bpf, linux-fsdevel, linux-kernel, linux-security-module,
kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton,
josef, mic, gnoack, m
On Tue, Jun 3, 2025 at 2:09 PM Song Liu <song@kernel.org> wrote:
>
> On Tue, Jun 3, 2025 at 11:40 AM Andrii Nakryiko
> <andrii.nakryiko@gmail.com> wrote:
> [...]
> > > +__bpf_kfunc struct path *bpf_iter_path_next(struct bpf_iter_path *it)
> > > +{
> > > + struct bpf_iter_path_kern *kit = (void *)it;
> > > + struct path root = {};
> > > +
> > > + if (!path_walk_parent(&kit->path, &root))
> > > + return NULL;
> > > + return &kit->path;
> > > +}
> > > +
> > > +__bpf_kfunc void bpf_iter_path_destroy(struct bpf_iter_path *it)
> > > +{
> > > + struct bpf_iter_path_kern *kit = (void *)it;
> > > +
> > > + path_put(&kit->path);
> >
> > note, destroy() will be called even if construction of iterator fails
> > or we exhausted iterator. So you need to make sure that you have
> > bpf_iter_path state where you can detect that there is no path present
> > and skip path_put().
>
> In bpf_iter_path_next(), when path_walk_parent() returns false, we
> still hold reference to kit->path, then _destroy() will release it. So we
> should be fine, no?
you still need to handle iterators that failed to be initialized,
though? And one can argue that if path_walk_parent() returns false, we
need to put that last path before returning NULL, no?
>
> Thanks,
> Song
>
> >
> > > +}
> > > +
> > > +__bpf_kfunc_end_defs();
> > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> > > index a7d6e0c5928b..45b45cdfb223 100644
> > > --- a/kernel/bpf/verifier.c
> > > +++ b/kernel/bpf/verifier.c
> > > @@ -7036,6 +7036,10 @@ BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket) {
> > > struct sock *sk;
> > > };
> > >
> > > +BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct path) {
> > > + struct dentry *dentry;
> > > +};
> > > +
> > > static bool type_is_rcu(struct bpf_verifier_env *env,
> > > struct bpf_reg_state *reg,
> > > const char *field_name, u32 btf_id)
> > > @@ -7076,6 +7080,7 @@ static bool type_is_trusted_or_null(struct bpf_verifier_env *env,
> > > const char *field_name, u32 btf_id)
> > > {
> > > BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket));
> > > + BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct path));
> > >
> > > return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id,
> > > "__safe_trusted_or_null");
> > > --
> > > 2.47.1
> > >
> >
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator
2025-06-03 21:44 ` Andrii Nakryiko
@ 2025-06-03 23:20 ` Song Liu
2025-06-04 20:37 ` Andrii Nakryiko
0 siblings, 1 reply; 23+ messages in thread
From: Song Liu @ 2025-06-03 23:20 UTC (permalink / raw)
To: Andrii Nakryiko
Cc: bpf, linux-fsdevel, linux-kernel, linux-security-module,
kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton,
josef, mic, gnoack, m
On Tue, Jun 3, 2025 at 2:45 PM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Tue, Jun 3, 2025 at 2:09 PM Song Liu <song@kernel.org> wrote:
> >
> > On Tue, Jun 3, 2025 at 11:40 AM Andrii Nakryiko
> > <andrii.nakryiko@gmail.com> wrote:
> > [...]
> > > > +__bpf_kfunc struct path *bpf_iter_path_next(struct bpf_iter_path *it)
> > > > +{
> > > > + struct bpf_iter_path_kern *kit = (void *)it;
> > > > + struct path root = {};
> > > > +
> > > > + if (!path_walk_parent(&kit->path, &root))
> > > > + return NULL;
> > > > + return &kit->path;
> > > > +}
> > > > +
> > > > +__bpf_kfunc void bpf_iter_path_destroy(struct bpf_iter_path *it)
> > > > +{
> > > > + struct bpf_iter_path_kern *kit = (void *)it;
> > > > +
> > > > + path_put(&kit->path);
> > >
> > > note, destroy() will be called even if construction of iterator fails
> > > or we exhausted iterator. So you need to make sure that you have
> > > bpf_iter_path state where you can detect that there is no path present
> > > and skip path_put().
> >
> > In bpf_iter_path_next(), when path_walk_parent() returns false, we
> > still hold reference to kit->path, then _destroy() will release it. So we
> > should be fine, no?
>
> you still need to handle iterators that failed to be initialized,
> though? And one can argue that if path_walk_parent() returns false, we
> need to put that last path before returning NULL, no?
kit->path is zero'ed on initialization failures, so we can path_put() it
safely. For _next() returns NULL case, we can either put kit->path
in _destroy(), which is the logic now, or put kit->path in the last
_next() call and make _destroy() a no-op in that case. I don't have
a strong preference either way.
Thanks,
Song
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator
2025-06-03 15:13 ` Alexei Starovoitov
@ 2025-06-04 17:22 ` Christian Brauner
0 siblings, 0 replies; 23+ messages in thread
From: Christian Brauner @ 2025-06-04 17:22 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Song Liu, bpf, Linux-Fsdevel, LKML, LSM List, Kernel Team,
Andrii Nakryiko, Eduard, Alexei Starovoitov, Daniel Borkmann,
Martin KaFai Lau, Alexander Viro, Jan Kara, KP Singh,
Matt Bobrowski, Amir Goldstein, repnop, Jeff Layton, Josef Bacik,
Mickaël Salaün, Günther Noack, m
On Tue, Jun 03, 2025 at 08:13:18AM -0700, Alexei Starovoitov wrote:
> On Mon, Jun 2, 2025 at 11:59 PM Song Liu <song@kernel.org> wrote:
> >
> > Introduce a path iterator, which reliably walk a struct path toward
> > the root. This path iterator is based on path_walk_parent. A fixed
> > zero'ed root is passed to path_walk_parent(). Therefore, unless the
> > user terminates it earlier, the iterator will terminate at the real
> > root.
> >
> > Signed-off-by: Song Liu <song@kernel.org>
> > ---
> > kernel/bpf/Makefile | 1 +
> > kernel/bpf/helpers.c | 3 +++
> > kernel/bpf/path_iter.c | 58 ++++++++++++++++++++++++++++++++++++++++++
> > kernel/bpf/verifier.c | 5 ++++
> > 4 files changed, 67 insertions(+)
> > create mode 100644 kernel/bpf/path_iter.c
> >
> > diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
> > index 3a335c50e6e3..454a650d934e 100644
> > --- a/kernel/bpf/Makefile
> > +++ b/kernel/bpf/Makefile
> > @@ -56,6 +56,7 @@ obj-$(CONFIG_BPF_SYSCALL) += kmem_cache_iter.o
> > ifeq ($(CONFIG_DMA_SHARED_BUFFER),y)
> > obj-$(CONFIG_BPF_SYSCALL) += dmabuf_iter.o
> > endif
> > +obj-$(CONFIG_BPF_SYSCALL) += path_iter.o
> >
> > CFLAGS_REMOVE_percpu_freelist.o = $(CC_FLAGS_FTRACE)
> > CFLAGS_REMOVE_bpf_lru_list.o = $(CC_FLAGS_FTRACE)
> > diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> > index b71e428ad936..b190c78e40f6 100644
> > --- a/kernel/bpf/helpers.c
> > +++ b/kernel/bpf/helpers.c
> > @@ -3397,6 +3397,9 @@ BTF_ID_FLAGS(func, bpf_iter_dmabuf_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPAB
> > BTF_ID_FLAGS(func, bpf_iter_dmabuf_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
> > #endif
> > BTF_ID_FLAGS(func, __bpf_trap)
> > +BTF_ID_FLAGS(func, bpf_iter_path_new, KF_ITER_NEW | KF_SLEEPABLE)
> > +BTF_ID_FLAGS(func, bpf_iter_path_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE)
> > +BTF_ID_FLAGS(func, bpf_iter_path_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
> > BTF_KFUNCS_END(common_btf_ids)
> >
> > static const struct btf_kfunc_id_set common_kfunc_set = {
> > diff --git a/kernel/bpf/path_iter.c b/kernel/bpf/path_iter.c
> > new file mode 100644
> > index 000000000000..0d972ec84beb
> > --- /dev/null
> > +++ b/kernel/bpf/path_iter.c
>
> I think Christian's preference was to keep
> everything in fs/bpf_fs_kfuncs.c
Yes. And since that also adds new fs helpers I want to take that through
the VFS tree, please. I'll provide a stable branch as we do with all
other subsystems.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 2/4] landlock: Use path_walk_parent()
2025-06-03 13:46 ` Mickaël Salaün
@ 2025-06-04 19:37 ` Song Liu
2025-06-05 16:47 ` Song Liu
0 siblings, 1 reply; 23+ messages in thread
From: Song Liu @ 2025-06-04 19:37 UTC (permalink / raw)
To: Mickaël Salaün
Cc: bpf, linux-fsdevel, linux-kernel, linux-security-module,
kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton,
josef, gnoack, m
On Tue, Jun 3, 2025 at 6:46 AM Mickaël Salaün <mic@digikod.net> wrote:
>
> Landlock tests with hostfs fail:
>
> ok 126 layout3_fs.hostfs.tag_inode_file
> # RUN layout3_fs.hostfs.release_inodes ...
> # fs_test.c:5555:release_inodes:Expected EACCES (13) == test_open(TMP_DIR, O_RDONLY) (0)
>
> This specific test checks that an access to a (denied) mount point over
> an allowed directory is indeed denied.
I am having trouble understanding the test. It appears to me
the newly mounted tmpfs on /tmp is allowed, but accesses to
/ and thus mount point /tmp is denied? What would the walk in
is_access_to_paths_allowed look like?
> It's not clear to me the origin of the issue, but it seems to be related
> to choose_mountpoint().
>
> You can run these tests with `check-linux.sh build kselftest` from
> https://github.com/landlock-lsm/landlock-test-tools
How should I debug this test? printk doesn't seem to work.
Thanks,
Song
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator
2025-06-03 23:20 ` Song Liu
@ 2025-06-04 20:37 ` Andrii Nakryiko
0 siblings, 0 replies; 23+ messages in thread
From: Andrii Nakryiko @ 2025-06-04 20:37 UTC (permalink / raw)
To: Song Liu
Cc: bpf, linux-fsdevel, linux-kernel, linux-security-module,
kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton,
josef, mic, gnoack, m
On Tue, Jun 3, 2025 at 4:20 PM Song Liu <song@kernel.org> wrote:
>
> On Tue, Jun 3, 2025 at 2:45 PM Andrii Nakryiko
> <andrii.nakryiko@gmail.com> wrote:
> >
> > On Tue, Jun 3, 2025 at 2:09 PM Song Liu <song@kernel.org> wrote:
> > >
> > > On Tue, Jun 3, 2025 at 11:40 AM Andrii Nakryiko
> > > <andrii.nakryiko@gmail.com> wrote:
> > > [...]
> > > > > +__bpf_kfunc struct path *bpf_iter_path_next(struct bpf_iter_path *it)
> > > > > +{
> > > > > + struct bpf_iter_path_kern *kit = (void *)it;
> > > > > + struct path root = {};
> > > > > +
> > > > > + if (!path_walk_parent(&kit->path, &root))
> > > > > + return NULL;
> > > > > + return &kit->path;
> > > > > +}
> > > > > +
> > > > > +__bpf_kfunc void bpf_iter_path_destroy(struct bpf_iter_path *it)
> > > > > +{
> > > > > + struct bpf_iter_path_kern *kit = (void *)it;
> > > > > +
> > > > > + path_put(&kit->path);
> > > >
> > > > note, destroy() will be called even if construction of iterator fails
> > > > or we exhausted iterator. So you need to make sure that you have
> > > > bpf_iter_path state where you can detect that there is no path present
> > > > and skip path_put().
> > >
> > > In bpf_iter_path_next(), when path_walk_parent() returns false, we
> > > still hold reference to kit->path, then _destroy() will release it. So we
> > > should be fine, no?
> >
> > you still need to handle iterators that failed to be initialized,
> > though? And one can argue that if path_walk_parent() returns false, we
> > need to put that last path before returning NULL, no?
>
> kit->path is zero'ed on initialization failures, so we can path_put() it
> safely. For _next() returns NULL case, we can either put kit->path
> in _destroy(), which is the logic now, or put kit->path in the last
> _next() call and make _destroy() a no-op in that case. I don't have
> a strong preference either way.
I didn't realize path_put() is a no-op for zeroed-out struct path. I'd
probably leave a comment for future selves, I don't have strong
preference otherwise.
>
> Thanks,
> Song
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 2/4] landlock: Use path_walk_parent()
2025-06-04 19:37 ` Song Liu
@ 2025-06-05 16:47 ` Song Liu
2025-06-06 10:46 ` Mickaël Salaün
0 siblings, 1 reply; 23+ messages in thread
From: Song Liu @ 2025-06-05 16:47 UTC (permalink / raw)
To: Mickaël Salaün
Cc: bpf, linux-fsdevel, linux-kernel, linux-security-module,
kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton,
josef, gnoack, m
On Wed, Jun 4, 2025 at 12:37 PM Song Liu <song@kernel.org> wrote:
>
> On Tue, Jun 3, 2025 at 6:46 AM Mickaël Salaün <mic@digikod.net> wrote:
> >
> > Landlock tests with hostfs fail:
> >
> > ok 126 layout3_fs.hostfs.tag_inode_file
> > # RUN layout3_fs.hostfs.release_inodes ...
> > # fs_test.c:5555:release_inodes:Expected EACCES (13) == test_open(TMP_DIR, O_RDONLY) (0)
> >
> > This specific test checks that an access to a (denied) mount point over
> > an allowed directory is indeed denied.
I just realized this only fails on hostfs. AFAICT, hostfs is only used
by um. Do we really need this to behave the same on um+hostfs?
Thanks,
Song
>
> I am having trouble understanding the test. It appears to me
> the newly mounted tmpfs on /tmp is allowed, but accesses to
> / and thus mount point /tmp is denied? What would the walk in
> is_access_to_paths_allowed look like?
>
> > It's not clear to me the origin of the issue, but it seems to be related
> > to choose_mountpoint().
> >
> > You can run these tests with `check-linux.sh build kselftest` from
> > https://github.com/landlock-lsm/landlock-test-tools
>
> How should I debug this test? printk doesn't seem to work.
>
> Thanks,
> Song
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator
2025-06-03 6:59 ` [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator Song Liu
2025-06-03 15:13 ` Alexei Starovoitov
2025-06-03 18:40 ` Andrii Nakryiko
@ 2025-06-05 19:27 ` Matt Bobrowski
2025-06-05 21:14 ` Song Liu
2 siblings, 1 reply; 23+ messages in thread
From: Matt Bobrowski @ 2025-06-05 19:27 UTC (permalink / raw)
To: Song Liu
Cc: bpf, linux-fsdevel, linux-kernel, linux-security-module,
kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, amir73il, repnop, jlayton, josef, mic,
gnoack, m
On Mon, Jun 02, 2025 at 11:59:19PM -0700, Song Liu wrote:
> Introduce a path iterator, which reliably walk a struct path toward
> the root. This path iterator is based on path_walk_parent. A fixed
> zero'ed root is passed to path_walk_parent(). Therefore, unless the
> user terminates it earlier, the iterator will terminate at the real
> root.
>
> Signed-off-by: Song Liu <song@kernel.org>
> ---
> kernel/bpf/Makefile | 1 +
> kernel/bpf/helpers.c | 3 +++
> kernel/bpf/path_iter.c | 58 ++++++++++++++++++++++++++++++++++++++++++
> kernel/bpf/verifier.c | 5 ++++
> 4 files changed, 67 insertions(+)
> create mode 100644 kernel/bpf/path_iter.c
>
> diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
> index 3a335c50e6e3..454a650d934e 100644
> --- a/kernel/bpf/Makefile
> +++ b/kernel/bpf/Makefile
> @@ -56,6 +56,7 @@ obj-$(CONFIG_BPF_SYSCALL) += kmem_cache_iter.o
> ifeq ($(CONFIG_DMA_SHARED_BUFFER),y)
> obj-$(CONFIG_BPF_SYSCALL) += dmabuf_iter.o
> endif
> +obj-$(CONFIG_BPF_SYSCALL) += path_iter.o
>
> CFLAGS_REMOVE_percpu_freelist.o = $(CC_FLAGS_FTRACE)
> CFLAGS_REMOVE_bpf_lru_list.o = $(CC_FLAGS_FTRACE)
> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> index b71e428ad936..b190c78e40f6 100644
> --- a/kernel/bpf/helpers.c
> +++ b/kernel/bpf/helpers.c
> @@ -3397,6 +3397,9 @@ BTF_ID_FLAGS(func, bpf_iter_dmabuf_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPAB
> BTF_ID_FLAGS(func, bpf_iter_dmabuf_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
> #endif
> BTF_ID_FLAGS(func, __bpf_trap)
> +BTF_ID_FLAGS(func, bpf_iter_path_new, KF_ITER_NEW | KF_SLEEPABLE)
Hm, I'd expect KF_TRUSTED_ARGS to be enforced onto
bpf_iter_path_new(), no? Shouldn't this only be operating on a stable
struct path reference?
> +BTF_ID_FLAGS(func, bpf_iter_path_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE)
> +BTF_ID_FLAGS(func, bpf_iter_path_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
At this point, the claim is that such are only to be used from the
context of the BPF LSM. If true, I'd expect these BPF kfuncs to be
part of bpf_fs_kfunc_set_ids once moved into fs/bpf_fs_kfuncs.c.
> static const struct btf_kfunc_id_set common_kfunc_set = {
> diff --git a/kernel/bpf/path_iter.c b/kernel/bpf/path_iter.c
> new file mode 100644
> index 000000000000..0d972ec84beb
> --- /dev/null
> +++ b/kernel/bpf/path_iter.c
> @@ -0,0 +1,58 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
> +#include <linux/bpf.h>
> +#include <linux/bpf_mem_alloc.h>
> +#include <linux/namei.h>
> +#include <linux/path.h>
> +
> +/* open-coded iterator */
> +struct bpf_iter_path {
> + __u64 __opaque[3];
> +} __aligned(8);
> +
> +struct bpf_iter_path_kern {
> + struct path path;
> + __u64 flags;
> +} __aligned(8);
> +
> +__bpf_kfunc_start_defs();
> +
> +__bpf_kfunc int bpf_iter_path_new(struct bpf_iter_path *it,
> + struct path *start,
> + __u64 flags)
> +{
> + struct bpf_iter_path_kern *kit = (void *)it;
> +
> + BUILD_BUG_ON(sizeof(*kit) > sizeof(*it));
> + BUILD_BUG_ON(__alignof__(*kit) != __alignof__(*it));
> +
> + if (flags) {
> + memset(&kit->path, 0, sizeof(struct path));
This warrants a comment for sure. Also why not just zero it out
entirely?
> + return -EINVAL;
> + }
> +
> + kit->path = *start;
> + path_get(&kit->path);
> + kit->flags = flags;
> +
> + return 0;
> +}
> +
> +__bpf_kfunc struct path *bpf_iter_path_next(struct bpf_iter_path *it)
> +{
> + struct bpf_iter_path_kern *kit = (void *)it;
> + struct path root = {};
I think this also warrants a comment. Specifically, that unless the
loop is explicitly terminated, bpf_iter_path_next() will continue
looping until we've reached the global root of the VFS.
> + if (!path_walk_parent(&kit->path, &root))
> + return NULL;
> + return &kit->path;
> +}
> +
> +__bpf_kfunc void bpf_iter_path_destroy(struct bpf_iter_path *it)
> +{
> + struct bpf_iter_path_kern *kit = (void *)it;
> +
> + path_put(&kit->path);
> +}
> +
> +__bpf_kfunc_end_defs();
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index a7d6e0c5928b..45b45cdfb223 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -7036,6 +7036,10 @@ BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket) {
> struct sock *sk;
> };
>
> +BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct path) {
> + struct dentry *dentry;
> +};
Only trusted if struct path is trusted, and hence why KF_TRUSTED_ARGS
should be enforced.
> static bool type_is_rcu(struct bpf_verifier_env *env,
> struct bpf_reg_state *reg,
> const char *field_name, u32 btf_id)
> @@ -7076,6 +7080,7 @@ static bool type_is_trusted_or_null(struct bpf_verifier_env *env,
> const char *field_name, u32 btf_id)
> {
> BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct socket));
> + BTF_TYPE_EMIT(BTF_TYPE_SAFE_TRUSTED_OR_NULL(struct path));
>
> return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id,
> "__safe_trusted_or_null");
> --
> 2.47.1
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator
2025-06-05 19:27 ` Matt Bobrowski
@ 2025-06-05 21:14 ` Song Liu
0 siblings, 0 replies; 23+ messages in thread
From: Song Liu @ 2025-06-05 21:14 UTC (permalink / raw)
To: Matt Bobrowski
Cc: bpf, linux-fsdevel, linux-kernel, linux-security-module,
kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, amir73il, repnop, jlayton, josef, mic,
gnoack, m
On Thu, Jun 5, 2025 at 12:27 PM Matt Bobrowski <mattbobrowski@google.com> wrote:
>
> On Mon, Jun 02, 2025 at 11:59:19PM -0700, Song Liu wrote:
> > Introduce a path iterator, which reliably walk a struct path toward
> > the root. This path iterator is based on path_walk_parent. A fixed
> > zero'ed root is passed to path_walk_parent(). Therefore, unless the
> > user terminates it earlier, the iterator will terminate at the real
> > root.
> >
> > Signed-off-by: Song Liu <song@kernel.org>
> > ---
> > kernel/bpf/Makefile | 1 +
> > kernel/bpf/helpers.c | 3 +++
> > kernel/bpf/path_iter.c | 58 ++++++++++++++++++++++++++++++++++++++++++
> > kernel/bpf/verifier.c | 5 ++++
> > 4 files changed, 67 insertions(+)
> > create mode 100644 kernel/bpf/path_iter.c
> >
> > diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile
> > index 3a335c50e6e3..454a650d934e 100644
> > --- a/kernel/bpf/Makefile
> > +++ b/kernel/bpf/Makefile
> > @@ -56,6 +56,7 @@ obj-$(CONFIG_BPF_SYSCALL) += kmem_cache_iter.o
> > ifeq ($(CONFIG_DMA_SHARED_BUFFER),y)
> > obj-$(CONFIG_BPF_SYSCALL) += dmabuf_iter.o
> > endif
> > +obj-$(CONFIG_BPF_SYSCALL) += path_iter.o
> >
> > CFLAGS_REMOVE_percpu_freelist.o = $(CC_FLAGS_FTRACE)
> > CFLAGS_REMOVE_bpf_lru_list.o = $(CC_FLAGS_FTRACE)
> > diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> > index b71e428ad936..b190c78e40f6 100644
> > --- a/kernel/bpf/helpers.c
> > +++ b/kernel/bpf/helpers.c
> > @@ -3397,6 +3397,9 @@ BTF_ID_FLAGS(func, bpf_iter_dmabuf_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPAB
> > BTF_ID_FLAGS(func, bpf_iter_dmabuf_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
> > #endif
> > BTF_ID_FLAGS(func, __bpf_trap)
> > +BTF_ID_FLAGS(func, bpf_iter_path_new, KF_ITER_NEW | KF_SLEEPABLE)
>
> Hm, I'd expect KF_TRUSTED_ARGS to be enforced onto
> bpf_iter_path_new(), no? Shouldn't this only be operating on a stable
> struct path reference?
Good catch! Added KF_TRUSTED_ARGS. also added a test with
untrusted pointer.
>
> > +BTF_ID_FLAGS(func, bpf_iter_path_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPABLE)
> > +BTF_ID_FLAGS(func, bpf_iter_path_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
>
> At this point, the claim is that such are only to be used from the
> context of the BPF LSM. If true, I'd expect these BPF kfuncs to be
> part of bpf_fs_kfunc_set_ids once moved into fs/bpf_fs_kfuncs.c.
I moved this to fs/bpf_fs_kfuncs.c in the next version.
>
> > static const struct btf_kfunc_id_set common_kfunc_set = {
> > diff --git a/kernel/bpf/path_iter.c b/kernel/bpf/path_iter.c
> > new file mode 100644
> > index 000000000000..0d972ec84beb
> > --- /dev/null
> > +++ b/kernel/bpf/path_iter.c
> > @@ -0,0 +1,58 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */
> > +#include <linux/bpf.h>
> > +#include <linux/bpf_mem_alloc.h>
> > +#include <linux/namei.h>
> > +#include <linux/path.h>
> > +
> > +/* open-coded iterator */
> > +struct bpf_iter_path {
> > + __u64 __opaque[3];
> > +} __aligned(8);
> > +
> > +struct bpf_iter_path_kern {
> > + struct path path;
> > + __u64 flags;
> > +} __aligned(8);
> > +
> > +__bpf_kfunc_start_defs();
> > +
> > +__bpf_kfunc int bpf_iter_path_new(struct bpf_iter_path *it,
> > + struct path *start,
> > + __u64 flags)
> > +{
> > + struct bpf_iter_path_kern *kit = (void *)it;
> > +
> > + BUILD_BUG_ON(sizeof(*kit) > sizeof(*it));
> > + BUILD_BUG_ON(__alignof__(*kit) != __alignof__(*it));
> > +
> > + if (flags) {
> > + memset(&kit->path, 0, sizeof(struct path));
>
> This warrants a comment for sure. Also why not just zero it out
> entirely?
Added some comments in v3. Also "flags" is removed in v3.
Thanks,
Song
[...]
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 2/4] landlock: Use path_walk_parent()
2025-06-05 16:47 ` Song Liu
@ 2025-06-06 10:46 ` Mickaël Salaün
0 siblings, 0 replies; 23+ messages in thread
From: Mickaël Salaün @ 2025-06-06 10:46 UTC (permalink / raw)
To: Song Liu
Cc: bpf, linux-fsdevel, linux-kernel, linux-security-module,
kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton,
josef, gnoack, m
On Thu, Jun 05, 2025 at 09:47:36AM -0700, Song Liu wrote:
> On Wed, Jun 4, 2025 at 12:37 PM Song Liu <song@kernel.org> wrote:
> >
> > On Tue, Jun 3, 2025 at 6:46 AM Mickaël Salaün <mic@digikod.net> wrote:
> > >
> > > Landlock tests with hostfs fail:
> > >
> > > ok 126 layout3_fs.hostfs.tag_inode_file
> > > # RUN layout3_fs.hostfs.release_inodes ...
> > > # fs_test.c:5555:release_inodes:Expected EACCES (13) == test_open(TMP_DIR, O_RDONLY) (0)
> > >
> > > This specific test checks that an access to a (denied) mount point over
> > > an allowed directory is indeed denied.
>
> I just realized this only fails on hostfs. AFAICT, hostfs is only used
> by um. Do we really need this to behave the same on um+hostfs?
Yes, this would be a regression, and in fact it is not related to hostfs
and it would be a new security bug.
The issue is that the path_walk_parent() doesn't return the parent
dentry but the underlying mount point if any. When choose_mountpoint()
returns true, path_walk_parent() should continue to the following root
check and potentiall the dget_parent() call. We need to be careful with
the path_put() though.
This issue was only spotted by this hostfs test because this one adds a
rule which is tied to the inode of the mount which is in fact the same
inode of the mount point because the mount is a bind mount. I'll send a
new test that check the same thing but with tmpfs (for convenience, but
it would be the same for any filesystem).
>
> Thanks,
> Song
>
> >
> > I am having trouble understanding the test. It appears to me
> > the newly mounted tmpfs on /tmp is allowed, but accesses to
> > / and thus mount point /tmp is denied? What would the walk in
> > is_access_to_paths_allowed look like?
The test checks that a mount is not wrongly identified as the underlying
mount point.
> >
> > > It's not clear to me the origin of the issue, but it seems to be related
> > > to choose_mountpoint().
> > >
> > > You can run these tests with `check-linux.sh build kselftest` from
> > > https://github.com/landlock-lsm/landlock-test-tools
> >
> > How should I debug this test? printk doesn't seem to work.
The console log level is set to warn, so you can use pr_warn().
> >
> > Thanks,
> > Song
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 1/4] namei: Introduce new helper function path_walk_parent()
2025-06-03 6:59 ` [PATCH v2 bpf-next 1/4] namei: Introduce new helper function path_walk_parent() Song Liu
@ 2025-06-06 11:10 ` Mickaël Salaün
2025-06-06 14:40 ` Al Viro
1 sibling, 0 replies; 23+ messages in thread
From: Mickaël Salaün @ 2025-06-06 11:10 UTC (permalink / raw)
To: Song Liu
Cc: bpf, linux-fsdevel, linux-kernel, linux-security-module,
kernel-team, andrii, eddyz87, ast, daniel, martin.lau, viro,
brauner, jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton,
josef, gnoack, m
On Mon, Jun 02, 2025 at 11:59:17PM -0700, Song Liu wrote:
> This helper walks an input path to its parent. Logic are added to handle
> walking across mount tree.
>
> This will be used by landlock, and BPF LSM.
>
> Signed-off-by: Song Liu <song@kernel.org>
> ---
> fs/namei.c | 52 +++++++++++++++++++++++++++++++++++++++++++
> include/linux/namei.h | 2 ++
> 2 files changed, 54 insertions(+)
>
> diff --git a/fs/namei.c b/fs/namei.c
> index 4bb889fc980b..7d5bf2bb604f 100644
> --- a/fs/namei.c
> +++ b/fs/namei.c
> @@ -1424,6 +1424,58 @@ static bool choose_mountpoint(struct mount *m, const struct path *root,
> return found;
> }
>
> +/**
> + * path_walk_parent - Walk to the parent of path
> + * @path: input and output path.
> + * @root: root of the path walk, do not go beyond this root. If @root is
> + * zero'ed, walk all the way to real root.
> + *
> + * Given a path, find the parent path. Replace @path with the parent path.
> + * If we were already at the real root or a disconnected root, @path is
> + * not changed.
> + *
> + * The logic of path_walk_parent() is similar to follow_dotdot(), except
> + * that path_walk_parent() will continue walking for !path_connected case.
> + * This effectively means we are walking from disconnectedbind mount to the
> + * original mount point. If this behavior is not desired, the caller can
> + * add a check like:
> + *
> + * if (path_walk_parent(&path) && !path_connected(path.mnt, path.dentry)
> + * // continue walking
> + * else
> + * // stop walking
> + *
> + * Returns:
> + * true - if @path is updated to its parent.
> + * false - if @path is already the root (real root or @root).
> + */
> +bool path_walk_parent(struct path *path, const struct path *root)
> +{
> + struct dentry *parent;
> +
> + if (path_equal(path, root))
> + return false;
> +
> + if (unlikely(path->dentry == path->mnt->mnt_root)) {
> + struct path p;
> +
> + if (!choose_mountpoint(real_mount(path->mnt), root, &p))
> + return false;
> + path_put(path);
> + *path = p;
> + return true;
It should not return here but continue with the following checks until
the potential dget_parent() call.
I sent a test to check this issue:
https://lore.kernel.org/r/20250606110811.211297-1-mic@digikod.net
> + }
> +
> + if (unlikely(IS_ROOT(path->dentry)))
> + return false;
> +
> + parent = dget_parent(path->dentry);
> + dput(path->dentry);
> + path->dentry = parent;
> + return true;
> +}
> +EXPORT_SYMBOL_GPL(path_walk_parent);
> +
> /*
> * Perform an automount
> * - return -EISDIR to tell follow_managed() to stop and return the path we
> diff --git a/include/linux/namei.h b/include/linux/namei.h
> index 5d085428e471..cba5373ecf86 100644
> --- a/include/linux/namei.h
> +++ b/include/linux/namei.h
> @@ -85,6 +85,8 @@ extern int follow_down_one(struct path *);
> extern int follow_down(struct path *path, unsigned int flags);
> extern int follow_up(struct path *);
>
> +bool path_walk_parent(struct path *path, const struct path *root);
> +
> extern struct dentry *lock_rename(struct dentry *, struct dentry *);
> extern struct dentry *lock_rename_child(struct dentry *, struct dentry *);
> extern void unlock_rename(struct dentry *, struct dentry *);
> --
> 2.47.1
>
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 1/4] namei: Introduce new helper function path_walk_parent()
2025-06-03 6:59 ` [PATCH v2 bpf-next 1/4] namei: Introduce new helper function path_walk_parent() Song Liu
2025-06-06 11:10 ` Mickaël Salaün
@ 2025-06-06 14:40 ` Al Viro
2025-06-06 17:01 ` Song Liu
1 sibling, 1 reply; 23+ messages in thread
From: Al Viro @ 2025-06-06 14:40 UTC (permalink / raw)
To: Song Liu
Cc: bpf, linux-fsdevel, linux-kernel, linux-security-module,
kernel-team, andrii, eddyz87, ast, daniel, martin.lau, brauner,
jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton, josef,
mic, gnoack, m
On Mon, Jun 02, 2025 at 11:59:17PM -0700, Song Liu wrote:
> This helper walks an input path to its parent. Logic are added to handle
> walking across mount tree.
>
> This will be used by landlock, and BPF LSM.
Unless I'm misreading that, it does *NOT* walk to parent - it treats
step into mountpoint as a separate step. NAK in that form - it's
simply a wrong primitive.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 bpf-next 1/4] namei: Introduce new helper function path_walk_parent()
2025-06-06 14:40 ` Al Viro
@ 2025-06-06 17:01 ` Song Liu
0 siblings, 0 replies; 23+ messages in thread
From: Song Liu @ 2025-06-06 17:01 UTC (permalink / raw)
To: Al Viro
Cc: bpf, linux-fsdevel, linux-kernel, linux-security-module,
kernel-team, andrii, eddyz87, ast, daniel, martin.lau, brauner,
jack, kpsingh, mattbobrowski, amir73il, repnop, jlayton, josef,
mic, gnoack, m
On Fri, Jun 6, 2025 at 7:41 AM Al Viro <viro@zeniv.linux.org.uk> wrote:
>
> On Mon, Jun 02, 2025 at 11:59:17PM -0700, Song Liu wrote:
> > This helper walks an input path to its parent. Logic are added to handle
> > walking across mount tree.
> >
> > This will be used by landlock, and BPF LSM.
>
> Unless I'm misreading that, it does *NOT* walk to parent - it treats
> step into mountpoint as a separate step. NAK in that form - it's
> simply a wrong primitive.
I think this should be fixed by Mickaël's comment. I will send v3 with
it.
Thanks,
Song
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2025-06-06 17:01 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-03 6:59 [PATCH v2 bpf-next 0/4] bpf path iterator Song Liu
2025-06-03 6:59 ` [PATCH v2 bpf-next 1/4] namei: Introduce new helper function path_walk_parent() Song Liu
2025-06-06 11:10 ` Mickaël Salaün
2025-06-06 14:40 ` Al Viro
2025-06-06 17:01 ` Song Liu
2025-06-03 6:59 ` [PATCH v2 bpf-next 2/4] landlock: Use path_walk_parent() Song Liu
2025-06-03 13:46 ` Mickaël Salaün
2025-06-04 19:37 ` Song Liu
2025-06-05 16:47 ` Song Liu
2025-06-06 10:46 ` Mickaël Salaün
2025-06-03 6:59 ` [PATCH v2 bpf-next 3/4] bpf: Introduce path iterator Song Liu
2025-06-03 15:13 ` Alexei Starovoitov
2025-06-04 17:22 ` Christian Brauner
2025-06-03 18:40 ` Andrii Nakryiko
2025-06-03 20:49 ` Yonghong Song
2025-06-03 21:10 ` Song Liu
2025-06-03 21:09 ` Song Liu
2025-06-03 21:44 ` Andrii Nakryiko
2025-06-03 23:20 ` Song Liu
2025-06-04 20:37 ` Andrii Nakryiko
2025-06-05 19:27 ` Matt Bobrowski
2025-06-05 21:14 ` Song Liu
2025-06-03 6:59 ` [PATCH v2 bpf-next 4/4] selftests/bpf: Add tests for bpf " Song Liu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).