Merge branch 'pcmcia-next' of git://git.kernel.org/pub/scm/linux/kernel/git/brodo...
[linux-2.6-microblaze.git] / fs / exec.c
index db17be5..06b4c55 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -985,6 +985,32 @@ int kernel_read_file_from_path(const char *path, void **buf, loff_t *size,
 }
 EXPORT_SYMBOL_GPL(kernel_read_file_from_path);
 
+int kernel_read_file_from_path_initns(const char *path, void **buf,
+                                     loff_t *size, loff_t max_size,
+                                     enum kernel_read_file_id id)
+{
+       struct file *file;
+       struct path root;
+       int ret;
+
+       if (!path || !*path)
+               return -EINVAL;
+
+       task_lock(&init_task);
+       get_fs_root(init_task.fs, &root);
+       task_unlock(&init_task);
+
+       file = file_open_root(root.dentry, root.mnt, path, O_RDONLY, 0);
+       path_put(&root);
+       if (IS_ERR(file))
+               return PTR_ERR(file);
+
+       ret = kernel_read_file(file, buf, size, max_size, id);
+       fput(file);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(kernel_read_file_from_path_initns);
+
 int kernel_read_file_from_fd(int fd, void **buf, loff_t *size, loff_t max_size,
                             enum kernel_read_file_id id)
 {
@@ -1010,16 +1036,26 @@ ssize_t read_code(struct file *file, unsigned long addr, loff_t pos, size_t len)
 }
 EXPORT_SYMBOL(read_code);
 
+/*
+ * Maps the mm_struct mm into the current task struct.
+ * On success, this function returns with the mutex
+ * exec_update_mutex locked.
+ */
 static int exec_mmap(struct mm_struct *mm)
 {
        struct task_struct *tsk;
        struct mm_struct *old_mm, *active_mm;
+       int ret;
 
        /* Notify parent that we're no longer interested in the old VM */
        tsk = current;
        old_mm = current->mm;
        exec_mm_release(tsk, old_mm);
 
+       ret = mutex_lock_killable(&tsk->signal->exec_update_mutex);
+       if (ret)
+               return ret;
+
        if (old_mm) {
                sync_mm_rss(old_mm);
                /*
@@ -1031,9 +1067,11 @@ static int exec_mmap(struct mm_struct *mm)
                down_read(&old_mm->mmap_sem);
                if (unlikely(old_mm->core_state)) {
                        up_read(&old_mm->mmap_sem);
+                       mutex_unlock(&tsk->signal->exec_update_mutex);
                        return -EINTR;
                }
        }
+
        task_lock(tsk);
        active_mm = tsk->active_mm;
        membarrier_exec_mmap(mm);
@@ -1189,10 +1227,22 @@ no_thread_group:
        /* we have changed execution domain */
        tsk->exit_signal = SIGCHLD;
 
-#ifdef CONFIG_POSIX_TIMERS
-       exit_itimers(sig);
-       flush_itimer_signals();
-#endif
+       BUG_ON(!thread_group_leader(tsk));
+       return 0;
+
+killed:
+       /* protects against exit_notify() and __exit_signal() */
+       read_lock(&tasklist_lock);
+       sig->group_exit_task = NULL;
+       sig->notify_count = 0;
+       read_unlock(&tasklist_lock);
+       return -EAGAIN;
+}
+
+
+static int unshare_sighand(struct task_struct *me)
+{
+       struct sighand_struct *oldsighand = me->sighand;
 
        if (refcount_read(&oldsighand->count) != 1) {
                struct sighand_struct *newsighand;
@@ -1210,23 +1260,13 @@ no_thread_group:
 
                write_lock_irq(&tasklist_lock);
                spin_lock(&oldsighand->siglock);
-               rcu_assign_pointer(tsk->sighand, newsighand);
+               rcu_assign_pointer(me->sighand, newsighand);
                spin_unlock(&oldsighand->siglock);
                write_unlock_irq(&tasklist_lock);
 
                __cleanup_sighand(oldsighand);
        }
-
-       BUG_ON(!thread_group_leader(tsk));
        return 0;
-
-killed:
-       /* protects against exit_notify() and __exit_signal() */
-       read_lock(&tasklist_lock);
-       sig->group_exit_task = NULL;
-       sig->notify_count = 0;
-       read_unlock(&tasklist_lock);
-       return -EAGAIN;
 }
 
 char *__get_task_comm(char *buf, size_t buf_size, struct task_struct *tsk)
@@ -1260,13 +1300,13 @@ void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec)
  */
 int flush_old_exec(struct linux_binprm * bprm)
 {
+       struct task_struct *me = current;
        int retval;
 
        /*
-        * Make sure we have a private signal table and that
-        * we are unassociated from the previous thread group.
+        * Make this the only thread in the thread group.
         */
-       retval = de_thread(current);
+       retval = de_thread(me);
        if (retval)
                goto out;
 
@@ -1286,18 +1326,31 @@ int flush_old_exec(struct linux_binprm * bprm)
                goto out;
 
        /*
-        * After clearing bprm->mm (to mark that current is using the
-        * prepared mm now), we have nothing left of the original
+        * After setting bprm->called_exec_mmap (to mark that current is
+        * using the prepared mm now), we have nothing left of the original
         * process. If anything from here on returns an error, the check
         * in search_binary_handler() will SEGV current.
         */
+       bprm->called_exec_mmap = 1;
        bprm->mm = NULL;
 
+#ifdef CONFIG_POSIX_TIMERS
+       exit_itimers(me->signal);
+       flush_itimer_signals();
+#endif
+
+       /*
+        * Make the signal table private.
+        */
+       retval = unshare_sighand(me);
+       if (retval)
+               goto out;
+
        set_fs(USER_DS);
-       current->flags &= ~(PF_RANDOMIZE | PF_FORKNOEXEC | PF_KTHREAD |
+       me->flags &= ~(PF_RANDOMIZE | PF_FORKNOEXEC | PF_KTHREAD |
                                        PF_NOFREEZE | PF_NO_SETAFFINITY);
        flush_thread();
-       current->personality &= ~bprm->per_clear;
+       me->personality &= ~bprm->per_clear;
 
        /*
         * We have to apply CLOEXEC before we change whether the process is
@@ -1305,7 +1358,7 @@ int flush_old_exec(struct linux_binprm * bprm)
         * trying to access the should-be-closed file descriptors of a process
         * undergoing exec(2).
         */
-       do_close_on_exec(current->files);
+       do_close_on_exec(me->files);
        return 0;
 
 out:
@@ -1386,7 +1439,7 @@ void setup_new_exec(struct linux_binprm * bprm)
 
        /* An exec changes our domain. We are no longer part of the thread
           group */
-       current->self_exec_id++;
+       WRITE_ONCE(current->self_exec_id, current->self_exec_id + 1);
        flush_signal_handlers(current, 0);
 }
 EXPORT_SYMBOL(setup_new_exec);
@@ -1424,6 +1477,8 @@ static void free_bprm(struct linux_binprm *bprm)
 {
        free_arg_pages(bprm);
        if (bprm->cred) {
+               if (bprm->called_exec_mmap)
+                       mutex_unlock(&current->signal->exec_update_mutex);
                mutex_unlock(&current->signal->cred_guard_mutex);
                abort_creds(bprm->cred);
        }
@@ -1473,6 +1528,7 @@ void install_exec_creds(struct linux_binprm *bprm)
         * credentials; any time after this it may be unlocked.
         */
        security_bprm_committed_creds(bprm);
+       mutex_unlock(&current->signal->exec_update_mutex);
        mutex_unlock(&current->signal->cred_guard_mutex);
 }
 EXPORT_SYMBOL(install_exec_creds);
@@ -1664,7 +1720,7 @@ int search_binary_handler(struct linux_binprm *bprm)
 
                read_lock(&binfmt_lock);
                put_binfmt(fmt);
-               if (retval < 0 && !bprm->mm) {
+               if (retval < 0 && bprm->called_exec_mmap) {
                        /* we got to flush_old_exec() and failed after it */
                        read_unlock(&binfmt_lock);
                        force_sigsegv(SIGSEGV);