summaryrefslogtreecommitdiff
path: root/kernel/fork.c
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-07 12:04:10 +0100
committerMark Brown <broonie@kernel.org>2026-07-07 12:04:10 +0100
commitfacd9724cea049c963fd341f8725651173e9ce55 (patch)
tree4bdc03d61c014fcd24eac70c7e4a2cc3f2fcfb9b /kernel/fork.c
parentbb6184558fb5f850b2d9a31205bad5a6450229d1 (diff)
parentcf6f88615485a68df77092de1f90f88708a32fa6 (diff)
downloadlinux-next-facd9724cea049c963fd341f8725651173e9ce55.tar.gz
linux-next-facd9724cea049c963fd341f8725651173e9ce55.zip
Merge branch 'vfs.all' of https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
Diffstat (limited to 'kernel/fork.c')
-rw-r--r--kernel/fork.c53
1 files changed, 33 insertions, 20 deletions
diff --git a/kernel/fork.c b/kernel/fork.c
index f0e2e131a9a5..94e021eabf1d 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1618,9 +1618,27 @@ static int copy_exec_state(u64 clone_flags, struct task_struct *tsk)
return task_exec_state_copy(tsk);
}
-static int copy_fs(u64 clone_flags, struct task_struct *tsk)
+static int copy_fs(u64 clone_flags, struct task_struct *tsk, bool umh)
{
- struct fs_struct *fs = current->fs;
+ struct fs_struct *fs;
+
+ /*
+ * Usermodehelper may copy userspace_init_fs filesystem state but
+ * they don't get to create mount namespaces, share the
+ * filesystem state, or be started from a non-initial mount
+ * namespace.
+ */
+ if (umh) {
+ if (clone_flags & (CLONE_NEWNS | CLONE_FS))
+ return -EINVAL;
+ if (current->nsproxy->mnt_ns != &init_mnt_ns)
+ return -EINVAL;
+ fs = userspace_init_fs;
+ } else {
+ fs = current->fs;
+ VFS_WARN_ON_ONCE(current->fs != current->real_fs);
+ }
+
if (clone_flags & CLONE_FS) {
/* tsk->fs is already what we want */
read_seqlock_excl(&fs->seq);
@@ -1633,7 +1651,7 @@ static int copy_fs(u64 clone_flags, struct task_struct *tsk)
read_sequnlock_excl(&fs->seq);
return 0;
}
- tsk->fs = copy_fs_struct(fs);
+ tsk->real_fs = tsk->fs = copy_fs_struct(fs);
if (!tsk->fs)
return -ENOMEM;
return 0;
@@ -2277,7 +2295,7 @@ __latent_entropy struct task_struct *copy_process(
retval = copy_files(clone_flags, p, args->no_files);
if (retval)
goto bad_fork_cleanup_semundo;
- retval = copy_fs(clone_flags, p);
+ retval = copy_fs(clone_flags, p, args->umh);
if (retval)
goto bad_fork_cleanup_files;
retval = copy_sighand(clone_flags, p);
@@ -2819,6 +2837,7 @@ pid_t user_mode_thread(int (*fn)(void *), void *arg, unsigned long flags)
.exit_signal = (flags & CSIGNAL),
.fn = fn,
.fn_arg = arg,
+ .umh = 1,
};
return kernel_clone(&args);
@@ -3216,7 +3235,7 @@ static int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp
*/
int ksys_unshare(unsigned long unshare_flags)
{
- struct fs_struct *fs, *new_fs = NULL;
+ struct fs_struct *new_fs = NULL;
struct files_struct *new_fd = NULL;
struct cred *new_cred = NULL;
struct nsproxy *new_nsproxy = NULL;
@@ -3247,6 +3266,10 @@ int ksys_unshare(unsigned long unshare_flags)
if (unshare_flags & CLONE_NEWNS)
unshare_flags |= CLONE_FS;
+ /* No unsharing with overriden fs state */
+ VFS_WARN_ON_ONCE(unshare_flags & (CLONE_NEWNS | CLONE_FS) &&
+ current->fs != current->real_fs);
+
err = check_unshare_flags(unshare_flags);
if (err)
goto bad_unshare_out;
@@ -3294,23 +3317,13 @@ int ksys_unshare(unsigned long unshare_flags)
new_nsproxy = NULL;
}
- task_lock(current);
+ if (new_fs)
+ new_fs = switch_fs_struct(new_fs);
- if (new_fs) {
- fs = current->fs;
- read_seqlock_excl(&fs->seq);
- current->fs = new_fs;
- if (--fs->users)
- new_fs = NULL;
- else
- new_fs = fs;
- read_sequnlock_excl(&fs->seq);
- }
-
- if (new_fd)
+ if (new_fd) {
+ guard(task_lock)(current);
swap(current->files, new_fd);
-
- task_unlock(current);
+ }
if (new_cred) {
/* Install the new user namespace */