[PATCH] idmapped-mounts: probe O_TMPFILE support with the caller's mapped id

Zizhi Wo <[email protected]> Fri, 31 Jul 2026 17:02:53 +0800
Newsgroups org.kernel.vger.fstests
Message-ID <[email protected]>
From: Zizhi Wo <[email protected]>

Kernel commit 539dce114465 ("fs: refuse O_TMPFILE creation with an
unmapped fsuid or fsgid") made vfs_tmpfile() return -EOVERFLOW when the
caller's fsuid/fsgid has no mapping in the filesystem's user namespace
after applying the mount's idmapping. This is a no-op on non-idmapped
mounts and only takes effect on an idmapped mount that does not map the
caller.

The idmapped-mount testcases behind generic/696 and generic/697 build an
idmapping (e.g. "0 10000 10000") that covers host uid 10000-19999 but
not uid 0, then call openat_tmpfile_supported() from the parent process
that is still real root (fsuid 0). The probe now gets -EOVERFLOW, falls
into the log_errno() branch, and prints

  utils.c: 928: openat_tmpfile_supported - Value too large for defined \
data type - failure: create

to stderr. Both testcases expect "Silence is golden", so this spurious
output fails them, even though the kernel behavior is correct and the
testcases' own assertions all pass.

Move each openat_tmpfile_supported() call into the child, after
switch_ids()/switch_userns(), so it probes with the mapped identity the
tmpfile section uses. Silencing EOVERFLOW in openat_tmpfile_supported()
would make it indistinguishable whether the idmapping genuinely has no
mapping for any caller, or the probe just ran in the unmapped parent before
the switch.

Signed-off-by: Zizhi Wo <[email protected]>
---
 src/vfs/idmapped-mounts.c | 32 ++++++++++++++++++++------------
 1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/src/vfs/idmapped-mounts.c b/src/vfs/idmapped-mounts.c
index ed9992f9..bfbe9042 100644
--- a/src/vfs/idmapped-mounts.c
+++ b/src/vfs/idmapped-mounts.c
@@ -3838,8 +3838,6 @@ int tcore_setgid_create_idmapped(const struct vfstest_info *info)
 		goto out;
 	}
 
-	supported = openat_tmpfile_supported(open_tree_fd);
-
 	pid = fork();
 	if (pid < 0) {
 		log_stderr("failure: fork");
@@ -3849,6 +3847,8 @@ int tcore_setgid_create_idmapped(const struct vfstest_info *info)
 		if (!switch_ids(10000, 11000))
 			die("failure: switch fsids");
 
+		supported = openat_tmpfile_supported(open_tree_fd);
+
 		/* create regular file via open() */
 		file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
 		if (file1_fd < 0)
@@ -4014,8 +4014,6 @@ int tcore_setgid_create_idmapped_in_userns(const struct vfstest_info *info)
 		goto out;
 	}
 
-	supported = openat_tmpfile_supported(open_tree_fd);
-
 	pid = fork();
 	if (pid < 0) {
 		log_stderr("failure: fork");
@@ -4025,6 +4023,8 @@ int tcore_setgid_create_idmapped_in_userns(const struct vfstest_info *info)
 		if (!switch_userns(attr.userns_fd, 0, 0, false))
 			die("failure: switch_userns");
 
+		supported = openat_tmpfile_supported(open_tree_fd);
+
 		/* create regular file via open() */
 		file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
 		if (file1_fd < 0)
@@ -4133,6 +4133,8 @@ int tcore_setgid_create_idmapped_in_userns(const struct vfstest_info *info)
 		if (!switch_userns(attr.userns_fd, 0, 0, false))
 			die("failure: switch_userns");
 
+		supported = openat_tmpfile_supported(open_tree_fd);
+
 		if (!caps_down_fsetid())
 			die("failure: caps_down_fsetid");
 
@@ -4257,6 +4259,8 @@ int tcore_setgid_create_idmapped_in_userns(const struct vfstest_info *info)
 		if (!switch_userns(attr.userns_fd, 0, 1000, false))
 			die("failure: switch_userns");
 
+		supported = openat_tmpfile_supported(open_tree_fd);
+
 		if (!caps_down_fsetid())
 			die("failure: caps_down_fsetid");
 
@@ -7733,8 +7737,6 @@ static int setgid_create_umask_idmapped(const struct vfstest_info *info)
 		goto out;
 	}
 
-	supported = openat_tmpfile_supported(open_tree_fd);
-
 	pid = fork();
 	if (pid < 0) {
 		log_stderr("failure: fork");
@@ -7752,6 +7754,8 @@ static int setgid_create_umask_idmapped(const struct vfstest_info *info)
 		if (!switch_ids(10000, 11000))
 			die("failure: switch fsids");
 
+		supported = openat_tmpfile_supported(open_tree_fd);
+
 		/* create regular file via open() */
 		file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
 		if (file1_fd < 0)
@@ -7947,8 +7951,6 @@ static int setgid_create_umask_idmapped_in_userns(const struct vfstest_info *inf
 		goto out;
 	}
 
-	supported = openat_tmpfile_supported(open_tree_fd);
-
 	/*
 	 * Below we verify that setgid inheritance for a newly created file or
 	 * directory works correctly. As part of this we need to verify that
@@ -7984,6 +7986,8 @@ static int setgid_create_umask_idmapped_in_userns(const struct vfstest_info *inf
 		if (!switch_userns(attr.userns_fd, 0, 0, false))
 			die("failure: switch_userns");
 
+		supported = openat_tmpfile_supported(open_tree_fd);
+
 		if (!caps_down_fsetid())
 			die("failure: caps_down_fsetid");
 
@@ -8181,8 +8185,6 @@ static int setgid_create_acl_idmapped(const struct vfstest_info *info)
 		goto out;
 	}
 
-	supported = openat_tmpfile_supported(open_tree_fd);
-
 	pid = fork();
 	if (pid < 0) {
 		log_stderr("failure: fork");
@@ -8207,6 +8209,8 @@ static int setgid_create_acl_idmapped(const struct vfstest_info *info)
 		if (!switch_ids(10000, 11000))
 			die("failure: switch fsids");
 
+		supported = openat_tmpfile_supported(open_tree_fd);
+
 		/* create regular file via open() */
 		file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
 		if (file1_fd < 0)
@@ -8342,6 +8346,8 @@ static int setgid_create_acl_idmapped(const struct vfstest_info *info)
 		if (!switch_ids(10000, 11000))
 			die("failure: switch fsids");
 
+		supported = openat_tmpfile_supported(open_tree_fd);
+
 		/* create regular file via open() */
 		file1_fd = openat(open_tree_fd, FILE1, O_CREAT | O_EXCL | O_CLOEXEC, S_IXGRP | S_ISGID);
 		if (file1_fd < 0)
@@ -8536,8 +8542,6 @@ static int setgid_create_acl_idmapped_in_userns(const struct vfstest_info *info)
 		goto out;
 	}
 
-	supported = openat_tmpfile_supported(open_tree_fd);
-
 	/*
 	 * Below we verify that setgid inheritance for a newly created file or
 	 * directory works correctly. As part of this we need to verify that
@@ -8580,6 +8584,8 @@ static int setgid_create_acl_idmapped_in_userns(const struct vfstest_info *info)
 		if (!switch_userns(attr.userns_fd, 0, 0, false))
 			die("failure: switch_userns");
 
+		supported = openat_tmpfile_supported(open_tree_fd);
+
 		if (!caps_down_fsetid())
 			die("failure: caps_down_fsetid");
 
@@ -8723,6 +8729,8 @@ static int setgid_create_acl_idmapped_in_userns(const struct vfstest_info *info)
 		if (!switch_userns(attr.userns_fd, 0, 0, false))
 			die("failure: switch_userns");
 
+		supported = openat_tmpfile_supported(open_tree_fd);
+
 		if (!caps_down_fsetid())
 			die("failure: caps_down_fsetid");
 
-- 
2.52.0