From: Justin Suess <utilityemal77@gmail.com>
To: "Christian Brauner" <brauner@kernel.org>,
"Alexander Viro" <viro@zeniv.linux.org.uk>,
"Jan Kara" <jack@suse.cz>, NeilBrown <neil@brown.name>,
"Mickaël Salaün" <mic@digikod.net>,
"Alexei Starovoitov" <ast@kernel.org>,
"Daniel Borkmann" <daniel@iogearbox.net>,
"Andrii Nakryiko" <andrii@kernel.org>,
"Song Liu" <song@kernel.org>
Cc: linux-fsdevel@vger.kernel.org, bpf@vger.kernel.org,
linux-security-module@vger.kernel.org,
linux-kernel@vger.kernel.org, "Günther Noack" <gnoack@google.com>,
"Paul Moore" <paul@paul-moore.com>,
"James Morris" <jmorris@namei.org>,
"Serge E . Hallyn" <serge@hallyn.com>,
"Martin KaFai Lau" <martin.lau@linux.dev>,
"Eduard Zingerman" <eddyz87@gmail.com>,
"Yonghong Song" <yonghong.song@linux.dev>,
"John Fastabend" <john.fastabend@gmail.com>,
"Kumar Kartikeya Dwivedi" <memxor@gmail.com>,
"Jiri Olsa" <jolsa@kernel.org>,
"Jeff Layton" <jlayton@kernel.org>,
"Amir Goldstein" <amir73il@gmail.com>,
"Mateusz Guzik" <mjguzik@gmail.com>,
"Shuah Khan" <shuah@kernel.org>, "Tingmao Wang" <m@maowtm.org>,
jose.marchesi@oracle.com
Subject: Re: [PATCH bpf-next v1 06/11] selftests/bpf: exercise the path ancestor iterator
Date: Tue, 13 Oct 2026 15:50:30 -0400 [thread overview]
Message-ID: <as6GG4NMzLrnpKXK@zenbox> (raw)
In-Reply-To: <20261013164800.2906577-7-utilityemal77@gmail.com>
On Tue, Oct 13, 2026 at 12:47:55PM -0400, Justin Suess wrote:
> Walk the ancestry of a mkdir's parent directory from a sleepable LSM
> program and check that the iteration reaches the root: the temporary
> directory, its parent and / are visited, no position comes back
> flagged, and the flags read back after the loop show it ended at the
> root rather than on a failed allocation.
>
> Hold the first position across the rest of the walk and resolve its
> pathname only once the loop is done and the iterator destroyed: the
> acquired reference, not the walk's, is then all that keeps the
> position alive.
>
> Signed-off-by: Justin Suess <utilityemal77@gmail.com>
> ---
> .../selftests/bpf/prog_tests/path_ancestors.c | 57 ++++++++++++++
> .../selftests/bpf/progs/path_ancestors.c | 74 +++++++++++++++++++
> 2 files changed, 131 insertions(+)
> create mode 100644 tools/testing/selftests/bpf/prog_tests/path_ancestors.c
> create mode 100644 tools/testing/selftests/bpf/progs/path_ancestors.c
>
> diff --git a/tools/testing/selftests/bpf/prog_tests/path_ancestors.c b/tools/testing/selftests/bpf/prog_tests/path_ancestors.c
> new file mode 100644
> index 000000000000..d1bd26bce10b
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/path_ancestors.c
> @@ -0,0 +1,57 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2026 Justin Suess */
> +
> +#include <sys/stat.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <test_progs.h>
> +#include "path_ancestors.skel.h"
> +
> +void test_path_ancestors(void)
> +{
> + char base[] = "/tmp/path_ancestors_XXXXXX";
> + struct path_ancestors *skel = NULL;
> + char suba[280], subb[280];
> +
> + if (!ASSERT_OK_PTR(mkdtemp(base), "mkdtemp"))
> + return;
> + snprintf(suba, sizeof(suba), "%s/a", base);
> + snprintf(subb, sizeof(subb), "%s/a/b", base);
> + if (!ASSERT_OK(mkdir(suba, 0755), "mkdir_a"))
> + goto out_rm;
> +
> + skel = path_ancestors__open_and_load();
> + if (!ASSERT_OK_PTR(skel, "open_and_load"))
> + goto out_rm;
> + skel->bss->monitored_pid = getpid();
> + if (!ASSERT_OK(path_ancestors__attach(skel), "attach"))
> + goto out;
> +
> + /* mkdir b: the hook sees dir == suba, whose ancestry is walked. */
> + if (!ASSERT_OK(mkdir(subb, 0755), "mkdir_b"))
> + goto out;
> +
> + /* suba, base, /tmp, / at least. */
> + ASSERT_GE(skel->bss->ref_count, 4, "ref_count");
> + ASSERT_EQ(skel->bss->ref_flags, 0, "ref_flags");
> + /*
> + * The referenced loop ended at the root, not on an allocation
> + * failure.
> + */
> + ASSERT_EQ(skel->bss->end_flags, 0, "end_flags");
> +
> + /* The acquired second position, used after its step was taken. */
> + ASSERT_STREQ(skel->bss->second_path, base, "second_path");
> + ASSERT_EQ(skel->bss->second_len, strlen(base) + 1, "second_len");
> +
> + /* The first position, used only after the walk itself was gone. */
> + ASSERT_STREQ(skel->bss->first_path, suba, "first_path");
> + ASSERT_EQ(skel->bss->first_len, strlen(suba) + 1, "first_len");
> +
> +out:
> + path_ancestors__destroy(skel);
> +out_rm:
> + rmdir(subb);
> + rmdir(suba);
> + rmdir(base);
> +}
> diff --git a/tools/testing/selftests/bpf/progs/path_ancestors.c b/tools/testing/selftests/bpf/progs/path_ancestors.c
> new file mode 100644
> index 000000000000..6287f5099a18
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/path_ancestors.c
> @@ -0,0 +1,74 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2026 Justin Suess */
> +
> +#include "vmlinux.h"
> +#include <errno.h>
> +#include <bpf/bpf_helpers.h>
> +#include <bpf/bpf_tracing.h>
> +#include "bpf_kfuncs.h"
> +
> +char _license[] SEC("license") = "GPL";
> +
> +__u32 monitored_pid;
> +
> +int ref_count; /* positions seen by the pure referenced walk */
> +int ref_flags; /* pos flags seen by the referenced walk */
> +int end_flags; /* flags after a referenced loop ended: 0 unless NOMEM */
> +int second_len; /* d_path length of the walk's second position */
> +int first_len; /* d_path length of the first position, after the walk */
> +char second_path[256];
> +char first_path[256];
> +
> +static bool monitored(void)
> +{
> + return (bpf_get_current_pid_tgid() >> 32) == monitored_pid;
> +}
> +
> +SEC("lsm.s/path_mkdir")
> +int BPF_PROG(walk_modes, const struct path *dir, struct dentry *dentry,
> + umode_t mode)
> +{
> + struct bpf_iter_path_ancestors it;
> + struct path *pos, *first = NULL;
> +
> + /*
> + * Keep first's init opaque: gcc-bpf otherwise folds it into a
> + * preceding loop's exit value, which the verifier cannot track.
> + */
> + barrier_var(first);
> +
cc'ing Eduard Zingerman + Jose Marchesi
This barrier_var line was necessary for this selftest to pass
under GCC BPF.
Othwise it will be rejected by the verifier:
84: (85) call bpf_iter_path_ancestors_rcu_next#63514
85: R0=rcu_ptr_path(id=30) ... depth=9
86: (bf) r6 = r0 ; R6=rcu_ptr_path(id=30)
88: (55) if r0 != 0x0 goto pc-64
...
101: (85) call bpf_rcu_read_unlock#64876
^-- R6 downgraded rcu_ptr_ -> untrusted_ptr_
...
146: (15) if r6 == 0x0 goto pc+10 ; R6=untrusted_ptr_path(id=30)
147: (bf) r1 = r6
151: (85) call bpf_path_d_path#64649
R1 must be referenced or trusted
I think it's trying to optimize out the "struct path* first" and make it
use the r6 register. It tries reusing the R6 register between the two
loops, and doing so causes the program to fail to verify because it doesn't
understand that the path was reinitialized with r6=0 from the previous
exit.
So I had to add the memory barrier to force struct path to be
instantiated outside the register.
There's nothing wrong from a *correctness* perspective with the code
generated by GCC-BPF. But I'm not sure if this is a GCC-BPF bug or a
bug in the SCEV widening series (merge 15b578b1715d). It depends on
the contract BPF bytecode is supposed to meet.
But clang works without the barrier, it's only GCC-BPF that produces
this issue requiring the barrier.
I can give more details if needed.
Justin
> [...]
next prev parent reply other threads:[~2026-10-13 19:50 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-13 16:47 [PATCH bpf-next v1 00/11] fs: unified VFS ancestor walk for Landlock and BPF Justin Suess
2026-10-13 16:47 ` [PATCH bpf-next v1 01/11] namei: introduce __path_walk_parent() Justin Suess
2026-10-13 16:47 ` [PATCH bpf-next v1 02/11] namei: add vfs_walk_ancestors() Justin Suess
2026-10-13 16:47 ` [PATCH bpf-next v1 03/11] landlock: convert ancestor walk to vfs_walk_ancestors() Justin Suess
2026-10-13 16:47 ` [PATCH bpf-next v1 04/11] namei: expose the stepwise ancestor walk to fs/ Justin Suess
2026-10-13 16:47 ` [PATCH bpf-next v1 05/11] bpf: add a path ancestor iterator Justin Suess
2026-10-13 17:11 ` sashiko-bot
2026-10-13 18:00 ` Justin Suess
2026-10-13 16:47 ` [PATCH bpf-next v1 06/11] selftests/bpf: exercise the " Justin Suess
2026-10-13 19:50 ` Justin Suess [this message]
2026-10-13 16:47 ` [PATCH bpf-next v1 07/11] fs: add mnt_undo_legitimize() Justin Suess
2026-10-13 16:47 ` [PATCH bpf-next v1 08/11] namei: add an rcu-walk mode to the ancestor walk Justin Suess
2026-10-13 17:01 ` sashiko-bot
2026-10-13 17:56 ` Justin Suess
2026-10-13 16:47 ` [PATCH bpf-next v1 09/11] bpf: support "__uninit" iterator arguments in generic kfuncs Justin Suess
2026-10-13 16:47 ` [PATCH bpf-next v1 10/11] bpf: add a lockless path ancestor iterator Justin Suess
2026-10-13 16:48 ` [PATCH bpf-next v1 11/11] selftests/bpf: exercise the " Justin Suess
2026-10-13 17:50 ` bot+bpf-ci
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=as6GG4NMzLrnpKXK@zenbox \
--to=utilityemal77@gmail.com \
--cc=amir73il@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=brauner@kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=gnoack@google.com \
--cc=jack@suse.cz \
--cc=jlayton@kernel.org \
--cc=jmorris@namei.org \
--cc=john.fastabend@gmail.com \
--cc=jolsa@kernel.org \
--cc=jose.marchesi@oracle.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=m@maowtm.org \
--cc=martin.lau@linux.dev \
--cc=memxor@gmail.com \
--cc=mic@digikod.net \
--cc=mjguzik@gmail.com \
--cc=neil@brown.name \
--cc=paul@paul-moore.com \
--cc=serge@hallyn.com \
--cc=shuah@kernel.org \
--cc=song@kernel.org \
--cc=viro@zeniv.linux.org.uk \
--cc=yonghong.song@linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox