Btrfs: finish read pages in the order they are submitted
[linux-2.6.git] / fs / exec.c
index 4a8849e..ba112bd 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -33,7 +33,7 @@
 #include <linux/string.h>
 #include <linux/init.h>
 #include <linux/pagemap.h>
-#include <linux/perf_counter.h>
+#include <linux/perf_event.h>
 #include <linux/highmem.h>
 #include <linux/spinlock.h>
 #include <linux/key.h>
@@ -55,6 +55,7 @@
 #include <linux/kmod.h>
 #include <linux/fsnotify.h>
 #include <linux/fs_struct.h>
+#include <linux/pipe_fs_i.h>
 
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
@@ -63,6 +64,7 @@
 
 int core_uses_pid;
 char core_pattern[CORENAME_MAX_SIZE] = "core";
+unsigned int core_pipe_limit;
 int suid_dumpable = 0;
 
 /* The maximal length of core_pattern is also specified in sysctl.c */
@@ -622,10 +624,8 @@ int setup_arg_pages(struct linux_binprm *bprm,
        /* Move stack pages down in memory. */
        if (stack_shift) {
                ret = shift_arg_pages(vma, stack_shift);
-               if (ret) {
-                       up_write(&mm->mmap_sem);
-                       return ret;
-               }
+               if (ret)
+                       goto out_unlock;
        }
 
 #ifdef CONFIG_STACK_GROWSUP
@@ -639,7 +639,7 @@ int setup_arg_pages(struct linux_binprm *bprm,
 
 out_unlock:
        up_write(&mm->mmap_sem);
-       return 0;
+       return ret;
 }
 EXPORT_SYMBOL(setup_arg_pages);
 
@@ -678,8 +678,8 @@ exit:
 }
 EXPORT_SYMBOL(open_exec);
 
-int kernel_read(struct file *file, unsigned long offset,
-       char *addr, unsigned long count)
+int kernel_read(struct file *file, loff_t offset,
+               char *addr, unsigned long count)
 {
        mm_segment_t old_fs;
        loff_t pos = offset;
@@ -845,6 +845,9 @@ static int de_thread(struct task_struct *tsk)
        sig->notify_count = 0;
 
 no_thread_group:
+       if (current->mm)
+               setmax_mm_hiwater_rss(&sig->maxrss, current->mm);
+
        exit_itimers(sig);
        flush_itimer_signals();
 
@@ -923,7 +926,7 @@ void set_task_comm(struct task_struct *tsk, char *buf)
        task_lock(tsk);
        strlcpy(tsk->comm, buf, sizeof(tsk->comm));
        task_unlock(tsk);
-       perf_counter_comm(tsk);
+       perf_event_comm(tsk);
 }
 
 int flush_old_exec(struct linux_binprm * bprm)
@@ -997,7 +1000,7 @@ int flush_old_exec(struct linux_binprm * bprm)
         * security domain:
         */
        if (!get_dumpable(current->mm))
-               perf_counter_exit_task(current);
+               perf_event_exit_task(current);
 
        /* An exec changes our domain. We are no longer part of the thread
           group */
@@ -1016,6 +1019,35 @@ out:
 EXPORT_SYMBOL(flush_old_exec);
 
 /*
+ * Prepare credentials and lock ->cred_guard_mutex.
+ * install_exec_creds() commits the new creds and drops the lock.
+ * Or, if exec fails before, free_bprm() should release ->cred and
+ * and unlock.
+ */
+int prepare_bprm_creds(struct linux_binprm *bprm)
+{
+       if (mutex_lock_interruptible(&current->cred_guard_mutex))
+               return -ERESTARTNOINTR;
+
+       bprm->cred = prepare_exec_creds();
+       if (likely(bprm->cred))
+               return 0;
+
+       mutex_unlock(&current->cred_guard_mutex);
+       return -ENOMEM;
+}
+
+void free_bprm(struct linux_binprm *bprm)
+{
+       free_arg_pages(bprm);
+       if (bprm->cred) {
+               mutex_unlock(&current->cred_guard_mutex);
+               abort_creds(bprm->cred);
+       }
+       kfree(bprm);
+}
+
+/*
  * install the new credentials for this executable
  */
 void install_exec_creds(struct linux_binprm *bprm)
@@ -1024,12 +1056,13 @@ void install_exec_creds(struct linux_binprm *bprm)
 
        commit_creds(bprm->cred);
        bprm->cred = NULL;
-
-       /* cred_guard_mutex must be held at least to this point to prevent
+       /*
+        * cred_guard_mutex must be held at least to this point to prevent
         * ptrace_attach() from altering our determination of the task's
-        * credentials; any time after this it may be unlocked */
-
+        * credentials; any time after this it may be unlocked.
+        */
        security_bprm_committed_creds(bprm);
+       mutex_unlock(&current->cred_guard_mutex);
 }
 EXPORT_SYMBOL(install_exec_creds);
 
@@ -1246,14 +1279,6 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
 
 EXPORT_SYMBOL(search_binary_handler);
 
-void free_bprm(struct linux_binprm *bprm)
-{
-       free_arg_pages(bprm);
-       if (bprm->cred)
-               abort_creds(bprm->cred);
-       kfree(bprm);
-}
-
 /*
  * sys_execve() executes a new program.
  */
@@ -1277,20 +1302,15 @@ int do_execve(char * filename,
        if (!bprm)
                goto out_files;
 
-       retval = -ERESTARTNOINTR;
-       if (mutex_lock_interruptible(&current->cred_guard_mutex))
+       retval = prepare_bprm_creds(bprm);
+       if (retval)
                goto out_free;
-       current->in_execve = 1;
-
-       retval = -ENOMEM;
-       bprm->cred = prepare_exec_creds();
-       if (!bprm->cred)
-               goto out_unlock;
 
        retval = check_unsafe_exec(bprm);
        if (retval < 0)
-               goto out_unlock;
+               goto out_free;
        clear_in_exec = retval;
+       current->in_execve = 1;
 
        file = open_exec(filename);
        retval = PTR_ERR(file);
@@ -1337,10 +1357,11 @@ int do_execve(char * filename,
        if (retval < 0)
                goto out;
 
+       current->stack_start = current->mm->start_stack;
+
        /* execve succeeded */
        current->fs->in_exec = 0;
        current->in_execve = 0;
-       mutex_unlock(&current->cred_guard_mutex);
        acct_update_integrals(current);
        free_bprm(bprm);
        if (displaced)
@@ -1360,10 +1381,7 @@ out_file:
 out_unmark:
        if (clear_in_exec)
                current->fs->in_exec = 0;
-
-out_unlock:
        current->in_execve = 0;
-       mutex_unlock(&current->cred_guard_mutex);
 
 out_free:
        free_bprm(bprm);
@@ -1375,18 +1393,16 @@ out_ret:
        return retval;
 }
 
-int set_binfmt(struct linux_binfmt *new)
+void set_binfmt(struct linux_binfmt *new)
 {
-       struct linux_binfmt *old = current->binfmt;
+       struct mm_struct *mm = current->mm;
 
-       if (new) {
-               if (!try_module_get(new->module))
-                       return -1;
-       }
-       current->binfmt = new;
-       if (old)
-               module_put(old->module);
-       return 0;
+       if (mm->binfmt)
+               module_put(mm->binfmt->module);
+
+       mm->binfmt = new;
+       if (new)
+               __module_get(new->module);
 }
 
 EXPORT_SYMBOL(set_binfmt);
@@ -1710,6 +1726,29 @@ int get_dumpable(struct mm_struct *mm)
        return (ret >= 2) ? 2 : ret;
 }
 
+static void wait_for_dump_helpers(struct file *file)
+{
+       struct pipe_inode_info *pipe;
+
+       pipe = file->f_path.dentry->d_inode->i_pipe;
+
+       pipe_lock(pipe);
+       pipe->readers++;
+       pipe->writers--;
+
+       while ((pipe->readers > 1) && (!signal_pending(current))) {
+               wake_up_interruptible_sync(&pipe->wait);
+               kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
+               pipe_wait(pipe);
+       }
+
+       pipe->readers--;
+       pipe->writers++;
+       pipe_unlock(pipe);
+
+}
+
+
 void do_coredump(long signr, int exit_code, struct pt_regs *regs)
 {
        struct core_state core_state;
@@ -1726,11 +1765,12 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
        unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
        char **helper_argv = NULL;
        int helper_argc = 0;
-       char *delimit;
+       int dump_count = 0;
+       static atomic_t core_dump_count = ATOMIC_INIT(0);
 
        audit_core_dumps(signr);
 
-       binfmt = current->binfmt;
+       binfmt = mm->binfmt;
        if (!binfmt || !binfmt->core_dump)
                goto fail;
 
@@ -1781,54 +1821,63 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
        lock_kernel();
        ispipe = format_corename(corename, signr);
        unlock_kernel();
-       /*
-        * Don't bother to check the RLIMIT_CORE value if core_pattern points
-        * to a pipe.  Since we're not writing directly to the filesystem
-        * RLIMIT_CORE doesn't really apply, as no actual core file will be
-        * created unless the pipe reader choses to write out the core file
-        * at which point file size limits and permissions will be imposed
-        * as it does with any other process
-        */
+
        if ((!ispipe) && (core_limit < binfmt->min_coredump))
                goto fail_unlock;
 
        if (ispipe) {
+               if (core_limit == 0) {
+                       /*
+                        * Normally core limits are irrelevant to pipes, since
+                        * we're not writing to the file system, but we use
+                        * core_limit of 0 here as a speacial value. Any
+                        * non-zero limit gets set to RLIM_INFINITY below, but
+                        * a limit of 0 skips the dump.  This is a consistent
+                        * way to catch recursive crashes.  We can still crash
+                        * if the core_pattern binary sets RLIM_CORE =  !0
+                        * but it runs as root, and can do lots of stupid things
+                        * Note that we use task_tgid_vnr here to grab the pid
+                        * of the process group leader.  That way we get the
+                        * right pid if a thread in a multi-threaded
+                        * core_pattern process dies.
+                        */
+                       printk(KERN_WARNING
+                               "Process %d(%s) has RLIMIT_CORE set to 0\n",
+                               task_tgid_vnr(current), current->comm);
+                       printk(KERN_WARNING "Aborting core\n");
+                       goto fail_unlock;
+               }
+
+               dump_count = atomic_inc_return(&core_dump_count);
+               if (core_pipe_limit && (core_pipe_limit < dump_count)) {
+                       printk(KERN_WARNING "Pid %d(%s) over core_pipe_limit\n",
+                              task_tgid_vnr(current), current->comm);
+                       printk(KERN_WARNING "Skipping core dump\n");
+                       goto fail_dropcount;
+               }
+
                helper_argv = argv_split(GFP_KERNEL, corename+1, &helper_argc);
                if (!helper_argv) {
                        printk(KERN_WARNING "%s failed to allocate memory\n",
                               __func__);
-                       goto fail_unlock;
-               }
-               /* Terminate the string before the first option */
-               delimit = strchr(corename, ' ');
-               if (delimit)
-                       *delimit = '\0';
-               delimit = strrchr(helper_argv[0], '/');
-               if (delimit)
-                       delimit++;
-               else
-                       delimit = helper_argv[0];
-               if (!strcmp(delimit, current->comm)) {
-                       printk(KERN_NOTICE "Recursive core dump detected, "
-                                       "aborting\n");
-                       goto fail_unlock;
+                       goto fail_dropcount;
                }
 
                core_limit = RLIM_INFINITY;
 
                /* SIGPIPE can happen, but it's just never processed */
-               if (call_usermodehelper_pipe(corename+1, helper_argv, NULL,
+               if (call_usermodehelper_pipe(helper_argv[0], helper_argv, NULL,
                                &file)) {
                        printk(KERN_INFO "Core dump to %s pipe failed\n",
                               corename);
-                       goto fail_unlock;
+                       goto fail_dropcount;
                }
        } else
                file = filp_open(corename,
                                 O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
                                 0600);
        if (IS_ERR(file))
-               goto fail_unlock;
+               goto fail_dropcount;
        inode = file->f_path.dentry->d_inode;
        if (inode->i_nlink > 1)
                goto close_fail;        /* multiple links - don't dump */
@@ -1857,7 +1906,12 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
        if (retval)
                current->signal->group_exit_code |= 0x80;
 close_fail:
+       if (ispipe && core_pipe_limit)
+               wait_for_dump_helpers(file);
        filp_close(file, NULL);
+fail_dropcount:
+       if (dump_count)
+               atomic_dec(&core_dump_count);
 fail_unlock:
        if (helper_argv)
                argv_free(helper_argv);