mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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


  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®