From: Christian Brauner <brauner@kernel.org>
To: linux-fsdevel@vger.kernel.org
Cc: Alexander Viro <viro@zeniv.linux.org.uk>, Jan Kara <jack@suse.cz>,
linux-kernel@vger.kernel.org, Jeff Layton <jlayton@kernel.org>,
Jann Horn <jannh@google.com>, Neil Brown <neil@brown.name>,
Amir Goldstein <amir73il@gmail.com>,
"Christian Brauner (Amutable)" <brauner@kernel.org>
Subject: [PATCH 12/21] selftests/filesystems: check that an automount below an overlay layer is refused
Date: Fri, 02 Oct 2026 15:52:43 +0200 [thread overview]
Message-ID: <20261002-work-mount-fixes-4-v1-12-dd44b89d44ce@kernel.org> (raw)
In-Reply-To: <20261002-work-mount-fixes-4-v1-0-dd44b89d44ce@kernel.org>
Make sure that we don't automount on top of internal things.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
.../selftests/filesystems/overlayfs/.gitignore | 1 +
.../selftests/filesystems/overlayfs/Makefile | 1 +
.../filesystems/overlayfs/automount_in_layer.c | 174 +++++++++++++++++++++
3 files changed, 176 insertions(+)
diff --git a/tools/testing/selftests/filesystems/overlayfs/.gitignore b/tools/testing/selftests/filesystems/overlayfs/.gitignore
index 077f7a128168..b343cc430051 100644
--- a/tools/testing/selftests/filesystems/overlayfs/.gitignore
+++ b/tools/testing/selftests/filesystems/overlayfs/.gitignore
@@ -2,3 +2,4 @@
dev_in_maps
set_layers_via_fds
idmapped_mounts
+automount_in_layer
diff --git a/tools/testing/selftests/filesystems/overlayfs/Makefile b/tools/testing/selftests/filesystems/overlayfs/Makefile
index b3185f684add..382a59bda5e7 100644
--- a/tools/testing/selftests/filesystems/overlayfs/Makefile
+++ b/tools/testing/selftests/filesystems/overlayfs/Makefile
@@ -9,6 +9,7 @@ LOCAL_HDRS += ../wrappers.h log.h
TEST_GEN_PROGS := dev_in_maps
TEST_GEN_PROGS += set_layers_via_fds
TEST_GEN_PROGS += idmapped_mounts
+TEST_GEN_PROGS += automount_in_layer
include ../../lib.mk
diff --git a/tools/testing/selftests/filesystems/overlayfs/automount_in_layer.c b/tools/testing/selftests/filesystems/overlayfs/automount_in_layer.c
new file mode 100644
index 000000000000..0476c4582bdf
--- /dev/null
+++ b/tools/testing/selftests/filesystems/overlayfs/automount_in_layer.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * A layer of an overlay is a private clone of the mount it was given and
+ * belongs to no mount namespace. fanotify hands out descriptors on it. An
+ * automount triggered through one has no namespace to go into: the open has
+ * to fail, not oops with namespace_sem held.
+ */
+#define _GNU_SOURCE
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/magic.h>
+#include <sys/fanotify.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+
+#include "../../kselftest_harness.h"
+
+#define DIR_LEN 64
+#define PATH_LEN 192
+
+static bool have_fs(const char *name)
+{
+ char line[128];
+ bool found = false;
+ FILE *f;
+
+ f = fopen("/proc/filesystems", "re");
+ if (!f)
+ return false;
+ while (fgets(line, sizeof(line), f)) {
+ char *nl = strchr(line, '\n');
+ char *tab = strchr(line, '\t');
+
+ if (nl)
+ *nl = 0;
+ if (tab && !strcmp(tab + 1, name))
+ found = true;
+ }
+ fclose(f);
+ return found;
+}
+
+static int mnt_id_of(int fd)
+{
+ char path[64], buf[4096], *p;
+ ssize_t n;
+ int info;
+
+ snprintf(path, sizeof(path), "/proc/self/fdinfo/%d", fd);
+ info = open(path, O_RDONLY | O_CLOEXEC);
+ if (info < 0)
+ return -1;
+ n = read(info, buf, sizeof(buf) - 1);
+ close(info);
+ if (n <= 0)
+ return -1;
+ buf[n] = 0;
+ p = strstr(buf, "mnt_id:");
+ return p ? atoi(p + strlen("mnt_id:")) : -1;
+}
+
+static bool on_debugfs(int fd)
+{
+ struct statfs sf;
+
+ return !fstatfs(fd, &sf) && sf.f_type == DEBUGFS_MAGIC;
+}
+
+FIXTURE(layer) {
+ char base[DIR_LEN];
+ int fan;
+ int evfd; /* on the layer clone of the lower debugfs */
+};
+
+FIXTURE_SETUP(layer)
+{
+ char lower[PATH_LEN], other[PATH_LEN], ovl[PATH_LEN], opts[2 * PATH_LEN + 16];
+ struct fanotify_event_metadata *ev;
+ struct pollfd pfd;
+ char buf[4096];
+ int lfd, lower_id;
+ ssize_t n;
+ DIR *d;
+
+ self->fan = -1;
+ self->evfd = -1;
+ if (geteuid())
+ SKIP(return, "test requires root");
+ if (!have_fs("debugfs") || !have_fs("tracefs"))
+ SKIP(return, "test requires debugfs with the tracefs automount");
+ if (!have_fs("overlay"))
+ SKIP(return, "test requires overlayfs");
+
+ snprintf(self->base, sizeof(self->base), "/tmp/layer.XXXXXX");
+ ASSERT_NE(mkdtemp(self->base), NULL);
+ ASSERT_EQ(unshare(CLONE_NEWNS), 0);
+ ASSERT_EQ(mount("", "/", NULL, MS_REC | MS_PRIVATE, NULL), 0);
+ ASSERT_EQ(mount("tmpfs", self->base, "tmpfs", 0, "mode=0755"), 0);
+ snprintf(lower, sizeof(lower), "%s/lower", self->base);
+ snprintf(other, sizeof(other), "%s/other", self->base);
+ snprintf(ovl, sizeof(ovl), "%s/ovl", self->base);
+ ASSERT_EQ(mkdir(lower, 0755), 0);
+ ASSERT_EQ(mkdir(other, 0755), 0);
+ ASSERT_EQ(mkdir(ovl, 0755), 0);
+ ASSERT_EQ(mount("debugfs", lower, "debugfs", 0, NULL), 0);
+ snprintf(opts, sizeof(opts), "lowerdir=%s:%s", lower, other);
+ ASSERT_EQ(mount("overlay", ovl, "overlay", MS_RDONLY, opts), 0);
+
+ self->fan = fanotify_init(FAN_CLASS_NOTIF | FAN_NONBLOCK | FAN_CLOEXEC,
+ O_RDONLY | O_CLOEXEC);
+ ASSERT_GE(self->fan, 0);
+ ASSERT_EQ(fanotify_mark(self->fan, FAN_MARK_ADD | FAN_MARK_FILESYSTEM,
+ FAN_OPEN | FAN_ONDIR, AT_FDCWD, lower), 0);
+ lfd = open(lower, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ ASSERT_GE(lfd, 0);
+ lower_id = mnt_id_of(lfd);
+ close(lfd);
+
+ /* the overlay opens its lower directory through the layer clone */
+ d = opendir(ovl);
+ ASSERT_NE(d, NULL);
+ closedir(d);
+ pfd.fd = self->fan;
+ pfd.events = POLLIN;
+ ASSERT_EQ(poll(&pfd, 1, 5000), 1);
+ n = read(self->fan, buf, sizeof(buf));
+ ASSERT_GT(n, 0);
+ for (ev = (void *)buf; FAN_EVENT_OK(ev, n); ev = FAN_EVENT_NEXT(ev, n)) {
+ if (ev->fd < 0)
+ continue;
+ if (self->evfd < 0 && on_debugfs(ev->fd) &&
+ mnt_id_of(ev->fd) != lower_id)
+ self->evfd = ev->fd;
+ else
+ close(ev->fd);
+ }
+ ASSERT_GE(self->evfd, 0)
+ TH_LOG("no event on the layer clone");
+}
+
+FIXTURE_TEARDOWN(layer)
+{
+ if (self->evfd >= 0)
+ close(self->evfd);
+ if (self->fan >= 0)
+ close(self->fan);
+ umount2(self->base, MNT_DETACH);
+ rmdir(self->base);
+}
+
+TEST_F(layer, automount_below_the_clone_is_refused)
+{
+ struct stat st;
+ int fd;
+
+ /* the automount point is there */
+ ASSERT_EQ(fstatat(self->evfd, "tracing", &st, AT_NO_AUTOMOUNT), 0);
+ /* the clone is in no namespace, so the automount has nowhere to go */
+ fd = openat(self->evfd, "tracing", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ EXPECT_LT(fd, 0);
+ EXPECT_EQ(errno, EINVAL);
+ if (fd >= 0)
+ close(fd);
+}
+
+TEST_HARNESS_MAIN
--
2.53.0
next prev parent reply other threads:[~2026-10-02 13:54 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-02 13:52 [PATCH 00/21] mount: more bugfixes, trapped in the Black Lodge edition Christian Brauner
2026-10-02 13:52 ` [PATCH 01/21] namespace: unhash a dentry before detaching the mounts on it Christian Brauner
2026-10-02 13:52 ` [PATCH 02/21] namei: don't reveal overmounted entries in refwalk Christian Brauner
2026-10-02 13:52 ` [PATCH 03/21] fcntl: refuse F_SET_RW_HINT on an immutable inode Christian Brauner
2026-10-02 13:52 ` [PATCH 04/21] selftests/filesystems: check that an immutable inode takes no write hint Christian Brauner
2026-10-02 13:52 ` [PATCH 05/21] namespace: refuse an automount below a mount that is in no namespace Christian Brauner
2026-10-02 13:52 ` [PATCH 06/21] namespace: handle mount locking for automounts correctly Christian Brauner
2026-10-02 13:52 ` [PATCH 07/21] nullfs: don't update the access time Christian Brauner
2026-10-02 13:52 ` [PATCH 08/21] namespace: never expire a locked mount Christian Brauner
2026-10-02 13:52 ` [PATCH 09/21] namespace: keep the lock on a mount that a propagated copy is moved beneath Christian Brauner
2026-10-02 13:52 ` [PATCH 10/21] selftests/filesystems: check that a lock lands on the right mount and stays Christian Brauner
2026-10-02 13:52 ` [PATCH 11/21] selftests/filesystems: check the atime of the empty mount namespace root Christian Brauner
2026-10-02 13:52 ` Christian Brauner [this message]
2026-10-02 13:52 ` [PATCH 13/21] fhandle: decide the subtree check under mount_lock Christian Brauner
2026-10-02 13:52 ` [PATCH 14/21] namespace: keep the private nullfs instance in knullfs Christian Brauner
2026-10-02 13:52 ` [PATCH 15/21] namespace: nothing is mounted on or written through knullfs Christian Brauner
2026-10-02 13:52 ` [PATCH 16/21] fsnotify: let a filesystem refuse marks on its objects Christian Brauner
2026-10-02 14:26 ` Amir Goldstein
2026-10-02 13:52 ` [PATCH 17/21] nullfs: refuse file locks Christian Brauner
2026-10-02 13:52 ` [PATCH 18/21] nullfs: refuse leases and delegations Christian Brauner
2026-10-03 8:20 ` Jeff Layton
2026-10-02 13:52 ` [PATCH 19/21] readdir: take no inode lock on an immutable directory Christian Brauner
2026-10-02 13:52 ` [PATCH 20/21] selftests/filesystems: add a helper that holds a readdir in a page fault Christian Brauner
2026-10-02 13:52 ` [PATCH 21/21] selftests/filesystems: check that reading the root of an empty mount namespace stalls nobody Christian Brauner
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=20261002-work-mount-fixes-4-v1-12-dd44b89d44ce@kernel.org \
--to=brauner@kernel.org \
--cc=amir73il@gmail.com \
--cc=jack@suse.cz \
--cc=jannh@google.com \
--cc=jlayton@kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=neil@brown.name \
--cc=viro@zeniv.linux.org.uk \
/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
all inboxes | Powered by JetHome®