Merge branch 'for-linus' of git://oss.sgi.com/xfs/xfs into for-2.6.34-incoming
[safe/jmp/linux-2.6] / fs / binfmt_elf.c
index 40381df..fd5b2ea 100644 (file)
@@ -44,8 +44,8 @@ static unsigned long elf_map(struct file *, unsigned long, struct elf_phdr *,
  * If we don't support core dumping, then supply a NULL so we
  * don't even try.
  */
-#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE)
-static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
+#ifdef CONFIG_ELF_CORE
+static int elf_core_dump(struct coredump_params *cprm);
 #else
 #define elf_core_dump  NULL
 #endif
@@ -501,22 +501,22 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
                }
        }
 
-       /*
-        * Now fill out the bss section.  First pad the last page up
-        * to the page boundary, and then perform a mmap to make sure
-        * that there are zero-mapped pages up to and including the 
-        * last bss page.
-        */
-       if (padzero(elf_bss)) {
-               error = -EFAULT;
-               goto out_close;
-       }
+       if (last_bss > elf_bss) {
+               /*
+                * Now fill out the bss section.  First pad the last page up
+                * to the page boundary, and then perform a mmap to make sure
+                * that there are zero-mapped pages up to and including the
+                * last bss page.
+                */
+               if (padzero(elf_bss)) {
+                       error = -EFAULT;
+                       goto out_close;
+               }
 
-       /* What we have mapped so far */
-       elf_bss = ELF_PAGESTART(elf_bss + ELF_MIN_ALIGN - 1);
+               /* What we have mapped so far */
+               elf_bss = ELF_PAGESTART(elf_bss + ELF_MIN_ALIGN - 1);
 
-       /* Map the last of the bss segment */
-       if (last_bss > elf_bss) {
+               /* Map the last of the bss segment */
                down_write(&current->mm->mmap_sem);
                error = do_brk(elf_bss, last_bss - elf_bss);
                up_write(&current->mm->mmap_sem);
@@ -662,27 +662,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
                        if (elf_interpreter[elf_ppnt->p_filesz - 1] != '\0')
                                goto out_free_interp;
 
-                       /*
-                        * The early SET_PERSONALITY here is so that the lookup
-                        * for the interpreter happens in the namespace of the 
-                        * to-be-execed image.  SET_PERSONALITY can select an
-                        * alternate root.
-                        *
-                        * However, SET_PERSONALITY is NOT allowed to switch
-                        * this task into the new images's memory mapping
-                        * policy - that is, TASK_SIZE must still evaluate to
-                        * that which is appropriate to the execing application.
-                        * This is because exit_mmap() needs to have TASK_SIZE
-                        * evaluate to the size of the old image.
-                        *
-                        * So if (say) a 64-bit application is execing a 32-bit
-                        * application it is the architecture's responsibility
-                        * to defer changing the value of TASK_SIZE until the
-                        * switch really is going to happen - do this in
-                        * flush_thread().      - akpm
-                        */
-                       SET_PERSONALITY(loc->elf_ex);
-
                        interpreter = open_exec(elf_interpreter);
                        retval = PTR_ERR(interpreter);
                        if (IS_ERR(interpreter))
@@ -730,9 +709,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
                /* Verify the interpreter has a valid arch */
                if (!elf_check_arch(&loc->interp_elf_ex))
                        goto out_free_dentry;
-       } else {
-               /* Executables without an interpreter also need a personality  */
-               SET_PERSONALITY(loc->elf_ex);
        }
 
        /* Flush all traces of the currently running executable */
@@ -752,7 +728,8 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 
        if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
                current->flags |= PF_RANDOMIZE;
-       arch_pick_mmap_layout(current->mm);
+
+       setup_new_exec(bprm);
 
        /* Do this so that we can load the interpreter, if need be.  We will
           change some of these later */
@@ -767,7 +744,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
        
        current->mm->start_stack = bprm->p;
 
-       /* Now we do a little grungy work by mmaping the ELF image into
+       /* Now we do a little grungy work by mmapping the ELF image into
           the correct location in memory. */
        for(i = 0, elf_ppnt = elf_phdata;
            i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
@@ -1101,12 +1078,7 @@ out:
        return error;
 }
 
-/*
- * Note that some platforms still use traditional core dumps and not
- * the ELF core dump.  Each platform can select it as appropriate.
- */
-#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE)
-
+#ifdef CONFIG_ELF_CORE
 /*
  * ELF core dumper
  *
@@ -1277,11 +1249,9 @@ static int writenote(struct memelfnote *men, struct file *file,
 }
 #undef DUMP_WRITE
 
-#define DUMP_WRITE(addr, nr)   \
-       if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
-               goto end_coredump;
-#define DUMP_SEEK(off) \
-       if (!dump_seek(file, (off))) \
+#define DUMP_WRITE(addr, nr)                           \
+       if ((size += (nr)) > cprm->limit ||             \
+           !dump_write(cprm->file, (addr), (nr)))      \
                goto end_coredump;
 
 static void fill_elf_header(struct elfhdr *elf, int segs,
@@ -1340,8 +1310,10 @@ static void fill_prstatus(struct elf_prstatus *prstatus,
        prstatus->pr_info.si_signo = prstatus->pr_cursig = signr;
        prstatus->pr_sigpend = p->pending.signal.sig[0];
        prstatus->pr_sighold = p->blocked.sig[0];
+       rcu_read_lock();
+       prstatus->pr_ppid = task_pid_vnr(rcu_dereference(p->real_parent));
+       rcu_read_unlock();
        prstatus->pr_pid = task_pid_vnr(p);
-       prstatus->pr_ppid = task_pid_vnr(p->real_parent);
        prstatus->pr_pgrp = task_pgrp_vnr(p);
        prstatus->pr_sid = task_session_vnr(p);
        if (thread_group_leader(p)) {
@@ -1382,8 +1354,10 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
                        psinfo->pr_psargs[i] = ' ';
        psinfo->pr_psargs[len] = 0;
 
+       rcu_read_lock();
+       psinfo->pr_ppid = task_pid_vnr(rcu_dereference(p->real_parent));
+       rcu_read_unlock();
        psinfo->pr_pid = task_pid_vnr(p);
-       psinfo->pr_ppid = task_pid_vnr(p->real_parent);
        psinfo->pr_pgrp = task_pgrp_vnr(p);
        psinfo->pr_sid = task_session_vnr(p);
 
@@ -1518,11 +1492,11 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
        info->thread = NULL;
 
        psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL);
-       fill_note(&info->psinfo, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo);
-
        if (psinfo == NULL)
                return 0;
 
+       fill_note(&info->psinfo, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo);
+
        /*
         * Figure out how many notes we're going to need for each thread.
         */
@@ -1710,42 +1684,52 @@ struct elf_note_info {
        int numnote;
 };
 
-static int fill_note_info(struct elfhdr *elf, int phdrs,
-                         struct elf_note_info *info,
-                         long signr, struct pt_regs *regs)
+static int elf_note_info_init(struct elf_note_info *info)
 {
-#define        NUM_NOTES       6
-       struct list_head *t;
-
-       info->notes = NULL;
-       info->prstatus = NULL;
-       info->psinfo = NULL;
-       info->fpu = NULL;
-#ifdef ELF_CORE_COPY_XFPREGS
-       info->xfpu = NULL;
-#endif
+       memset(info, 0, sizeof(*info));
        INIT_LIST_HEAD(&info->thread_list);
 
-       info->notes = kmalloc(NUM_NOTES * sizeof(struct memelfnote),
-                             GFP_KERNEL);
+       /* Allocate space for six ELF notes */
+       info->notes = kmalloc(6 * sizeof(struct memelfnote), GFP_KERNEL);
        if (!info->notes)
                return 0;
        info->psinfo = kmalloc(sizeof(*info->psinfo), GFP_KERNEL);
        if (!info->psinfo)
-               return 0;
+               goto notes_free;
        info->prstatus = kmalloc(sizeof(*info->prstatus), GFP_KERNEL);
        if (!info->prstatus)
-               return 0;
+               goto psinfo_free;
        info->fpu = kmalloc(sizeof(*info->fpu), GFP_KERNEL);
        if (!info->fpu)
-               return 0;
+               goto prstatus_free;
 #ifdef ELF_CORE_COPY_XFPREGS
        info->xfpu = kmalloc(sizeof(*info->xfpu), GFP_KERNEL);
        if (!info->xfpu)
-               return 0;
+               goto fpu_free;
 #endif
+       return 1;
+#ifdef ELF_CORE_COPY_XFPREGS
+ fpu_free:
+       kfree(info->fpu);
+#endif
+ prstatus_free:
+       kfree(info->prstatus);
+ psinfo_free:
+       kfree(info->psinfo);
+ notes_free:
+       kfree(info->notes);
+       return 0;
+}
+
+static int fill_note_info(struct elfhdr *elf, int phdrs,
+                         struct elf_note_info *info,
+                         long signr, struct pt_regs *regs)
+{
+       struct list_head *t;
+
+       if (!elf_note_info_init(info))
+               return 0;
 
-       info->thread_status_size = 0;
        if (signr) {
                struct core_thread *ct;
                struct elf_thread_status *ets;
@@ -1805,8 +1789,6 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
 #endif
 
        return 1;
-
-#undef NUM_NOTES
 }
 
 static size_t get_note_info_size(struct elf_note_info *info)
@@ -1897,7 +1879,7 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma,
  * and then they are actually written out.  If we run out of core limit
  * we just truncate.
  */
-static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
+static int elf_core_dump(struct coredump_params *cprm)
 {
        int has_dumped = 0;
        mm_segment_t fs;
@@ -1925,7 +1907,10 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
        elf = kmalloc(sizeof(*elf), GFP_KERNEL);
        if (!elf)
                goto out;
-       
+       /*
+        * The number of segs are recored into ELF header as 16bit value.
+        * Please check DEFAULT_MAX_MAP_COUNT definition when you modify here.
+        */
        segs = current->mm->map_count;
 #ifdef ELF_CORE_EXTRA_PHDRS
        segs += ELF_CORE_EXTRA_PHDRS;
@@ -1940,7 +1925,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
         * notes.  This also sets up the file header.
         */
        if (!fill_note_info(elf, segs + 1, /* including notes section */
-                           &info, signr, regs))
+                           &info, cprm->signr, cprm->regs))
                goto cleanup;
 
        has_dumped = 1;
@@ -2002,14 +1987,15 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
 #endif
 
        /* write out the notes section */
-       if (!write_note_info(&info, file, &foffset))
+       if (!write_note_info(&info, cprm->file, &foffset))
                goto end_coredump;
 
-       if (elf_coredump_extra_notes_write(file, &foffset))
+       if (elf_coredump_extra_notes_write(cprm->file, &foffset))
                goto end_coredump;
 
        /* Align to page */
-       DUMP_SEEK(dataoff - foffset);
+       if (!dump_seek(cprm->file, dataoff - foffset))
+               goto end_coredump;
 
        for (vma = first_vma(current, gate_vma); vma != NULL;
                        vma = next_vma(vma, gate_vma)) {
@@ -2020,33 +2006,20 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
 
                for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) {
                        struct page *page;
-                       struct vm_area_struct *tmp_vma;
-
-                       if (get_user_pages(current, current->mm, addr, 1, 0, 1,
-                                               &page, &tmp_vma) <= 0) {
-                               DUMP_SEEK(PAGE_SIZE);
-                       } else {
-                               if (page == ZERO_PAGE(0)) {
-                                       if (!dump_seek(file, PAGE_SIZE)) {
-                                               page_cache_release(page);
-                                               goto end_coredump;
-                                       }
-                               } else {
-                                       void *kaddr;
-                                       flush_cache_page(tmp_vma, addr,
-                                                        page_to_pfn(page));
-                                       kaddr = kmap(page);
-                                       if ((size += PAGE_SIZE) > limit ||
-                                           !dump_write(file, kaddr,
-                                           PAGE_SIZE)) {
-                                               kunmap(page);
-                                               page_cache_release(page);
-                                               goto end_coredump;
-                                       }
-                                       kunmap(page);
-                               }
+                       int stop;
+
+                       page = get_dump_page(addr);
+                       if (page) {
+                               void *kaddr = kmap(page);
+                               stop = ((size += PAGE_SIZE) > cprm->limit) ||
+                                       !dump_write(cprm->file, kaddr,
+                                                   PAGE_SIZE);
+                               kunmap(page);
                                page_cache_release(page);
-                       }
+                       } else
+                               stop = !dump_seek(cprm->file, PAGE_SIZE);
+                       if (stop)
+                               goto end_coredump;
                }
        }
 
@@ -2064,7 +2037,7 @@ out:
        return has_dumped;
 }
 
-#endif         /* USE_ELF_CORE_DUMP */
+#endif         /* CONFIG_ELF_CORE */
 
 static int __init init_elf_binfmt(void)
 {