]> bbs.cooldavid.org Git - net-next-2.6.git/blobdiff - fs/exec.c
exec: make do_coredump() more resilient to recursive crashes
[net-next-2.6.git] / fs / exec.c
index 4a8849e45b2191ef5a91821ad3b47d0588e7b12b..735d9c18ec719534276bbe38ab68c3c69fb6cfdb 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>
@@ -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 */
@@ -1015,6 +1018,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
  */
@@ -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);
@@ -1781,38 +1799,39 @@ 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;
+               }
+
                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;
-               }
 
                core_limit = RLIM_INFINITY;