UBUNTU: Ubuntu-2.6.38-12.51
[linux-flexiantxendom0-natty.git] / fs / binfmt_elf.c
index c1a4995..fcfbc16 100644 (file)
@@ -66,12 +66,11 @@ static int elf_core_dump(struct coredump_params *cprm);
 #define ELF_PAGEALIGN(_v) (((_v) + ELF_MIN_ALIGN - 1) & ~(ELF_MIN_ALIGN - 1))
 
 static struct linux_binfmt elf_format = {
-               .module         = THIS_MODULE,
-               .load_binary    = load_elf_binary,
-               .load_shlib     = load_elf_library,
-               .core_dump      = elf_core_dump,
-               .min_coredump   = ELF_EXEC_PAGESIZE,
-               .hasvdso        = 1
+       .module         = THIS_MODULE,
+       .load_binary    = load_elf_binary,
+       .load_shlib     = load_elf_library,
+       .core_dump      = elf_core_dump,
+       .min_coredump   = ELF_EXEC_PAGESIZE,
 };
 
 #define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE)
@@ -316,8 +315,6 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
        return 0;
 }
 
-#ifndef elf_map
-
 static unsigned long elf_map(struct file *filep, unsigned long addr,
                struct elf_phdr *eppnt, int prot, int type,
                unsigned long total_size)
@@ -354,8 +351,6 @@ static unsigned long elf_map(struct file *filep, unsigned long addr,
        return(map_addr);
 }
 
-#endif /* !elf_map */
-
 static unsigned long total_mapping_size(struct elf_phdr *cmds, int nr)
 {
        int i, first_idx = -1, last_idx = -1;
@@ -421,7 +416,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
                goto out;
 
        retval = kernel_read(interpreter, interp_elf_ex->e_phoff,
-                            (char *)elf_phdata,size);
+                            (char *)elf_phdata, size);
        error = -EIO;
        if (retval != size) {
                if (retval < 0)
@@ -601,7 +596,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
                goto out;
        if (!elf_check_arch(&loc->elf_ex))
                goto out;
-       if (!bprm->file->f_op||!bprm->file->f_op->mmap)
+       if (!bprm->file->f_op || !bprm->file->f_op->mmap)
                goto out;
 
        /* Now read in all of the header information */
@@ -717,6 +712,15 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
        if (retval)
                goto out_free_dentry;
 
+#ifdef CONFIG_X86_32
+       /*
+        * Turn off the CS limit completely if exec-shield disabled or
+        * NX active:
+        */
+       if (disable_nx || executable_stack != EXSTACK_DISABLE_X || (__supported_pte_mask & _PAGE_NX))
+               arch_add_exec_range(current->mm, -1);
+#endif
+
        /* OK, This is the point of no return */
        current->flags &= ~PF_FORKNOEXEC;
        current->mm->def_flags = def_flags;
@@ -761,8 +765,8 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
                        /* There was a PT_LOAD segment with p_memsz > p_filesz
                           before this one. Map anonymous pages, if needed,
                           and clear the area.  */
-                       retval = set_brk (elf_bss + load_bias,
-                                         elf_brk + load_bias);
+                       retval = set_brk(elf_bss + load_bias,
+                                        elf_brk + load_bias);
                        if (retval) {
                                send_sig(SIGKILL, current, 0);
                                goto out_free_dentry;
@@ -800,7 +804,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
                         * default mmap base, as well as whatever program they
                         * might try to exec.  This is because the brk will
                         * follow the loader, and is not movable.  */
-#ifdef CONFIG_X86
+#if defined(CONFIG_X86) || defined(CONFIG_ARM)
                        load_bias = 0;
 #else
                        load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
@@ -946,9 +950,13 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
        current->mm->start_stack = bprm->p;
 
 #ifdef arch_randomize_brk
-       if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1))
+       if ((current->flags & PF_RANDOMIZE) && (randomize_va_space > 1)) {
                current->mm->brk = current->mm->start_brk =
                        arch_randomize_brk(current->mm);
+#ifdef CONFIG_COMPAT_BRK
+               current->brk_randomized = 1;
+#endif
+       }
 #endif
 
        if (current->personality & MMAP_PAGE_ZERO) {
@@ -1838,6 +1846,34 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma,
        return gate_vma;
 }
 
+static void fill_extnum_info(struct elfhdr *elf, struct elf_shdr *shdr4extnum,
+                            elf_addr_t e_shoff, int segs)
+{
+       elf->e_shoff = e_shoff;
+       elf->e_shentsize = sizeof(*shdr4extnum);
+       elf->e_shnum = 1;
+       elf->e_shstrndx = SHN_UNDEF;
+
+       memset(shdr4extnum, 0, sizeof(*shdr4extnum));
+
+       shdr4extnum->sh_type = SHT_NULL;
+       shdr4extnum->sh_size = elf->e_shnum;
+       shdr4extnum->sh_link = elf->e_shstrndx;
+       shdr4extnum->sh_info = segs;
+}
+
+static size_t elf_core_vma_data_size(struct vm_area_struct *gate_vma,
+                                    unsigned long mm_flags)
+{
+       struct vm_area_struct *vma;
+       size_t size = 0;
+
+       for (vma = first_vma(current, gate_vma); vma != NULL;
+            vma = next_vma(vma, gate_vma))
+               size += vma_dump_size(vma, mm_flags);
+       return size;
+}
+
 /*
  * Actual dumper
  *
@@ -1854,8 +1890,11 @@ static int elf_core_dump(struct coredump_params *cprm)
        struct vm_area_struct *vma, *gate_vma;
        struct elfhdr *elf = NULL;
        loff_t offset = 0, dataoff, foffset;
-       unsigned long mm_flags;
        struct elf_note_info info;
+       struct elf_phdr *phdr4note = NULL;
+       struct elf_shdr *shdr4extnum = NULL;
+       Elf_Half e_phnum;
+       elf_addr_t e_shoff;
 
        /*
         * We no longer stop all VM operations.
@@ -1884,12 +1923,19 @@ static int elf_core_dump(struct coredump_params *cprm)
        if (gate_vma != NULL)
                segs++;
 
+       /* for notes section */
+       segs++;
+
+       /* If segs > PN_XNUM(0xffff), then e_phnum overflows. To avoid
+        * this, kernel supports extended numbering. Have a look at
+        * include/linux/elf.h for further information. */
+       e_phnum = segs > PN_XNUM ? PN_XNUM : segs;
+
        /*
         * Collect all the non-memory information about the process for the
         * notes.  This also sets up the file header.
         */
-       if (!fill_note_info(elf, segs + 1, /* including notes section */
-                           &info, cprm->signr, cprm->regs))
+       if (!fill_note_info(elf, e_phnum, &info, cprm->signr, cprm->regs))
                goto cleanup;
 
        has_dumped = 1;
@@ -1898,38 +1944,47 @@ static int elf_core_dump(struct coredump_params *cprm)
        fs = get_fs();
        set_fs(KERNEL_DS);
 
-       size += sizeof(*elf);
-       if (size > cprm->limit || !dump_write(cprm->file, elf, sizeof(*elf)))
-               goto end_coredump;
-
        offset += sizeof(*elf);                         /* Elf header */
-       offset += (segs + 1) * sizeof(struct elf_phdr); /* Program headers */
+       offset += segs * sizeof(struct elf_phdr);       /* Program headers */
        foffset = offset;
 
        /* Write notes phdr entry */
        {
-               struct elf_phdr phdr;
                size_t sz = get_note_info_size(&info);
 
                sz += elf_coredump_extra_notes_size();
 
-               fill_elf_note_phdr(&phdr, sz, offset);
+               phdr4note = kmalloc(sizeof(*phdr4note), GFP_KERNEL);
+               if (!phdr4note)
+                       goto end_coredump;
+
+               fill_elf_note_phdr(phdr4note, sz, offset);
                offset += sz;
+       }
 
-               size += sizeof(phdr);
-               if (size > cprm->limit
-                   || !dump_write(cprm->file, &phdr, sizeof(phdr)))
+       dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
+
+       offset += elf_core_vma_data_size(gate_vma, cprm->mm_flags);
+       offset += elf_core_extra_data_size();
+       e_shoff = offset;
+
+       if (e_phnum == PN_XNUM) {
+               shdr4extnum = kmalloc(sizeof(*shdr4extnum), GFP_KERNEL);
+               if (!shdr4extnum)
                        goto end_coredump;
+               fill_extnum_info(elf, shdr4extnum, e_shoff, segs);
        }
 
-       dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
+       offset = dataoff;
 
-       /*
-        * We must use the same mm->flags while dumping core to avoid
-        * inconsistency between the program headers and bodies, otherwise an
-        * unusable core file can be generated.
-        */
-       mm_flags = current->mm->flags;
+       size += sizeof(*elf);
+       if (size > cprm->limit || !dump_write(cprm->file, elf, sizeof(*elf)))
+               goto end_coredump;
+
+       size += sizeof(*phdr4note);
+       if (size > cprm->limit
+           || !dump_write(cprm->file, phdr4note, sizeof(*phdr4note)))
+               goto end_coredump;
 
        /* Write program headers for segments dump */
        for (vma = first_vma(current, gate_vma); vma != NULL;
@@ -1940,7 +1995,7 @@ static int elf_core_dump(struct coredump_params *cprm)
                phdr.p_offset = offset;
                phdr.p_vaddr = vma->vm_start;
                phdr.p_paddr = 0;
-               phdr.p_filesz = vma_dump_size(vma, mm_flags);
+               phdr.p_filesz = vma_dump_size(vma, cprm->mm_flags);
                phdr.p_memsz = vma->vm_end - vma->vm_start;
                offset += phdr.p_filesz;
                phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0;
@@ -1975,7 +2030,7 @@ static int elf_core_dump(struct coredump_params *cprm)
                unsigned long addr;
                unsigned long end;
 
-               end = vma->vm_start + vma_dump_size(vma, mm_flags);
+               end = vma->vm_start + vma_dump_size(vma, cprm->mm_flags);
 
                for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) {
                        struct page *page;
@@ -1999,11 +2054,21 @@ static int elf_core_dump(struct coredump_params *cprm)
        if (!elf_core_write_extra_data(cprm->file, &size, cprm->limit))
                goto end_coredump;
 
+       if (e_phnum == PN_XNUM) {
+               size += sizeof(*shdr4extnum);
+               if (size > cprm->limit
+                   || !dump_write(cprm->file, shdr4extnum,
+                                  sizeof(*shdr4extnum)))
+                       goto end_coredump;
+       }
+
 end_coredump:
        set_fs(fs);
 
 cleanup:
        free_note_info(&info);
+       kfree(shdr4extnum);
+       kfree(phdr4note);
        kfree(elf);
 out:
        return has_dumped;