[PATCH] ipmi: use refcount in message handler
[safe/jmp/linux-2.6] / fs / exec.c
index e56ee24..cd6c574 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -48,6 +48,7 @@
 #include <linux/syscalls.h>
 #include <linux/rmap.h>
 #include <linux/acct.h>
+#include <linux/cn_proc.h>
 
 #include <asm/uaccess.h>
 #include <asm/mmu_context.h>
@@ -58,6 +59,9 @@
 
 int core_uses_pid;
 char core_pattern[65] = "core";
+int suid_dumpable = 0;
+
+EXPORT_SYMBOL(suid_dumpable);
 /* The maximal length of core_pattern is also specified in sysctl.c */
 
 static struct linux_binfmt *formats;
@@ -123,8 +127,7 @@ asmlinkage long sys_uselib(const char __user * library)
        struct nameidata nd;
        int error;
 
-       nd.intent.open.flags = FMODE_READ;
-       error = __user_walk(library, LOOKUP_FOLLOW|LOOKUP_OPEN, &nd);
+       error = __user_path_lookup_open(library, LOOKUP_FOLLOW, &nd, FMODE_READ);
        if (error)
                goto out;
 
@@ -136,7 +139,7 @@ asmlinkage long sys_uselib(const char __user * library)
        if (error)
                goto exit;
 
-       file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
+       file = nameidata_to_filp(&nd, O_RDONLY);
        error = PTR_ERR(file);
        if (IS_ERR(file))
                goto out;
@@ -164,6 +167,7 @@ asmlinkage long sys_uselib(const char __user * library)
 out:
        return error;
 exit:
+       release_open_intent(&nd);
        path_release(&nd);
        goto out;
 }
@@ -306,40 +310,36 @@ void install_arg_page(struct vm_area_struct *vma,
        pud_t * pud;
        pmd_t * pmd;
        pte_t * pte;
+       spinlock_t *ptl;
 
        if (unlikely(anon_vma_prepare(vma)))
-               goto out_sig;
+               goto out;
 
        flush_dcache_page(page);
        pgd = pgd_offset(mm, address);
-
-       spin_lock(&mm->page_table_lock);
        pud = pud_alloc(mm, pgd, address);
        if (!pud)
                goto out;
        pmd = pmd_alloc(mm, pud, address);
        if (!pmd)
                goto out;
-       pte = pte_alloc_map(mm, pmd, address);
+       pte = pte_alloc_map_lock(mm, pmd, address, &ptl);
        if (!pte)
                goto out;
        if (!pte_none(*pte)) {
-               pte_unmap(pte);
+               pte_unmap_unlock(pte, ptl);
                goto out;
        }
-       inc_mm_counter(mm, rss);
+       inc_mm_counter(mm, anon_rss);
        lru_cache_add_active(page);
        set_pte_at(mm, address, pte, pte_mkdirty(pte_mkwrite(mk_pte(
                                        page, vma->vm_page_prot))));
        page_add_anon_rmap(page, vma, address);
-       pte_unmap(pte);
-       spin_unlock(&mm->page_table_lock);
+       pte_unmap_unlock(pte, ptl);
 
        /* no need for flush_tlb */
        return;
 out:
-       spin_unlock(&mm->page_table_lock);
-out_sig:
        __free_page(page);
        force_sig(SIGKILL, current);
 }
@@ -418,11 +418,6 @@ int setup_arg_pages(struct linux_binprm *bprm,
        if (!mpnt)
                return -ENOMEM;
 
-       if (security_vm_enough_memory(arg_size >> PAGE_SHIFT)) {
-               kmem_cache_free(vm_area_cachep, mpnt);
-               return -ENOMEM;
-       }
-
        memset(mpnt, 0, sizeof(*mpnt));
 
        down_write(&mm->mmap_sem);
@@ -492,8 +487,7 @@ struct file *open_exec(const char *name)
        int err;
        struct file *file;
 
-       nd.intent.open.flags = FMODE_READ;
-       err = path_lookup(name, LOOKUP_FOLLOW|LOOKUP_OPEN, &nd);
+       err = path_lookup_open(name, LOOKUP_FOLLOW, &nd, FMODE_READ);
        file = ERR_PTR(err);
 
        if (!err) {
@@ -506,7 +500,7 @@ struct file *open_exec(const char *name)
                                err = -EACCES;
                        file = ERR_PTR(err);
                        if (!err) {
-                               file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
+                               file = nameidata_to_filp(&nd, O_RDONLY);
                                if (!IS_ERR(file)) {
                                        err = deny_write_access(file);
                                        if (err) {
@@ -518,6 +512,7 @@ out:
                                return file;
                        }
                }
+               release_open_intent(&nd);
                path_release(&nd);
        }
        goto out;
@@ -636,9 +631,22 @@ static inline int de_thread(struct task_struct *tsk)
        /*
         * Account for the thread group leader hanging around:
         */
-       count = 2;
-       if (thread_group_leader(current))
-               count = 1;
+       count = 1;
+       if (!thread_group_leader(current)) {
+               count = 2;
+               /*
+                * The SIGALRM timer survives the exec, but needs to point
+                * at us as the new group leader now.  We have a race with
+                * a timer firing now getting the old leader, so we need to
+                * synchronize with any firing (by calling del_timer_sync)
+                * before we can safely let the old group leader die.
+                */
+               sig->real_timer.data = (unsigned long)current;
+               spin_unlock_irq(lock);
+               if (del_timer_sync(&sig->real_timer))
+                       add_timer(&sig->real_timer);
+               spin_lock_irq(lock);
+       }
        while (atomic_read(&sig->count) > count) {
                sig->group_exit_task = current;
                sig->notify_count = count;
@@ -675,10 +683,8 @@ static inline int de_thread(struct task_struct *tsk)
                proc_dentry2 = proc_pid_unhash(leader);
                write_lock_irq(&tasklist_lock);
 
-               if (leader->tgid != current->tgid)
-                       BUG();
-               if (current->pid == current->tgid)
-                       BUG();
+               BUG_ON(leader->tgid != current->tgid);
+               BUG_ON(current->pid == current->tgid);
                /*
                 * An exec() starts a new thread group with the
                 * TGID of the previous thread group. Rehash the
@@ -726,14 +732,13 @@ static inline int de_thread(struct task_struct *tsk)
                proc_pid_flush(proc_dentry1);
                proc_pid_flush(proc_dentry2);
 
-               if (exit_state != EXIT_ZOMBIE)
-                       BUG();
+               BUG_ON(exit_state != EXIT_ZOMBIE);
                release_task(leader);
         }
 
        /*
-        * Now there are really no other threads at all,
-        * so it's safe to stop telling them to kill themselves.
+        * There may be one thread left which is just exiting,
+        * but it's safe to stop telling the group to kill themselves.
         */
        sig->flags = 0;
 
@@ -772,10 +777,7 @@ no_thread_group:
                        kmem_cache_free(sighand_cachep, oldsighand);
        }
 
-       if (!thread_group_empty(current))
-               BUG();
-       if (!thread_group_leader(current))
-               BUG();
+       BUG_ON(!thread_group_leader(current));
        return 0;
 }
        
@@ -787,6 +789,7 @@ no_thread_group:
 static inline void flush_old_files(struct files_struct * files)
 {
        long j = -1;
+       struct fdtable *fdt;
 
        spin_lock(&files->file_lock);
        for (;;) {
@@ -794,12 +797,13 @@ static inline void flush_old_files(struct files_struct * files)
 
                j++;
                i = j * __NFDBITS;
-               if (i >= files->max_fds || i >= files->max_fdset)
+               fdt = files_fdtable(files);
+               if (i >= fdt->max_fds || i >= fdt->max_fdset)
                        break;
-               set = files->close_on_exec->fds_bits[j];
+               set = fdt->close_on_exec->fds_bits[j];
                if (!set)
                        continue;
-               files->close_on_exec->fds_bits[j] = 0;
+               fdt->close_on_exec->fds_bits[j] = 0;
                spin_unlock(&files->file_lock);
                for ( ; set ; i++,set >>= 1) {
                        if (set & 1) {
@@ -868,6 +872,9 @@ int flush_old_exec(struct linux_binprm * bprm)
 
        if (current->euid == current->uid && current->egid == current->gid)
                current->mm->dumpable = 1;
+       else
+               current->mm->dumpable = suid_dumpable;
+
        name = bprm->filename;
 
        /* Copies the binary name from after last slash */
@@ -888,7 +895,7 @@ int flush_old_exec(struct linux_binprm * bprm)
            permission(bprm->file->f_dentry->d_inode,MAY_READ, NULL) ||
            (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP)) {
                suid_keys(current);
-               current->mm->dumpable = 0;
+               current->mm->dumpable = suid_dumpable;
        }
 
        /* An exec changes our domain. We are no longer part of the thread
@@ -1090,6 +1097,7 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
                                        fput(bprm->file);
                                bprm->file = NULL;
                                current->did_exec = 1;
+                               proc_exec_connector(current);
                                return retval;
                        }
                        read_lock(&binfmt_lock);
@@ -1197,7 +1205,6 @@ int do_execve(char * filename,
                /* execve success */
                security_bprm_free(bprm);
                acct_update_integrals(current);
-               update_mem_hiwater(current);
                kfree(bprm);
                return retval;
        }
@@ -1412,19 +1419,16 @@ static void zap_threads (struct mm_struct *mm)
 static void coredump_wait(struct mm_struct *mm)
 {
        DECLARE_COMPLETION(startup_done);
+       int core_waiters;
 
-       mm->core_waiters++; /* let other threads block */
        mm->core_startup_done = &startup_done;
 
-       /* give other threads a chance to run: */
-       yield();
-
        zap_threads(mm);
-       if (--mm->core_waiters) {
-               up_write(&mm->mmap_sem);
+       core_waiters = mm->core_waiters;
+       up_write(&mm->mmap_sem);
+
+       if (core_waiters)
                wait_for_completion(&startup_done);
-       } else
-               up_write(&mm->mmap_sem);
        BUG_ON(mm->core_waiters);
 }
 
@@ -1436,6 +1440,8 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
        struct inode * inode;
        struct file * file;
        int retval = 0;
+       int fsuid = current->fsuid;
+       int flag = 0;
 
        binfmt = current->binfmt;
        if (!binfmt || !binfmt->core_dump)
@@ -1445,12 +1451,32 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
                up_write(&mm->mmap_sem);
                goto fail;
        }
+
+       /*
+        *      We cannot trust fsuid as being the "true" uid of the
+        *      process nor do we know its entire history. We only know it
+        *      was tainted so we dump it as root in mode 2.
+        */
+       if (mm->dumpable == 2) {        /* Setuid core dump mode */
+               flag = O_EXCL;          /* Stop rewrite attacks */
+               current->fsuid = 0;     /* Dump root private */
+       }
        mm->dumpable = 0;
-       init_completion(&mm->core_done);
+
+       retval = -EAGAIN;
        spin_lock_irq(&current->sighand->siglock);
-       current->signal->flags = SIGNAL_GROUP_EXIT;
-       current->signal->group_exit_code = exit_code;
+       if (!(current->signal->flags & SIGNAL_GROUP_EXIT)) {
+               current->signal->flags = SIGNAL_GROUP_EXIT;
+               current->signal->group_exit_code = exit_code;
+               retval = 0;
+       }
        spin_unlock_irq(&current->sighand->siglock);
+       if (retval) {
+               up_write(&mm->mmap_sem);
+               goto fail;
+       }
+
+       init_completion(&mm->core_done);
        coredump_wait(mm);
 
        /*
@@ -1470,7 +1496,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
        lock_kernel();
        format_corename(corename, core_pattern, signr);
        unlock_kernel();
-       file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE, 0600);
+       file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag, 0600);
        if (IS_ERR(file))
                goto fail_unlock;
        inode = file->f_dentry->d_inode;
@@ -1485,7 +1511,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
                goto close_fail;
        if (!file->f_op->write)
                goto close_fail;
-       if (do_truncate(file->f_dentry, 0) != 0)
+       if (do_truncate(file->f_dentry, 0, file) != 0)
                goto close_fail;
 
        retval = binfmt->core_dump(signr, regs, file);
@@ -1495,6 +1521,7 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs)
 close_fail:
        filp_close(file, NULL);
 fail_unlock:
+       current->fsuid = fsuid;
        complete_all(&mm->core_done);
 fail:
        return retval;