This commit was generated by cvs2svn to compensate for changes in r1129,
[linux-2.6.git] / fs / binfmt_elf.c
index 6f21f25..94d8435 100644 (file)
 #include <linux/syscalls.h>
 #include <linux/random.h>
 #include <linux/vs_memory.h>
+#include <linux/vs_cvirt.h>
 
 #include <asm/uaccess.h>
 #include <asm/param.h>
 #include <asm/page.h>
-#include <asm/pgalloc.h>
 
 #include <linux/elf.h>
 
 static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs);
 static int load_elf_library(struct file*);
-static unsigned long elf_map (struct file *, unsigned long, struct elf_phdr *, int, int, unsigned long);
+static unsigned long elf_map (struct file *, unsigned long, struct elf_phdr *, int, int);
 extern int dump_fpu (struct pt_regs *, elf_fpregset_t *);
 
 #ifndef elf_addr_t
@@ -60,7 +60,7 @@ extern int dump_fpu (struct pt_regs *, elf_fpregset_t *);
  * If we don't support core dumping, then supply a NULL so we
  * don't even try.
  */
-#ifdef USE_ELF_CORE_DUMP
+#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE)
 static int elf_core_dump(long signr, struct pt_regs * regs, struct file * file);
 #else
 #define elf_core_dump  NULL
@@ -88,7 +88,7 @@ static struct linux_binfmt elf_format = {
                .min_coredump   = ELF_EXEC_PAGESIZE
 };
 
-#define BAD_ADDR(x)    ((unsigned long)(x) > TASK_SIZE)
+#define BAD_ADDR(x)    ((unsigned long)(x) >= TASK_SIZE)
 
 static int set_brk(unsigned long start, unsigned long end)
 {
@@ -287,59 +287,26 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr * exec,
 #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)
+                       struct elf_phdr *eppnt, int prot, int type)
 {
        unsigned long map_addr;
-       unsigned long size = eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr);
-       unsigned long off = eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr);
-
-       addr = ELF_PAGESTART(addr);
-       size = ELF_PAGEALIGN(size);
+       unsigned long pageoffset = ELF_PAGEOFFSET(eppnt->p_vaddr);
 
        down_write(&current->mm->mmap_sem);
-
-       /*
-        * total_size is the size of the ELF (interpreter) image.
-        * The _first_ mmap needs to know the full size, otherwise
-        * randomization might put this image into an overlapping
-        * position with the ELF binary image. (since size < total_size)
-        * So we first map the 'big' image - and unmap the remainder at
-        * the end. (which unmap is needed for ELF images with holes.)
-        */
-       if (total_size) {
-               total_size = ELF_PAGEALIGN(total_size);
-               map_addr = do_mmap(filep, addr, total_size, prot, type, off);
-               if (!BAD_ADDR(map_addr))
-                       do_munmap(current->mm, map_addr+size, total_size-size);
-       } else
-               map_addr = do_mmap(filep, addr, size, prot, type, off);
-               
+       /* mmap() will return -EINVAL if given a zero size, but a
+        * segment with zero filesize is perfectly valid */
+       if (eppnt->p_filesz + pageoffset)
+               map_addr = do_mmap(filep, ELF_PAGESTART(addr),
+                                  eppnt->p_filesz + pageoffset, prot, type,
+                                  eppnt->p_offset - pageoffset);
+       else
+               map_addr = ELF_PAGESTART(addr);
        up_write(&current->mm->mmap_sem);
-
-       return map_addr;
+       return(map_addr);
 }
 
 #endif /* !elf_map */
 
-static inline unsigned long total_mapping_size(struct elf_phdr *cmds, int nr)
-{
-       int i, first_idx = -1, last_idx = -1;
-
-       for (i = 0; i < nr; i++)
-               if (cmds[i].p_type == PT_LOAD) {
-                       last_idx = i;
-                       if (first_idx == -1)
-                               first_idx = i;
-               }
-
-       if (first_idx == -1)
-               return 0;
-
-       return cmds[last_idx].p_vaddr + cmds[last_idx].p_memsz -
-                               ELF_PAGESTART(cmds[first_idx].p_vaddr);
-}
-
 /* This is much more generalized than the library routine read function,
    so we keep this separate.  Technically the library read function
    is only provided so that we can read a.out libraries that have
@@ -347,8 +314,7 @@ static inline unsigned long total_mapping_size(struct elf_phdr *cmds, int nr)
 
 static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
                                     struct file * interpreter,
-                                    unsigned long *interp_load_addr,
-                                    unsigned long no_base)
+                                    unsigned long *interp_load_addr)
 {
        struct elf_phdr *elf_phdata;
        struct elf_phdr *eppnt;
@@ -356,7 +322,6 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
        int load_addr_set = 0;
        unsigned long last_bss = 0, elf_bss = 0;
        unsigned long error = ~0UL;
-       unsigned long total_size;
        int retval, i, size;
 
        /* First of all, some simple consistency checks */
@@ -395,10 +360,6 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
                goto out_close;
        }
 
-       total_size = total_mapping_size(elf_phdata, interp_elf_ex->e_phnum);
-       if (!total_size)
-               goto out_close;
-
        eppnt = elf_phdata;
        for (i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) {
          if (eppnt->p_type == PT_LOAD) {
@@ -413,11 +374,8 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
            vaddr = eppnt->p_vaddr;
            if (interp_elf_ex->e_type == ET_EXEC || load_addr_set)
                elf_type |= MAP_FIXED;
-           else if (no_base && interp_elf_ex->e_type == ET_DYN)
-               load_addr = -vaddr;
 
-           map_addr = elf_map(interpreter, load_addr + vaddr, eppnt, elf_prot, elf_type, total_size);
-           total_size = 0;
+           map_addr = elf_map(interpreter, load_addr + vaddr, eppnt, elf_prot, elf_type);
            error = map_addr;
            if (BAD_ADDR(map_addr))
                goto out_close;
@@ -433,7 +391,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex,
             * <= p_memsize so it is only necessary to check p_memsz.
             */
            k = load_addr + eppnt->p_vaddr;
-           if (k > TASK_SIZE || eppnt->p_filesz > eppnt->p_memsz ||
+           if (BAD_ADDR(k) || eppnt->p_filesz > eppnt->p_memsz ||
                eppnt->p_memsz > TASK_SIZE || TASK_SIZE - eppnt->p_memsz < k) {
                error = -ENOMEM;
                goto out_close;
@@ -577,7 +535,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
        unsigned long reloc_func_desc = 0;
        char passed_fileno[6];
        struct files_struct *files;
-       int have_pt_gnu_stack, executable_stack;
+       int have_pt_gnu_stack, executable_stack = EXSTACK_DEFAULT;
        unsigned long def_flags = 0;
        struct {
                struct elfhdr elf_ex;
@@ -666,7 +624,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                                goto out_free_file;
 
                        retval = -ENOMEM;
-                       elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz,
+                       elf_interpreter = kmalloc(elf_ppnt->p_filesz,
                                                           GFP_KERNEL);
                        if (!elf_interpreter)
                                goto out_free_file;
@@ -733,8 +691,6 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
        }
 
        elf_ppnt = elf_phdata;
-       executable_stack = EXSTACK_DEFAULT;
-
        for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
                if (elf_ppnt->p_type == PT_GNU_STACK) {
                        if (elf_ppnt->p_flags & PF_X)
@@ -745,11 +701,6 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                }
        have_pt_gnu_stack = (i < loc->elf_ex.e_phnum);
 
-       if (current->personality == PER_LINUX && exec_shield == 2) {
-               executable_stack = EXSTACK_DISABLE_X;
-               current->flags |= PF_RANDOMIZE;
-       }
-
        /* Some simple consistency checks for the interpreter */
        if (elf_interpreter) {
                interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
@@ -803,15 +754,6 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
        if (retval)
                goto out_free_dentry;
 
-#ifdef __i386__
-       /*
-        * Turn off the CS limit completely if exec-shield disabled or
-        * NX active:
-        */
-       if (!exec_shield || executable_stack != EXSTACK_DISABLE_X || nx_enabled)
-               arch_add_exec_range(current->mm, -1);
-#endif
-
        /* Discard our unneeded old files struct */
        if (files) {
                steal_locks(files);
@@ -830,8 +772,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
        /* Do this immediately, since STACK_TOP as used in setup_arg_pages
           may depend on the personality.  */
        SET_PERSONALITY(loc->elf_ex, ibcs2_interpreter);
-       if (exec_shield != 2 &&
-                       elf_read_implies_exec(loc->elf_ex, executable_stack))
+       if (elf_read_implies_exec(loc->elf_ex, executable_stack))
                current->personality |= READ_IMPLIES_EXEC;
 
        if ( !(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
@@ -840,8 +781,8 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
 
        /* Do this so that we can load the interpreter, if need be.  We will
           change some of these later */
-       set_mm_counter(current->mm, rss, 0);
        current->mm->free_area_cache = current->mm->mmap_base;
+       current->mm->cached_hole_size = 0;
        retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP),
                                 executable_stack);
        if (retval < 0) {
@@ -851,10 +792,10 @@ 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
-          the correct location in memory.
-        */
+          the correct location in memory.  At this point, we assume that
+          the image should be loaded at fixed address, not at a variable
+          address. */
 
        for(i = 0, elf_ppnt = elf_phdata; i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
                int elf_prot = 0, elf_flags;
@@ -898,16 +839,16 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                elf_flags = MAP_PRIVATE|MAP_DENYWRITE|MAP_EXECUTABLE;
 
                vaddr = elf_ppnt->p_vaddr;
-               if (loc->elf_ex.e_type == ET_EXEC || load_addr_set)
+               if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
                        elf_flags |= MAP_FIXED;
-               else if (loc->elf_ex.e_type == ET_DYN)
-#ifdef __i386__
-                       load_bias = 0;
-#else
+               } else if (loc->elf_ex.e_type == ET_DYN) {
+                       /* Try and get dynamic programs out of the way of the 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.  */
                        load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
-#endif
+               }
 
-               error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, elf_prot, elf_flags, 0);
+               error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, elf_prot, elf_flags);
                if (BAD_ADDR(error)) {
                        send_sig(SIGKILL, current, 0);
                        goto out_free_dentry;
@@ -932,7 +873,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                 * allowed task size. Note that p_filesz must always be
                 * <= p_memsz so it is only necessary to check p_memsz.
                 */
-               if (k > TASK_SIZE || elf_ppnt->p_filesz > elf_ppnt->p_memsz ||
+               if (BAD_ADDR(k) || elf_ppnt->p_filesz > elf_ppnt->p_memsz ||
                    elf_ppnt->p_memsz > TASK_SIZE ||
                    TASK_SIZE - elf_ppnt->p_memsz < k) {
                        /* set_brk can never work.  Avoid overflows.  */
@@ -971,7 +912,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                send_sig(SIGKILL, current, 0);
                goto out_free_dentry;
        }
-       if (padzero(elf_bss)) {
+       if (likely(elf_bss != elf_brk) && unlikely(padzero(elf_bss))) {
                send_sig(SIGSEGV, current, 0);
                retval = -EFAULT; /* Nobody gets to see this, but.. */
                goto out_free_dentry;
@@ -984,13 +925,11 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                else
                        elf_entry = load_elf_interp(&loc->interp_elf_ex,
                                                    interpreter,
-                                                   &interp_load_addr,
-                                                   load_bias);
+                                                   &interp_load_addr);
                if (BAD_ADDR(elf_entry)) {
-                       printk(KERN_ERR "Unable to load interpreter %.128s\n",
-                               elf_interpreter);
                        force_sig(SIGSEGV, current);
-                       retval = -ENOEXEC; /* Nobody gets to see this, but.. */
+                       retval = IS_ERR((void *)elf_entry) ?
+                                       (int)elf_entry : -EINVAL;
                        goto out_free_dentry;
                }
                reloc_func_desc = interp_load_addr;
@@ -1000,8 +939,15 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
                kfree(elf_interpreter);
        } else {
                elf_entry = loc->elf_ex.e_entry;
+               if (BAD_ADDR(elf_entry)) {
+                       force_sig(SIGSEGV, current);
+                       retval = -EINVAL;
+                       goto out_free_dentry;
+               }
        }
 
+       kfree(elf_phdata);
+
        if (interpreter_type != INTERPRETER_AOUT)
                sys_close(elf_exec_fileno);
 
@@ -1011,12 +957,10 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
        retval = arch_setup_additional_pages(bprm, executable_stack);
        if (retval < 0) {
                send_sig(SIGKILL, current, 0);
-               goto out_free_fh;
+               goto out;
        }
 #endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */
 
-       kfree(elf_phdata);
-
        compute_creds(bprm);
        current->flags &= ~PF_FORKNOEXEC;
        create_elf_tables(bprm, &loc->elf_ex, (interpreter_type == INTERPRETER_AOUT),
@@ -1030,10 +974,6 @@ static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs)
        current->mm->end_data = end_data;
        current->mm->start_stack = bprm->p;
 
-#ifdef __HAVE_ARCH_RANDOMIZE_BRK
-       if (current->flags & PF_RANDOMIZE)
-               randomize_brk(elf_brk);
-#endif
        if (current->personality & MMAP_PAGE_ZERO) {
                /* Why this, you ask???  Well SVr4 maps page 0 as read-only,
                   and some applications "depend" upon this behavior.
@@ -1078,8 +1018,7 @@ out_free_dentry:
        if (interpreter)
                fput(interpreter);
 out_free_interp:
-       if (elf_interpreter)
-               kfree(elf_interpreter);
+       kfree(elf_interpreter);
 out_free_file:
        sys_close(elf_exec_fileno);
 out_free_fh:
@@ -1180,7 +1119,7 @@ out:
  * Note that some platforms still use traditional core dumps and not
  * the ELF core dump.  Each platform can select it as appropriate.
  */
-#ifdef USE_ELF_CORE_DUMP
+#if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE)
 
 /*
  * ELF core dumper
@@ -1220,9 +1159,6 @@ static int maydump(struct vm_area_struct *vma)
        if (vma->vm_flags & (VM_IO | VM_RESERVED))
                return 0;
 
-       if (vma->vm_flags & VM_DONTEXPAND) /* Kludge for vDSO.  */
-               return 1;
-
        /* Dump shared memory only if mapped from an anonymous file.  */
        if (vma->vm_flags & VM_SHARED)
                return vma->vm_file->f_dentry->d_inode->i_nlink == 0;
@@ -1288,7 +1224,7 @@ static int writenote(struct memelfnote *men, struct file *file)
        if (!dump_seek(file, (off))) \
                goto end_coredump;
 
-static inline void fill_elf_header(struct elfhdr *elf, int segs)
+static void fill_elf_header(struct elfhdr *elf, int segs)
 {
        memcpy(elf->e_ident, ELFMAG, SELFMAG);
        elf->e_ident[EI_CLASS] = ELF_CLASS;
@@ -1313,7 +1249,7 @@ static inline void fill_elf_header(struct elfhdr *elf, int segs)
        return;
 }
 
-static inline void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, off_t offset)
+static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, off_t offset)
 {
        phdr->p_type = PT_NOTE;
        phdr->p_offset = offset;
@@ -1577,9 +1513,7 @@ static int elf_core_dump(long signr, struct pt_regs * regs, struct file * file)
        fill_psinfo(psinfo, current->group_leader, current->mm);
        fill_note(notes +1, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo);
        
-       fill_note(notes +2, "CORE", NT_TASKSTRUCT, sizeof(*current), current);
-  
-       numnote = 3;
+       numnote = 2;
 
        auxv = (elf_addr_t *) current->mm->saved_auxv;
 
@@ -1706,17 +1640,17 @@ static int elf_core_dump(long signr, struct pt_regs * regs, struct file * file)
        ELF_CORE_WRITE_EXTRA_DATA;
 #endif
 
-       if ((off_t) file->f_pos != offset) {
+       if ((off_t)file->f_pos != offset) {
                /* Sanity check */
-               printk("elf_core_dump: file->f_pos (%ld) != offset (%ld)\n",
-                      (off_t) file->f_pos, offset);
+               printk(KERN_WARNING "elf_core_dump: file->f_pos (%ld) != offset (%ld)\n",
+                      (off_t)file->f_pos, offset);
        }
 
 end_coredump:
        set_fs(fs);
 
 cleanup:
-       while(!list_empty(&thread_list)) {
+       while (!list_empty(&thread_list)) {
                struct list_head *tmp = thread_list.next;
                list_del(tmp);
                kfree(list_entry(tmp, struct elf_thread_status, list));