diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-17 11:35:12 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-17 11:35:12 -0700 |
| commit | fff0150b0299f834fe2c335ce0eb5c68bb414cbd (patch) | |
| tree | 87b252e40d51b3d033926fee45cea2220fcb87f7 /init | |
| parent | de03b17ec046d81c4713bc54306579b9024fc513 (diff) | |
| parent | b4343aebd3a4dd255b15b2e5b1363d6399da302f (diff) | |
Merge tag 'vfs-7.3-rc1.kthread' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
Pull kthread vfs updates from Christian Brauner:
"This stops kernel threads from sharing filesystem state with
userspace. This work is about 3 cycles old and has been in -next
for about that time.
When the kernel boots init_task creates PID 1 and then kthreadd. From
that point every kthread and PID 1 share the same fs_struct. That is
why pivot_root() has to rewrite the fs_struct of all kthreads. The
rewriting exists so that kthreads can use init's filesystem state when
they want to. It also means userspace can move the ground out from
under the kernel.
PID 1 now gets a completely separate fs_struct. All kthreads are
anchored in a private SB_KERNMOUNT instance of nullfs that cannot be
mounted on and cannot be used to follow other mounts. Userspace init
can no longer affect kthread filesystem state and kthreads can no
longer affect userspace fs state without explicit opting in to that.
Path lookup from a kthread now fails by default. It makes it
deliberately hard to offload security sensitive operations into init's
filesystem state from a kthread.
Places that legitimately need to look something up there opt in
through the new scoped_with_init_fs() which temporarily overrides the
caller's fs_struct with init's. usermodehelpers remain the only kernel
tasks that genuinely share init's filesystem state, since they execute
random binaries in the root filesystem (excellent...).
The visible result is that /proc/2/root is a nullfs with an empty
mountinfo while /proc/1/root is the real root"
* tag 'vfs-7.3-rc1.kthread' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (26 commits)
initramfs_test: use test init/exit hooks to override init fs
fs: stop rewriting paths for PF_EXITING | PF_DUMPCORE
fs: stop rewriting kthread fs structs
fs: start all kthreads in nullfs
nullfs: make nullfs multi-instance
devtmpfs: create private mount namespace
fs: add umh argument to struct kernel_clone_args
fs: stop sharing fs_struct between init_task and pid 1
af_unix: use scoped_with_init_fs() for coredump socket lookup
initramfs: use scoped_with_init_fs() for rootfs unpacking
pnfs/blocklayout: use scoped_with_init_fs() for SCSI device lookup
ksmbd: use scoped_with_init_fs() for VFS path operations
ksmbd: use scoped_with_init_fs() for filesystem info path lookup
ksmbd: use scoped_with_init_fs() for share path resolution
fs: use scoped_with_init_fs() for kernel_read_file_from_path_initns()
coredump: use scoped_with_init_fs() for coredump path resolution
btrfs: use scoped_with_init_fs() for update_dev_time()
scsi: target: use scoped_with_init_fs() for APTPL metadata
scsi: target: use scoped_with_init_fs() for ALUA metadata
crypto: ccp: use scoped_with_init_fs() for SEV file access
...
Diffstat (limited to 'init')
| -rw-r--r-- | init/init_task.c | 1 | ||||
| -rw-r--r-- | init/initramfs.c | 14 | ||||
| -rw-r--r-- | init/initramfs_test.c | 19 | ||||
| -rw-r--r-- | init/main.c | 10 |
4 files changed, 37 insertions, 7 deletions
diff --git a/init/init_task.c b/init/init_task.c index b67ef6040a65..ba5c2523f7e0 100644 --- a/init/init_task.c +++ b/init/init_task.c @@ -162,6 +162,7 @@ struct task_struct init_task __aligned(L1_CACHE_BYTES) = { RCU_POINTER_INITIALIZER(cred, &init_cred), .comm = INIT_TASK_COMM, .thread = INIT_THREAD, + .real_fs = &init_fs, .fs = &init_fs, .files = &init_files, #ifdef CONFIG_IO_URING diff --git a/init/initramfs.c b/init/initramfs.c index 20a18fcda48e..4e27b97a8844 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -6,6 +6,7 @@ #include <linux/fcntl.h> #include <linux/file.h> #include <linux/fs.h> +#include <linux/fs_struct.h> #include <linux/hex.h> #include <linux/init.h> #include <linux/init_syscalls.h> @@ -716,7 +717,7 @@ static void __init populate_initrd_image(char *err) } #endif /* CONFIG_BLK_DEV_RAM */ -static void __init do_populate_rootfs(void *unused, async_cookie_t cookie) +static void __init unpack_initramfs(async_cookie_t cookie) { /* Load the built in initramfs */ char *err = unpack_to_rootfs(__initramfs_start, __initramfs_size); @@ -724,7 +725,7 @@ static void __init do_populate_rootfs(void *unused, async_cookie_t cookie) panic_show_mem("%s", err); /* Failed to decompress INTERNAL initramfs */ if (!initrd_start || IS_ENABLED(CONFIG_INITRAMFS_FORCE)) - goto done; + return; if (IS_ENABLED(CONFIG_BLK_DEV_RAM)) printk(KERN_INFO "Trying to unpack rootfs image as initramfs...\n"); @@ -739,9 +740,14 @@ static void __init do_populate_rootfs(void *unused, async_cookie_t cookie) printk(KERN_EMERG "Initramfs unpacking failed: %s\n", err); #endif } +} -done: - security_initramfs_populated(); +static void __init do_populate_rootfs(void *unused, async_cookie_t cookie) +{ + scoped_with_init_fs() { + unpack_initramfs(cookie); + security_initramfs_populated(); + } /* * If the initrd region is overlapped with crashkernel reserved region, diff --git a/init/initramfs_test.c b/init/initramfs_test.c index bc55306d226d..9cf316c13ffa 100644 --- a/init/initramfs_test.c +++ b/init/initramfs_test.c @@ -3,6 +3,7 @@ #include <linux/fcntl.h> #include <linux/file.h> #include <linux/fs.h> +#include <linux/fs_struct.h> #include <linux/init.h> #include <linux/init_syscalls.h> #include <linux/initrd.h> @@ -562,7 +563,7 @@ static struct kunit_case __refdata initramfs_test_cases[] = { {}, }; -static int __init initramfs_test_init(struct kunit_suite *suite) +static int __init initramfs_suite_init(struct kunit_suite *suite) { /* * unpack_to_rootfs() uses module-static state (victim, byte_count, @@ -574,9 +575,23 @@ static int __init initramfs_test_init(struct kunit_suite *suite) return 0; } +/* Tests run in a nullfs kthread; always use the init fs for path resolution. */ +static int __init initramfs_test_init(struct kunit *test) +{ + test->priv = __override_init_fs(); + return 0; +} + +static void __init initramfs_test_exit(struct kunit *test) +{ + __revert_init_fs(test->priv); +} + static struct kunit_suite __refdata initramfs_test_suite = { .name = "initramfs", - .suite_init = initramfs_test_init, + .suite_init = initramfs_suite_init, + .init = initramfs_test_init, + .exit = initramfs_test_exit, .test_cases = initramfs_test_cases, }; kunit_test_init_section_suites(&initramfs_test_suite); diff --git a/init/main.c b/init/main.c index e363232b428b..92d34e496a33 100644 --- a/init/main.c +++ b/init/main.c @@ -103,6 +103,7 @@ #include <linux/stackdepot.h> #include <linux/randomize_kstack.h> #include <linux/pidfs.h> +#include <linux/fs_struct.h> #include <linux/ptdump.h> #include <linux/time_namespace.h> #include <linux/unaligned.h> @@ -670,6 +671,11 @@ static __initdata DECLARE_COMPLETION(kthreadd_done); static noinline void __ref __noreturn rest_init(void) { + struct kernel_clone_args init_args = { + .flags = (CLONE_VM | CLONE_UNTRACED), + .fn = kernel_init, + .fn_arg = NULL, + }; struct task_struct *tsk; int pid; @@ -679,7 +685,7 @@ static noinline void __ref __noreturn rest_init(void) * the init task will end up wanting to create kthreads, which, if * we schedule it before we create kthreadd, will OOPS. */ - pid = user_mode_thread(kernel_init, NULL, CLONE_FS); + pid = kernel_clone(&init_args); /* * Pin init on the boot CPU. Task migration is not properly working * until sched_init_smp() has been run. It will set the allowed @@ -1540,6 +1546,8 @@ static int __ref kernel_init(void *unused) { int ret; + init_userspace_fs(); + /* * Wait until kthreadd is all set-up. */ |
