diff options
Diffstat (limited to 'kernel/fork.c')
| -rw-r--r-- | kernel/fork.c | 125 |
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, ©); - 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) { |
