NFSd: Move default initialisers from create_client() to alloc_client()
[pandora-kernel.git] / fs / exec.c
index 3625464..78199eb 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -973,6 +973,9 @@ static int de_thread(struct task_struct *tsk)
        sig->notify_count = 0;
 
 no_thread_group:
+       /* we have changed execution domain */
+       tsk->exit_signal = SIGCHLD;
+
        if (current->mm)
                setmax_mm_hiwater_rss(&sig->maxrss, current->mm);
 
@@ -1092,7 +1095,8 @@ int flush_old_exec(struct linux_binprm * bprm)
        bprm->mm = NULL;                /* We're using it now */
 
        set_fs(USER_DS);
-       current->flags &= ~(PF_RANDOMIZE | PF_KTHREAD);
+       current->flags &=
+               ~(PF_RANDOMIZE | PF_KTHREAD | PF_NOFREEZE | PF_FREEZER_NOSIG);
        flush_thread();
        current->personality &= ~bprm->per_clear;
 
@@ -1155,13 +1159,6 @@ void setup_new_exec(struct linux_binprm * bprm)
                        set_dumpable(current->mm, suid_dumpable);
        }
 
-       /*
-        * Flush performance counters when crossing a
-        * security domain:
-        */
-       if (!get_dumpable(current->mm))
-               perf_event_exit_task(current);
-
        /* An exec changes our domain. We are no longer part of the thread
           group */
 
@@ -1198,9 +1195,24 @@ void free_bprm(struct linux_binprm *bprm)
                mutex_unlock(&current->signal->cred_guard_mutex);
                abort_creds(bprm->cred);
        }
+       /* If a binfmt changed the interp, free it. */
+       if (bprm->interp != bprm->filename)
+               kfree(bprm->interp);
        kfree(bprm);
 }
 
+int bprm_change_interp(char *interp, struct linux_binprm *bprm)
+{
+       /* If a binfmt changed the interp, free it first. */
+       if (bprm->interp != bprm->filename)
+               kfree(bprm->interp);
+       bprm->interp = kstrdup(interp, GFP_KERNEL);
+       if (!bprm->interp)
+               return -ENOMEM;
+       return 0;
+}
+EXPORT_SYMBOL(bprm_change_interp);
+
 /*
  * install the new credentials for this executable
  */
@@ -1210,6 +1222,15 @@ void install_exec_creds(struct linux_binprm *bprm)
 
        commit_creds(bprm->cred);
        bprm->cred = NULL;
+
+       /*
+        * Disable monitoring for regular users
+        * when executing setuid binaries. Must
+        * wait until new credentials are committed
+        * by commit_creds() above
+        */
+       if (get_dumpable(current->mm) != SUID_DUMP_USER)
+               perf_event_exit_task(current);
        /*
         * cred_guard_mutex must be held at least to this point to prevent
         * ptrace_attach() from altering our determination of the task's
@@ -1366,6 +1387,10 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
        struct linux_binfmt *fmt;
        pid_t old_pid;
 
+       /* This allows 4 levels of binfmt rewrites before failing hard. */
+       if (depth > 5)
+               return -ELOOP;
+
        retval = security_bprm_check(bprm);
        if (retval)
                return retval;
@@ -1389,12 +1414,8 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
                        if (!try_module_get(fmt->module))
                                continue;
                        read_unlock(&binfmt_lock);
+                       bprm->recursion_depth = depth + 1;
                        retval = fn(bprm, regs);
-                       /*
-                        * Restore the depth counter to its starting value
-                        * in this call, so we don't have to rely on every
-                        * load_binary function to restore it on return.
-                        */
                        bprm->recursion_depth = depth;
                        if (retval >= 0) {
                                if (depth == 0)
@@ -2011,6 +2032,12 @@ static int __get_dumpable(unsigned long mm_flags)
        return (ret >= 2) ? 2 : ret;
 }
 
+/*
+ * This returns the actual value of the suid_dumpable flag. For things
+ * that are using this for checking for privilege transitions, it must
+ * test against SUID_DUMP_USER rather than treating it as a boolean
+ * value.
+ */
 int get_dumpable(struct mm_struct *mm)
 {
        return __get_dumpable(mm->flags);