aboutsummaryrefslogtreecommitdiff
path: root/kernel/fork.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/fork.c')
-rw-r--r--kernel/fork.c125
1 files changed, 54 insertions, 71 deletions
diff --git a/kernel/fork.c b/kernel/fork.c
index eb198c8dfdc7..5efceb8d58a7 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -858,7 +858,8 @@ void __init fork_init(void)
#ifndef ARCH_MIN_TASKALIGN
#define ARCH_MIN_TASKALIGN 0
#endif
- int align = max_t(int, L1_CACHE_BYTES, ARCH_MIN_TASKALIGN);
+ int align = max3(L1_CACHE_BYTES, ARCH_MIN_TASKALIGN,
+ __alignof__(struct task_struct));
unsigned long useroffset, usersize;
/* create a slab on which task_structs can be allocated */
@@ -970,10 +971,10 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
dup_user_cpus_ptr(tsk, orig, node);
/*
- * One for the user space visible state that goes away when reaped.
- * One for the scheduler.
+ * Nobody can keep the task while copy_process() may still fail, which
+ * frees it no matter what.
*/
- refcount_set(&tsk->rcu_users, 2);
+ refcount_set(&tsk->rcu_users, 0);
/* One for the rcu users */
refcount_set(&tsk->usage, 1);
#ifdef CONFIG_BLK_DEV_IO_TRACE
@@ -1083,9 +1084,7 @@ static void mmap_init_lock(struct mm_struct *mm)
{
init_rwsem(&mm->mmap_lock);
mm_lock_seqcount_init(mm);
-#ifdef CONFIG_PER_VMA_LOCK
rcuwait_init(&mm->vma_writer_wait);
-#endif
}
static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
@@ -1534,12 +1533,19 @@ static struct mm_struct *dup_mm(struct task_struct *tsk,
if (!mm)
goto fail_nomem;
+ uprobe_start_dup_mmap();
+ if (mmap_write_lock_killable(oldmm)) {
+ free_mm(mm);
+ goto fail_uprobe;
+ }
+
memcpy(mm, oldmm, sizeof(*mm));
- if (!mm_init(mm, tsk))
- goto fail_nomem;
+ if (!mm_init(mm, tsk)) {
+ mmap_write_unlock(oldmm);
+ goto fail_uprobe;
+ }
- uprobe_start_dup_mmap();
err = dup_mmap(mm, oldmm);
if (err)
goto free_pt;
@@ -1558,9 +1564,10 @@ free_pt:
mm->binfmt = NULL;
mm_init_owner(mm, NULL);
mmput(mm);
- if (err)
- uprobe_end_dup_mmap();
-
+ if (!err)
+ return NULL;
+fail_uprobe:
+ uprobe_end_dup_mmap();
fail_nomem:
return NULL;
}
@@ -1619,7 +1626,13 @@ 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, bool umh)
+enum copy_fs_flags_t {
+ COPY_FS_UMH = (1 << 0),
+ COPY_FS_IDLE = (1 << 1),
+};
+
+static int copy_fs(u64 clone_flags, struct task_struct *tsk,
+ enum copy_fs_flags_t flags)
{
struct fs_struct *fs;
@@ -1629,19 +1642,20 @@ static int copy_fs(u64 clone_flags, struct task_struct *tsk, bool umh)
* filesystem state, or be started from a non-initial mount
* namespace.
*/
- if (umh) {
+ if (flags & COPY_FS_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 if (flags & COPY_FS_IDLE) {
+ fs = &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);
/* "users" and "in_exec" locked for check_unsafe_exec() */
if (fs->in_exec) {
@@ -1650,6 +1664,7 @@ static int copy_fs(u64 clone_flags, struct task_struct *tsk, bool umh)
}
fs->users++;
read_sequnlock_excl(&fs->seq);
+ tsk->real_fs = tsk->fs = fs;
return 0;
}
tsk->real_fs = tsk->fs = copy_fs_struct(fs);
@@ -1677,6 +1692,7 @@ static int copy_files(u64 clone_flags, struct task_struct *tsk,
if (clone_flags & CLONE_FILES) {
atomic_inc(&oldf->count);
+ tsk->files = oldf;
return 0;
}
@@ -2146,12 +2162,9 @@ __latent_entropy struct task_struct *copy_process(
if (args->kthread)
p->flags |= PF_KTHREAD;
if (args->user_worker) {
- /*
- * Mark us a user worker, and block any signal that isn't
- * fatal or STOP
- */
+ /* A user worker takes only the signals nobody can block. */
p->flags |= PF_USER_WORKER;
- siginitsetinv(&p->blocked, sigmask(SIGKILL)|sigmask(SIGSTOP));
+ siginitsetinv(&p->blocked, SIG_KERNEL_ONLY_MASK);
}
if (args->io_thread)
p->flags |= PF_IO_WORKER;
@@ -2200,6 +2213,8 @@ __latent_entropy struct task_struct *copy_process(
INIT_LIST_HEAD(&p->sibling);
rcu_copy_process(p);
p->vfork_done = NULL;
+ /* Set by copy_files(), exit_files() on the error path skips NULL. */
+ p->files = NULL;
spin_lock_init(&p->alloc_lock);
init_sigpending(&p->pending);
@@ -2299,9 +2314,11 @@ __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, args->umh);
+ retval = copy_fs(clone_flags, p,
+ (args->umh ? COPY_FS_UMH : 0) |
+ (args->idle ? COPY_FS_IDLE : 0));
if (retval)
- goto bad_fork_cleanup_files;
+ goto bad_fork_cleanup_semundo;
retval = copy_sighand(clone_flags, p);
if (retval)
goto bad_fork_cleanup_fs;
@@ -2501,6 +2518,12 @@ __latent_entropy struct task_struct *copy_process(
/* No more failure paths after this point. */
/*
+ * One for the user space visible state that goes away when reaped.
+ * One for the scheduler.
+ */
+ refcount_set(&p->rcu_users, 2);
+
+ /*
* Copy seccomp details explicitly here, in case they were changed
* before holding sighand lock.
*/
@@ -2615,8 +2638,6 @@ bad_fork_cleanup_sighand:
__cleanup_sighand(p->sighand);
bad_fork_cleanup_fs:
exit_fs(p); /* blocking */
-bad_fork_cleanup_files:
- exit_files(p); /* blocking */
bad_fork_cleanup_semundo:
exit_sem(p);
bad_fork_cleanup_security:
@@ -2627,6 +2648,8 @@ bad_fork_cleanup_perf:
perf_event_free_task(p);
bad_fork_sched_cancel_fork:
sched_cancel_fork(p);
+ /* ->release() of a file may need scx_fork_rwsem for write. */
+ exit_files(p); /* blocking */
bad_fork_cleanup_policy:
lockdep_free_task(p);
#ifdef CONFIG_NUMA
@@ -2671,7 +2694,7 @@ struct task_struct * __init fork_idle(int cpu)
{
struct task_struct *task;
struct kernel_clone_args args = {
- .flags = CLONE_VM,
+ .flags = CLONE_VM | CLONE_FS,
.fn = &idle_dummy,
.fn_arg = NULL,
.kthread = 1,
@@ -2705,6 +2728,10 @@ struct task_struct *create_io_thread(int (*fn)(void *), void *arg, int node)
.user_worker = 1,
};
+ /* A creator past its fatal signal or its coredump point gets no thread. */
+ if (current->flags & (PF_SIGNALED | PF_POSTCOREDUMP))
+ return ERR_PTR(-EINTR);
+
return copy_process(NULL, 0, node, &args);
}
@@ -3213,24 +3240,6 @@ static int unshare_fs(unsigned long unshare_flags, struct fs_struct **new_fsp)
}
/*
- * Unshare file descriptor table if it is being shared
- */
-static int unshare_fd(unsigned long unshare_flags, struct files_struct **new_fdp)
-{
- struct files_struct *fd = current->files;
-
- if ((unshare_flags & CLONE_FILES) &&
- (fd && atomic_read(&fd->count) > 1)) {
- fd = dup_fd(fd, NULL);
- if (IS_ERR(fd))
- return PTR_ERR(fd);
- *new_fdp = fd;
- }
-
- return 0;
-}
-
-/*
* unshare allows a process to 'unshare' part of the process
* context which was originally shared using clone. copy_*
* functions used by kernel_clone() cannot be used here directly
@@ -3328,10 +3337,8 @@ int ksys_unshare(unsigned long unshare_flags)
new_nsproxy = NULL;
}
- if (new_fd) {
- guard(task_lock)(current);
- swap(current->files, new_fd);
- }
+ if (new_fd)
+ switch_files_struct(current, no_free_ptr(new_fd));
if (new_cred) {
/* Install the new user namespace */
@@ -3365,30 +3372,6 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags)
return ksys_unshare(unshare_flags);
}
-/*
- * Helper to unshare the files of the current task.
- * We don't want to expose copy_files internals to
- * the exec layer of the kernel.
- */
-
-int unshare_files(void)
-{
- struct task_struct *task = current;
- struct files_struct *old, *copy = NULL;
- int error;
-
- error = unshare_fd(CLONE_FILES, &copy);
- if (error || !copy)
- return error;
-
- old = task->files;
- task_lock(task);
- task->files = copy;
- task_unlock(task);
- put_files_struct(old);
- return 0;
-}
-
static int sysctl_max_threads(const struct ctl_table *table, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{