]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - kernel/fork.c
[PATCH] unshare: Use rcu_assign_pointer when setting sighand
[linux-2.6-omap-h63xx.git] / kernel / fork.c
index 598e5c27242c16c0a780c2783d9d27c592bf2090..46060cb24af0e06a7c21fdb67978f33644abbc3d 100644 (file)
@@ -108,8 +108,10 @@ void free_task(struct task_struct *tsk)
 }
 EXPORT_SYMBOL(free_task);
 
-void __put_task_struct(struct task_struct *tsk)
+void __put_task_struct_cb(struct rcu_head *rhp)
 {
+       struct task_struct *tsk = container_of(rhp, struct task_struct, rcu);
+
        WARN_ON(!(tsk->exit_state & (EXIT_DEAD | EXIT_ZOMBIE)));
        WARN_ON(atomic_read(&tsk->usage));
        WARN_ON(tsk == current);
@@ -446,6 +448,55 @@ void mm_release(struct task_struct *tsk, struct mm_struct *mm)
        }
 }
 
+/*
+ * Allocate a new mm structure and copy contents from the
+ * mm structure of the passed in task structure.
+ */
+static struct mm_struct *dup_mm(struct task_struct *tsk)
+{
+       struct mm_struct *mm, *oldmm = current->mm;
+       int err;
+
+       if (!oldmm)
+               return NULL;
+
+       mm = allocate_mm();
+       if (!mm)
+               goto fail_nomem;
+
+       memcpy(mm, oldmm, sizeof(*mm));
+
+       if (!mm_init(mm))
+               goto fail_nomem;
+
+       if (init_new_context(tsk, mm))
+               goto fail_nocontext;
+
+       err = dup_mmap(mm, oldmm);
+       if (err)
+               goto free_pt;
+
+       mm->hiwater_rss = get_mm_rss(mm);
+       mm->hiwater_vm = mm->total_vm;
+
+       return mm;
+
+free_pt:
+       mmput(mm);
+
+fail_nomem:
+       return NULL;
+
+fail_nocontext:
+       /*
+        * If init_new_context() failed, we cannot use mmput() to free the mm
+        * because it calls destroy_context()
+        */
+       mm_free_pgd(mm);
+       free_mm(mm);
+       return NULL;
+}
+
 static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
 {
        struct mm_struct * mm, *oldmm;
@@ -473,43 +524,17 @@ static int copy_mm(unsigned long clone_flags, struct task_struct * tsk)
        }
 
        retval = -ENOMEM;
-       mm = allocate_mm();
+       mm = dup_mm(tsk);
        if (!mm)
                goto fail_nomem;
 
-       /* Copy the current MM stuff.. */
-       memcpy(mm, oldmm, sizeof(*mm));
-       if (!mm_init(mm))
-               goto fail_nomem;
-
-       if (init_new_context(tsk,mm))
-               goto fail_nocontext;
-
-       retval = dup_mmap(mm, oldmm);
-       if (retval)
-               goto free_pt;
-
-       mm->hiwater_rss = get_mm_rss(mm);
-       mm->hiwater_vm = mm->total_vm;
-
 good_mm:
        tsk->mm = mm;
        tsk->active_mm = mm;
        return 0;
 
-free_pt:
-       mmput(mm);
 fail_nomem:
        return retval;
-
-fail_nocontext:
-       /*
-        * If init_new_context() failed, we cannot use mmput() to free the mm
-        * because it calls destroy_context()
-        */
-       mm_free_pgd(mm);
-       free_mm(mm);
-       return retval;
 }
 
 static inline struct fs_struct *__copy_fs_struct(struct fs_struct *old)
@@ -597,32 +622,17 @@ out:
        return newf;
 }
 
-static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
+/*
+ * Allocate a new files structure and copy contents from the
+ * passed in files structure.
+ */
+static struct files_struct *dup_fd(struct files_struct *oldf, int *errorp)
 {
-       struct files_struct *oldf, *newf;
+       struct files_struct *newf;
        struct file **old_fds, **new_fds;
-       int open_files, size, i, error = 0, expand;
+       int open_files, size, i, expand;
        struct fdtable *old_fdt, *new_fdt;
 
-       /*
-        * A background process may not have any files ...
-        */
-       oldf = current->files;
-       if (!oldf)
-               goto out;
-
-       if (clone_flags & CLONE_FILES) {
-               atomic_inc(&oldf->count);
-               goto out;
-       }
-
-       /*
-        * Note: we may be using current for both targets (See exec.c)
-        * This works because we cache current->files (old) as oldf. Don't
-        * break this.
-        */
-       tsk->files = NULL;
-       error = -ENOMEM;
        newf = alloc_files();
        if (!newf)
                goto out;
@@ -651,9 +661,9 @@ static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
        if (expand) {
                spin_unlock(&oldf->file_lock);
                spin_lock(&newf->file_lock);
-               error = expand_files(newf, open_files-1);
+               *errorp = expand_files(newf, open_files-1);
                spin_unlock(&newf->file_lock);
-               if (error < 0)
+               if (*errorp < 0)
                        goto out_release;
                new_fdt = files_fdtable(newf);
                /*
@@ -702,10 +712,8 @@ static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
                memset(&new_fdt->close_on_exec->fds_bits[start], 0, left);
        }
 
-       tsk->files = newf;
-       error = 0;
 out:
-       return error;
+       return newf;
 
 out_release:
        free_fdset (new_fdt->close_on_exec, new_fdt->max_fdset);
@@ -715,6 +723,40 @@ out_release:
        goto out;
 }
 
+static int copy_files(unsigned long clone_flags, struct task_struct * tsk)
+{
+       struct files_struct *oldf, *newf;
+       int error = 0;
+
+       /*
+        * A background process may not have any files ...
+        */
+       oldf = current->files;
+       if (!oldf)
+               goto out;
+
+       if (clone_flags & CLONE_FILES) {
+               atomic_inc(&oldf->count);
+               goto out;
+       }
+
+       /*
+        * Note: we may be using current for both targets (See exec.c)
+        * This works because we cache current->files (old) as oldf. Don't
+        * break this.
+        */
+       tsk->files = NULL;
+       error = -ENOMEM;
+       newf = dup_fd(oldf, &error);
+       if (!newf)
+               goto out;
+
+       tsk->files = newf;
+       error = 0;
+out:
+       return error;
+}
+
 /*
  *     Helper to unshare the files of the current task.
  *     We don't want to expose copy_files internals to
@@ -1019,6 +1061,12 @@ static task_t *copy_process(unsigned long clone_flags,
         */
        p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr: NULL;
 
+       /*
+        * sigaltstack should be cleared when sharing the same VM
+        */
+       if ((clone_flags & (CLONE_VM|CLONE_VFORK)) == CLONE_VM)
+               p->sas_ss_sp = p->sas_ss_size = 0;
+
        /*
         * Syscall tracing should be turned off in the child regardless
         * of CLONE_PTRACE.
@@ -1083,8 +1131,8 @@ static task_t *copy_process(unsigned long clone_flags,
                p->real_parent = current;
        p->parent = p->real_parent;
 
+       spin_lock(&current->sighand->siglock);
        if (clone_flags & CLONE_THREAD) {
-               spin_lock(&current->sighand->siglock);
                /*
                 * Important: if an exit-all has been started then
                 * do not create this new thread - the whole thread
@@ -1122,8 +1170,6 @@ static task_t *copy_process(unsigned long clone_flags,
                         */
                        p->it_prof_expires = jiffies_to_cputime(1);
                }
-
-               spin_unlock(&current->sighand->siglock);
        }
 
        /*
@@ -1135,8 +1181,6 @@ static task_t *copy_process(unsigned long clone_flags,
        if (unlikely(p->ptrace & PT_PTRACED))
                __ptrace_link(p, current->parent);
 
-       attach_pid(p, PIDTYPE_PID, p->pid);
-       attach_pid(p, PIDTYPE_TGID, p->tgid);
        if (thread_group_leader(p)) {
                p->signal->tty = current->signal->tty;
                p->signal->pgrp = process_group(current);
@@ -1146,9 +1190,12 @@ static task_t *copy_process(unsigned long clone_flags,
                if (p->pid)
                        __get_cpu_var(process_counts)++;
        }
+       attach_pid(p, PIDTYPE_TGID, p->tgid);
+       attach_pid(p, PIDTYPE_PID, p->pid);
 
        nr_threads++;
        total_forks++;
+       spin_unlock(&current->sighand->siglock);
        write_unlock_irq(&tasklist_lock);
        proc_fork_connector(p);
        return p;
@@ -1388,16 +1435,21 @@ static int unshare_fs(unsigned long unshare_flags, struct fs_struct **new_fsp)
 }
 
 /*
- * Unsharing of namespace for tasks created without CLONE_NEWNS is not
- * supported yet
+ * Unshare the namespace structure if it is being shared
  */
-static int unshare_namespace(unsigned long unshare_flags, struct namespace **new_nsp)
+static int unshare_namespace(unsigned long unshare_flags, struct namespace **new_nsp, struct fs_struct *new_fs)
 {
        struct namespace *ns = current->namespace;
 
        if ((unshare_flags & CLONE_NEWNS) &&
-           (ns && atomic_read(&ns->count) > 1))
-               return -EINVAL;
+           (ns && atomic_read(&ns->count) > 1)) {
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+
+               *new_nsp = dup_namespace(current, new_fs ? new_fs : current->fs);
+               if (!*new_nsp)
+                       return -ENOMEM;
+       }
 
        return 0;
 }
@@ -1418,30 +1470,36 @@ static int unshare_sighand(unsigned long unshare_flags, struct sighand_struct **
 }
 
 /*
- * Unsharing of vm for tasks created with CLONE_VM is not supported yet
+ * Unshare vm if it is being shared
  */
 static int unshare_vm(unsigned long unshare_flags, struct mm_struct **new_mmp)
 {
        struct mm_struct *mm = current->mm;
 
        if ((unshare_flags & CLONE_VM) &&
-           (mm && atomic_read(&mm->mm_users) > 1))
-               return -EINVAL;
+           (mm && atomic_read(&mm->mm_users) > 1)) {
+               *new_mmp = dup_mm(current);
+               if (!*new_mmp)
+                       return -ENOMEM;
+       }
 
        return 0;
-
 }
 
 /*
- * Unsharing of files for tasks created with CLONE_FILES is not supported yet
+ * 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;
+       int error = 0;
 
        if ((unshare_flags & CLONE_FILES) &&
-           (fd && atomic_read(&fd->count) > 1))
-               return -EINVAL;
+           (fd && atomic_read(&fd->count) > 1)) {
+               *new_fdp = dup_fd(fd, &error);
+               if (!*new_fdp)
+                       return error;
+       }
 
        return 0;
 }
@@ -1482,7 +1540,7 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
                goto bad_unshare_out;
        if ((err = unshare_fs(unshare_flags, &new_fs)))
                goto bad_unshare_cleanup_thread;
-       if ((err = unshare_namespace(unshare_flags, &new_ns)))
+       if ((err = unshare_namespace(unshare_flags, &new_ns, new_fs)))
                goto bad_unshare_cleanup_fs;
        if ((err = unshare_sighand(unshare_flags, &new_sigh)))
                goto bad_unshare_cleanup_ns;
@@ -1511,7 +1569,7 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
 
                if (new_sigh) {
                        sigh = current->sighand;
-                       current->sighand = new_sigh;
+                       rcu_assign_pointer(current->sighand, new_sigh);
                        new_sigh = sigh;
                }