nsresourced: refuse RENAME_WHITEOUT where mknod is refused

renameat2() with RENAME_WHITEOUT leaves a whiteout in place of the
source. That's a character device owned by the caller's fsuid and fsgid
which the file system creates from its ->rename() method, so it never
passes the path_mknod hook. Since Linux 5.8 this doesn't require any
privileges either.

So a managed user namespace that can rename an entry on a file system
that outlives it, e.g., in a world-writable directory the user prepared
from the outside, can leave a whiteout owned by its transient range
behind.

The only hook that sees this is path_rename, which gets the flags.
Refuse RENAME_WHITEOUT there whenever validate_mount() would refuse a
mknod in the source directory.

(cherry picked from commit 075c364273)
(cherry picked from commit b4f5697d22)
This commit is contained in:
Christian Brauner authored and Luca Boccassi committed 2026-11-05 14:48:10 +00:00
1 parent 134e402aa0
commit 432834f79e
1 file changed
+13
+13
View File
@@ -69,6 +69,9 @@ void* bpf_rdonly_cast(const void *, __u32) __ksym;
#define OVERLAYFS_SUPER_MAGIC 0x794c7630
#define CGROUP2_SUPER_MAGIC 0x63677270
/* From include/uapi/linux/fs.h, for the same reason. */
#define RENAME_WHITEOUT (1U << 2)
/* From include/uapi/asm-generic/fcntl.h. Only archs without BPF trampolines (no LSM programs) deviate. */
#define __O_TMPFILE (1U << 22) /* O_TMPFILE without O_DIRECTORY */
@@ -416,6 +419,16 @@ int BPF_PROG(userns_restrict_file_open, struct file *file, int ret) {
return validate_mount(file->f_path.mnt, ret);
}
/* RENAME_WHITEOUT puts a whiteout owned by the calling task in place of the source, past the mknod hook */
SEC("lsm/path_rename")
int BPF_PROG(userns_restrict_path_rename, const struct path *old_dir, struct dentry *old_dentry,
const struct path *new_dir, struct dentry *new_dentry, unsigned flags, int ret) {
if (!(flags & RENAME_WHITEOUT))
return ret;
return validate_mount(old_dir->mnt, ret);
}
/* overlayfs' struct ovl_fs and struct ovl_layer are private to the module and hence absent from vmlinux.h
* whenever overlayfs is built as a module. We declare the two fields we need as CO-RE "type flavors": libbpf
* strips the ___local suffix and relocates these against the kernel's real ovl_fs/ovl_layer. This both