]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - kernel/fork.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
[karo-tx-linux.git] / kernel / fork.c
index 0d93b4d0617b006007e782d6dfdc6afc05c65d20..2845623fb58264eec28a8b99b48d4511856e718d 100644 (file)
@@ -454,8 +454,9 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
                tmp->vm_mm = mm;
                if (anon_vma_fork(tmp, mpnt))
                        goto fail_nomem_anon_vma_fork;
-               tmp->vm_flags &= ~VM_LOCKED;
+               tmp->vm_flags &= ~(VM_LOCKED|VM_UFFD_MISSING|VM_UFFD_WP);
                tmp->vm_next = tmp->vm_prev = NULL;
+               tmp->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
                file = tmp->vm_file;
                if (file) {
                        struct inode *inode = file_inode(file);
@@ -1148,6 +1149,10 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
        tty_audit_fork(sig);
        sched_autogroup_fork(sig);
 
+#ifdef CONFIG_CGROUPS
+       init_rwsem(&sig->group_rwsem);
+#endif
+
        sig->oom_score_adj = current->signal->oom_score_adj;
        sig->oom_score_adj_min = current->signal->oom_score_adj_min;
 
@@ -1246,6 +1251,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 {
        int retval;
        struct task_struct *p;
+       void *cgrp_ss_priv[CGROUP_CANFORK_COUNT] = {};
 
        if ((clone_flags & (CLONE_NEWNS|CLONE_FS)) == (CLONE_NEWNS|CLONE_FS))
                return ERR_PTR(-EINVAL);
@@ -1280,10 +1286,9 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 
        /*
         * If the new process will be in a different pid or user namespace
-        * do not allow it to share a thread group or signal handlers or
-        * parent with the forking task.
+        * do not allow it to share a thread group with the forking task.
         */
-       if (clone_flags & CLONE_SIGHAND) {
+       if (clone_flags & CLONE_THREAD) {
                if ((clone_flags & (CLONE_NEWUSER | CLONE_NEWPID)) ||
                    (task_active_pid_ns(current) !=
                                current->nsproxy->pid_ns_for_children))
@@ -1518,6 +1523,16 @@ static struct task_struct *copy_process(unsigned long clone_flags,
        INIT_LIST_HEAD(&p->thread_group);
        p->task_works = NULL;
 
+       /*
+        * Ensure that the cgroup subsystem policies allow the new process to be
+        * forked. It should be noted the the new process's css_set can be changed
+        * between here and cgroup_post_fork() if an organisation operation is in
+        * progress.
+        */
+       retval = cgroup_can_fork(p, cgrp_ss_priv);
+       if (retval)
+               goto bad_fork_free_pid;
+
        /*
         * Make it visible to the rest of the system, but dont wake it up yet.
         * Need tasklist lock for parent etc handling!
@@ -1554,7 +1569,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
                spin_unlock(&current->sighand->siglock);
                write_unlock_irq(&tasklist_lock);
                retval = -ERESTARTNOINTR;
-               goto bad_fork_free_pid;
+               goto bad_fork_cancel_cgroup;
        }
 
        if (likely(p->pid)) {
@@ -1596,7 +1611,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
        write_unlock_irq(&tasklist_lock);
 
        proc_fork_connector(p);
-       cgroup_post_fork(p);
+       cgroup_post_fork(p, cgrp_ss_priv);
        if (clone_flags & CLONE_THREAD)
                threadgroup_change_end(current);
        perf_event_fork(p);
@@ -1606,6 +1621,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
 
        return p;
 
+bad_fork_cancel_cgroup:
+       cgroup_cancel_fork(p, cgrp_ss_priv);
 bad_fork_free_pid:
        if (pid != &init_struct_pid)
                free_pid(pid);
@@ -1872,13 +1889,21 @@ static int check_unshare_flags(unsigned long unshare_flags)
                                CLONE_NEWUSER|CLONE_NEWPID))
                return -EINVAL;
        /*
-        * Not implemented, but pretend it works if there is nothing to
-        * unshare. Note that unsharing CLONE_THREAD or CLONE_SIGHAND
-        * needs to unshare vm.
+        * Not implemented, but pretend it works if there is nothing
+        * to unshare.  Note that unsharing the address space or the
+        * signal handlers also need to unshare the signal queues (aka
+        * CLONE_THREAD).
         */
        if (unshare_flags & (CLONE_THREAD | CLONE_SIGHAND | CLONE_VM)) {
-               /* FIXME: get_task_mm() increments ->mm_users */
-               if (atomic_read(&current->mm->mm_users) > 1)
+               if (!thread_group_empty(current))
+                       return -EINVAL;
+       }
+       if (unshare_flags & (CLONE_SIGHAND | CLONE_VM)) {
+               if (atomic_read(&current->sighand->count) > 1)
+                       return -EINVAL;
+       }
+       if (unshare_flags & CLONE_VM) {
+               if (!current_is_single_threaded())
                        return -EINVAL;
        }
 
@@ -1942,20 +1967,21 @@ SYSCALL_DEFINE1(unshare, unsigned long, unshare_flags)
        int err;
 
        /*
-        * If unsharing a user namespace must also unshare the thread.
+        * If unsharing a user namespace must also unshare the thread group
+        * and unshare the filesystem root and working directories.
         */
        if (unshare_flags & CLONE_NEWUSER)
                unshare_flags |= CLONE_THREAD | CLONE_FS;
-       /*
-        * If unsharing a thread from a thread group, must also unshare vm.
-        */
-       if (unshare_flags & CLONE_THREAD)
-               unshare_flags |= CLONE_VM;
        /*
         * If unsharing vm, must also unshare signal handlers.
         */
        if (unshare_flags & CLONE_VM)
                unshare_flags |= CLONE_SIGHAND;
+       /*
+        * If unsharing a signal handlers, must also unshare the signal queues.
+        */
+       if (unshare_flags & CLONE_SIGHAND)
+               unshare_flags |= CLONE_THREAD;
        /*
         * If unsharing namespace, must also unshare filesystem information.
         */