9p: soften invalidation in loose_mode
[safe/jmp/linux-2.6] / fs / binfmt_elf.c
index b2efbae..6e2f3b8 100644 (file)
@@ -31,7 +31,6 @@
 #include <linux/init.h>
 #include <linux/highuid.h>
 #include <linux/smp.h>
-#include <linux/smp_lock.h>
 #include <linux/compiler.h>
 #include <linux/highmem.h>
 #include <linux/pagemap.h>
@@ -39,6 +38,7 @@
 #include <linux/syscalls.h>
 #include <linux/random.h>
 #include <linux/elf.h>
+#include <linux/utsname.h>
 #include <asm/uaccess.h>
 #include <asm/param.h>
 #include <asm/page.h>
@@ -47,16 +47,12 @@ 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);
 
-#ifndef elf_addr_t
-#define elf_addr_t unsigned long
-#endif
-
 /*
  * 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);
+static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
 #else
 #define elf_core_dump  NULL
 #endif
@@ -80,7 +76,8 @@ static struct linux_binfmt elf_format = {
                .load_binary    = load_elf_binary,
                .load_shlib     = load_elf_library,
                .core_dump      = elf_core_dump,
-               .min_coredump   = ELF_EXEC_PAGESIZE
+               .min_coredump   = ELF_EXEC_PAGESIZE,
+               .hasvdso        = 1
 };
 
 #define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE)
@@ -151,6 +148,15 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
        elf_addr_t *elf_info;
        int ei_index = 0;
        struct task_struct *tsk = current;
+       struct vm_area_struct *vma;
+
+       /*
+        * In some cases (e.g. Hyper-Threading), we want to avoid L1
+        * evictions by the processes running on the same package. One
+        * thing we can do is to shuffle the initial stack for them.
+        */
+
+       p = arch_align_stack(p);
 
        /*
         * If this architecture has a platform capability string, copy it
@@ -162,14 +168,6 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
        if (k_platform) {
                size_t len = strlen(k_platform) + 1;
 
-               /*
-                * In some cases (e.g. Hyper-Threading), we want to avoid L1
-                * evictions by the processes running on the same package. One
-                * thing we can do is to shuffle the initial stack for them.
-                */
-
-               p = arch_align_stack(p);
-
                u_platform = (elf_addr_t __user *)STACK_ALLOC(p, len);
                if (__copy_to_user(u_platform, k_platform, len))
                        return -EFAULT;
@@ -177,6 +175,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
 
        /* Create the ELF interpreter info */
        elf_info = (elf_addr_t *)current->mm->saved_auxv;
+       /* update AT_VECTOR_SIZE_BASE if the number of NEW_AUX_ENT() changes */
 #define NEW_AUX_ENT(id, val) \
        do { \
                elf_info[ei_index++] = id; \
@@ -187,6 +186,8 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
        /* 
         * ARCH_DLINFO must come first so PPC can do its special alignment of
         * AUXV.
+        * update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT() in
+        * ARCH_DLINFO changes
         */
        ARCH_DLINFO;
 #endif
@@ -237,14 +238,24 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
        sp = (elf_addr_t __user *)bprm->p;
 #endif
 
+
+       /*
+        * Grow the stack manually; some architectures have a limit on how
+        * far ahead a user-space access may be in order to grow the stack.
+        */
+       vma = find_extend_vma(current->mm, bprm->p);
+       if (!vma)
+               return -EFAULT;
+
        /* Now, let's put argc (and argv, envp if appropriate) on the stack */
        if (__put_user(argc, sp++))
                return -EFAULT;
        if (interp_aout) {
                argv = sp + 2;
                envp = argv + argc + 1;
-               __put_user((elf_addr_t)(unsigned long)argv, sp++);
-               __put_user((elf_addr_t)(unsigned long)envp, sp++);
+               if (__put_user((elf_addr_t)(unsigned long)argv, sp++) ||
+                   __put_user((elf_addr_t)(unsigned long)envp, sp++))
+                       return -EFAULT;
        } else {
                argv = sp;
                envp = argv + argc + 1;
@@ -254,9 +265,10 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
        p = current->mm->arg_end = current->mm->arg_start;
        while (argc-- > 0) {
                size_t len;
-               __put_user((elf_addr_t)p, argv++);
-               len = strnlen_user((void __user *)p, PAGE_SIZE*MAX_ARG_PAGES);
-               if (!len || len > PAGE_SIZE*MAX_ARG_PAGES)
+               if (__put_user((elf_addr_t)p, argv++))
+                       return -EFAULT;
+               len = strnlen_user((void __user *)p, MAX_ARG_STRLEN);
+               if (!len || len > MAX_ARG_STRLEN)
                        return 0;
                p += len;
        }
@@ -265,9 +277,10 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
        current->mm->arg_end = current->mm->env_start = p;
        while (envc-- > 0) {
                size_t len;
-               __put_user((elf_addr_t)p, envp++);
-               len = strnlen_user((void __user *)p, PAGE_SIZE*MAX_ARG_PAGES);
-               if (!len || len > PAGE_SIZE*MAX_ARG_PAGES)
+               if (__put_user((elf_addr_t)p, envp++))
+                       return -EFAULT;
+               len = strnlen_user((void __user *)p, MAX_ARG_STRLEN);
+               if (!len || len > MAX_ARG_STRLEN)
                        return 0;
                p += len;
        }
@@ -507,7 +520,7 @@ out:
 #define INTERPRETER_ELF 2
 
 #ifndef STACK_RND_MASK
-#define STACK_RND_MASK 0x7ff           /* with 4K pages 8MB of VA */
+#define STACK_RND_MASK (0x7ff >> (PAGE_SHIFT - 12))    /* 8MB of VA */
 #endif
 
 static unsigned long randomize_stack_top(unsigned long stack_top)
@@ -545,7 +558,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 = EXSTACK_DEFAULT;
+       int executable_stack = EXSTACK_DEFAULT;
        unsigned long def_flags = 0;
        struct {
                struct elfhdr elf_ex;
@@ -683,6 +696,15 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
                        retval = PTR_ERR(interpreter);
                        if (IS_ERR(interpreter))
                                goto out_free_interp;
+
+                       /*
+                        * If the binary is not readable then enforce
+                        * mm->dumpable = 0 regardless of the interpreter's
+                        * permissions.
+                        */
+                       if (file_permission(interpreter, MAY_READ) < 0)
+                               bprm->interp_flags |= BINPRM_FLAGS_ENFORCE_NONDUMP;
+
                        retval = kernel_read(interpreter, 0, bprm->buf,
                                             BINPRM_BUF_SIZE);
                        if (retval != BINPRM_BUF_SIZE) {
@@ -708,10 +730,10 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
                                executable_stack = EXSTACK_DISABLE_X;
                        break;
                }
-       have_pt_gnu_stack = (i < loc->elf_ex.e_phnum);
 
        /* Some simple consistency checks for the interpreter */
        if (elf_interpreter) {
+               static int warn;
                interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
 
                /* Now figure out which format our binary is */
@@ -723,6 +745,13 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
                if (memcmp(loc->interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
                        interpreter_type &= ~INTERPRETER_ELF;
 
+               if (interpreter_type == INTERPRETER_AOUT && warn < 10) {
+                       printk(KERN_WARNING "a.out ELF interpreter %s is "
+                               "deprecated and will not be supported "
+                               "after Linux 2.6.25\n", elf_interpreter);
+                       warn++;
+               }
+
                retval = -ELIBBAD;
                if (!interpreter_type)
                        goto out_free_dentry;
@@ -769,10 +798,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
        }
 
        /* OK, This is the point of no return */
-       current->mm->start_data = 0;
-       current->mm->end_data = 0;
-       current->mm->end_code = 0;
-       current->mm->mmap = NULL;
        current->flags &= ~PF_FORKNOEXEC;
        current->mm->def_flags = def_flags;
 
@@ -856,19 +881,15 @@ 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.  */
-                       if (current->flags & PF_RANDOMIZE)
-                               load_bias = randomize_range(0x10000,
-                                                           ELF_ET_DYN_BASE,
-                                                           0);
-                       else
-                               load_bias = ELF_ET_DYN_BASE;
-                       load_bias = ELF_PAGESTART(load_bias - vaddr);
+                       load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
                }
 
                error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
                                elf_prot, elf_flags);
                if (BAD_ADDR(error)) {
                        send_sig(SIGKILL, current, 0);
+                       retval = IS_ERR((void *)error) ?
+                               PTR_ERR((void*)error) : -EINVAL;
                        goto out_free_dentry;
                }
 
@@ -898,6 +919,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
                    TASK_SIZE - elf_ppnt->p_memsz < k) {
                        /* set_brk can never work. Avoid overflows. */
                        send_sig(SIGKILL, current, 0);
+                       retval = -EINVAL;
                        goto out_free_dentry;
                }
 
@@ -983,9 +1005,13 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
 
        compute_creds(bprm);
        current->flags &= ~PF_FORKNOEXEC;
-       create_elf_tables(bprm, &loc->elf_ex,
+       retval = create_elf_tables(bprm, &loc->elf_ex,
                          (interpreter_type == INTERPRETER_AOUT),
                          load_addr, interp_load_addr);
+       if (retval < 0) {
+               send_sig(SIGKILL, current, 0);
+               goto out;
+       }
        /* N.B. passed_fileno might not be initialized? */
        if (interpreter_type == INTERPRETER_AOUT)
                current->mm->arg_start += strlen(passed_fileno) + 1;
@@ -1178,27 +1204,68 @@ static int dump_seek(struct file *file, loff_t off)
 }
 
 /*
- * Decide whether a segment is worth dumping; default is yes to be
- * sure (missing info is worse than too much; etc).
- * Personally I'd include everything, and use the coredump limit...
- *
- * I think we should skip something. But I am not sure how. H.J.
+ * Decide what to dump of a segment, part, all or none.
  */
-static int maydump(struct vm_area_struct *vma)
+static unsigned long vma_dump_size(struct vm_area_struct *vma,
+                                  unsigned long mm_flags)
 {
+       /* The vma can be set up to tell us the answer directly.  */
+       if (vma->vm_flags & VM_ALWAYSDUMP)
+               goto whole;
+
        /* Do not dump I/O mapped devices or special mappings */
        if (vma->vm_flags & (VM_IO | VM_RESERVED))
                return 0;
 
-       /* 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;
+#define FILTER(type)   (mm_flags & (1UL << MMF_DUMP_##type))
+
+       /* By default, dump shared memory if mapped from an anonymous file. */
+       if (vma->vm_flags & VM_SHARED) {
+               if (vma->vm_file->f_path.dentry->d_inode->i_nlink == 0 ?
+                   FILTER(ANON_SHARED) : FILTER(MAPPED_SHARED))
+                       goto whole;
+               return 0;
+       }
 
-       /* If it hasn't been written to, don't write it out */
-       if (!vma->anon_vma)
+       /* Dump segments that have been written to.  */
+       if (vma->anon_vma && FILTER(ANON_PRIVATE))
+               goto whole;
+       if (vma->vm_file == NULL)
                return 0;
 
-       return 1;
+       if (FILTER(MAPPED_PRIVATE))
+               goto whole;
+
+       /*
+        * If this looks like the beginning of a DSO or executable mapping,
+        * check for an ELF header.  If we find one, dump the first page to
+        * aid in determining what was mapped here.
+        */
+       if (FILTER(ELF_HEADERS) && vma->vm_file != NULL && vma->vm_pgoff == 0) {
+               u32 __user *header = (u32 __user *) vma->vm_start;
+               u32 word;
+               /*
+                * Doing it this way gets the constant folded by GCC.
+                */
+               union {
+                       u32 cmp;
+                       char elfmag[SELFMAG];
+               } magic;
+               BUILD_BUG_ON(SELFMAG != sizeof word);
+               magic.elfmag[EI_MAG0] = ELFMAG0;
+               magic.elfmag[EI_MAG1] = ELFMAG1;
+               magic.elfmag[EI_MAG2] = ELFMAG2;
+               magic.elfmag[EI_MAG3] = ELFMAG3;
+               if (get_user(word, header) == 0 && word == magic.cmp)
+                       return PAGE_SIZE;
+       }
+
+#undef FILTER
+
+       return 0;
+
+whole:
+       return vma->vm_end - vma->vm_start;
 }
 
 /* An ELF note in memory */
@@ -1319,7 +1386,7 @@ static void fill_prstatus(struct elf_prstatus *prstatus,
        prstatus->pr_pid = p->pid;
        prstatus->pr_ppid = p->parent->pid;
        prstatus->pr_pgrp = process_group(p);
-       prstatus->pr_sid = p->signal->session;
+       prstatus->pr_sid = process_session(p);
        if (thread_group_leader(p)) {
                /*
                 * This is the record for the group leader.  Add in the
@@ -1365,7 +1432,7 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
        psinfo->pr_pid = p->pid;
        psinfo->pr_ppid = p->parent->pid;
        psinfo->pr_pgrp = process_group(p);
-       psinfo->pr_sid = p->signal->session;
+       psinfo->pr_sid = process_session(p);
 
        i = p->state ? ffz(~p->state) + 1 : 0;
        psinfo->pr_state = i;
@@ -1388,7 +1455,7 @@ struct elf_thread_status
        elf_fpregset_t fpu;             /* NT_PRFPREG */
        struct task_struct *thread;
 #ifdef ELF_CORE_COPY_XFPREGS
-       elf_fpxregset_t xfpu;           /* NT_PRXFPREG */
+       elf_fpxregset_t xfpu;           /* ELF_CORE_XFPREG_TYPE */
 #endif
        struct memelfnote notes[3];
        int num_notes;
@@ -1423,8 +1490,8 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t)
 
 #ifdef ELF_CORE_COPY_XFPREGS
        if (elf_core_copy_task_xfpregs(p, &t->xfpu)) {
-               fill_note(&t->notes[2], "LINUX", NT_PRXFPREG, sizeof(t->xfpu),
-                         &t->xfpu);
+               fill_note(&t->notes[2], "LINUX", ELF_CORE_XFPREG_TYPE,
+                         sizeof(t->xfpu), &t->xfpu);
                t->num_notes++;
                sz += notesize(&t->notes[2]);
        }
@@ -1432,6 +1499,32 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t)
        return sz;
 }
 
+static struct vm_area_struct *first_vma(struct task_struct *tsk,
+                                       struct vm_area_struct *gate_vma)
+{
+       struct vm_area_struct *ret = tsk->mm->mmap;
+
+       if (ret)
+               return ret;
+       return gate_vma;
+}
+/*
+ * Helper function for iterating across a vma list.  It ensures that the caller
+ * will visit `gate_vma' prior to terminating the search.
+ */
+static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma,
+                                       struct vm_area_struct *gate_vma)
+{
+       struct vm_area_struct *ret;
+
+       ret = this_vma->vm_next;
+       if (ret)
+               return ret;
+       if (this_vma == gate_vma)
+               return NULL;
+       return gate_vma;
+}
+
 /*
  * Actual dumper
  *
@@ -1439,7 +1532,7 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t)
  * 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)
+static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
 {
 #define        NUM_NOTES       6
        int has_dumped = 0;
@@ -1447,10 +1540,9 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
        int segs;
        size_t size = 0;
        int i;
-       struct vm_area_struct *vma;
+       struct vm_area_struct *vma, *gate_vma;
        struct elfhdr *elf = NULL;
        loff_t offset = 0, dataoff, foffset;
-       unsigned long limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
        int numnote;
        struct memelfnote *notes = NULL;
        struct elf_prstatus *prstatus = NULL;   /* NT_PRSTATUS */
@@ -1464,6 +1556,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
 #endif
        int thread_status_size = 0;
        elf_addr_t *auxv;
+       unsigned long mm_flags;
 
        /*
         * We no longer stop all VM operations.
@@ -1533,6 +1626,10 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
        segs += ELF_CORE_EXTRA_PHDRS;
 #endif
 
+       gate_vma = get_gate_vma(current);
+       if (gate_vma != NULL)
+               segs++;
+
        /* Set up header */
        fill_elf_header(elf, segs + 1); /* including notes section */
 
@@ -1567,7 +1664,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
 #ifdef ELF_CORE_COPY_XFPREGS
        if (elf_core_copy_task_xfpregs(current, xfpu))
                fill_note(notes + numnote++,
-                         "LINUX", NT_PRXFPREG, sizeof(*xfpu), xfpu);
+                         "LINUX", ELF_CORE_XFPREG_TYPE, sizeof(*xfpu), xfpu);
 #endif 
   
        fs = get_fs();
@@ -1588,9 +1685,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
                
                sz += thread_status_size;
 
-#ifdef ELF_CORE_WRITE_EXTRA_NOTES
-               sz += ELF_CORE_EXTRA_NOTES_SIZE;
-#endif
+               sz += elf_coredump_extra_notes_size();
 
                fill_elf_note_phdr(&phdr, sz, offset);
                offset += sz;
@@ -1599,19 +1694,24 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
 
        dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
 
+       /*
+        * 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;
+
        /* Write program headers for segments dump */
-       for (vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) {
+       for (vma = first_vma(current, gate_vma); vma != NULL;
+                       vma = next_vma(vma, gate_vma)) {
                struct elf_phdr phdr;
-               size_t sz;
-
-               sz = vma->vm_end - vma->vm_start;
 
                phdr.p_type = PT_LOAD;
                phdr.p_offset = offset;
                phdr.p_vaddr = vma->vm_start;
                phdr.p_paddr = 0;
-               phdr.p_filesz = maydump(vma) ? sz : 0;
-               phdr.p_memsz = sz;
+               phdr.p_filesz = vma_dump_size(vma, 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;
                if (vma->vm_flags & VM_WRITE)
@@ -1632,9 +1732,8 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
                if (!writenote(notes + i, file, &foffset))
                        goto end_coredump;
 
-#ifdef ELF_CORE_WRITE_EXTRA_NOTES
-       ELF_CORE_WRITE_EXTRA_NOTES;
-#endif
+       if (elf_coredump_extra_notes_write(file, &foffset))
+               goto end_coredump;
 
        /* write out the thread status notes section */
        list_for_each(t, &thread_list) {
@@ -1649,15 +1748,14 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
        /* Align to page */
        DUMP_SEEK(dataoff - foffset);
 
-       for (vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) {
+       for (vma = first_vma(current, gate_vma); vma != NULL;
+                       vma = next_vma(vma, gate_vma)) {
                unsigned long addr;
+               unsigned long end;
 
-               if (!maydump(vma))
-                       continue;
+               end = vma->vm_start + vma_dump_size(vma, mm_flags);
 
-               for (addr = vma->vm_start;
-                    addr < vma->vm_end;
-                    addr += PAGE_SIZE) {
+               for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) {
                        struct page *page;
                        struct vm_area_struct *vma;
 
@@ -1665,8 +1763,11 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
                                                &page, &vma) <= 0) {
                                DUMP_SEEK(PAGE_SIZE);
                        } else {
-                               if (page == ZERO_PAGE(addr)) {
-                                       DUMP_SEEK(PAGE_SIZE);
+                               if (page == ZERO_PAGE(0)) {
+                                       if (!dump_seek(file, PAGE_SIZE)) {
+                                               page_cache_release(page);
+                                               goto end_coredump;
+                                       }
                                } else {
                                        void *kaddr;
                                        flush_cache_page(vma, addr,