udf: decrement correct link count in udf_rmdir
[safe/jmp/linux-2.6] / fs / binfmt_elf.c
index bb43da5..67d9b31 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>
 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);
-extern int dump_fpu (struct pt_regs *, elf_fpregset_t *);
-
-#ifndef elf_addr_t
-#define elf_addr_t unsigned long
-#endif
 
 /*
  * If we don't support core dumping, then supply a NULL so we
@@ -81,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)
@@ -244,8 +240,9 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
        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;
@@ -255,7 +252,8 @@ 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++);
+               if (__put_user((elf_addr_t)p, argv++))
+                       return -EFAULT;
                len = strnlen_user((void __user *)p, PAGE_SIZE*MAX_ARG_PAGES);
                if (!len || len > PAGE_SIZE*MAX_ARG_PAGES)
                        return 0;
@@ -266,7 +264,8 @@ 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++);
+               if (__put_user((elf_addr_t)p, envp++))
+                       return -EFAULT;
                len = strnlen_user((void __user *)p, PAGE_SIZE*MAX_ARG_PAGES);
                if (!len || len > PAGE_SIZE*MAX_ARG_PAGES)
                        return 0;
@@ -508,7 +507,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)
@@ -546,7 +545,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;
@@ -684,6 +683,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) {
@@ -709,7 +717,6 @@ 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) {
@@ -1152,11 +1159,23 @@ static int dump_write(struct file *file, const void *addr, int nr)
 
 static int dump_seek(struct file *file, loff_t off)
 {
-       if (file->f_op->llseek) {
-               if (file->f_op->llseek(file, off, 0) != off)
+       if (file->f_op->llseek && file->f_op->llseek != no_llseek) {
+               if (file->f_op->llseek(file, off, SEEK_CUR) < 0)
+                       return 0;
+       } else {
+               char *buf = (char *)get_zeroed_page(GFP_KERNEL);
+               if (!buf)
                        return 0;
-       } else
-               file->f_pos = off;
+               while (off > 0) {
+                       unsigned long n = off;
+                       if (n > PAGE_SIZE)
+                               n = PAGE_SIZE;
+                       if (!dump_write(file, buf, n))
+                               return 0;
+                       off -= n;
+               }
+               free_page((unsigned long)buf);
+       }
        return 1;
 }
 
@@ -1169,13 +1188,17 @@ static int dump_seek(struct file *file, loff_t off)
  */
 static int maydump(struct vm_area_struct *vma)
 {
+       /* The vma can be set up to tell us the answer directly.  */
+       if (vma->vm_flags & VM_ALWAYSDUMP)
+               return 1;
+
        /* 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;
+               return vma->vm_file->f_path.dentry->d_inode->i_nlink == 0;
 
        /* If it hasn't been written to, don't write it out */
        if (!vma->anon_vma)
@@ -1204,30 +1227,35 @@ static int notesize(struct memelfnote *en)
        return sz;
 }
 
-#define DUMP_WRITE(addr, nr)   \
-       do { if (!dump_write(file, (addr), (nr))) return 0; } while(0)
-#define DUMP_SEEK(off) \
-       do { if (!dump_seek(file, (off))) return 0; } while(0)
+#define DUMP_WRITE(addr, nr, foffset)  \
+       do { if (!dump_write(file, (addr), (nr))) return 0; *foffset += (nr); } while(0)
 
-static int writenote(struct memelfnote *men, struct file *file)
+static int alignfile(struct file *file, loff_t *foffset)
 {
-       struct elf_note en;
+       static const char buf[4] = { 0, };
+       DUMP_WRITE(buf, roundup(*foffset, 4) - *foffset, foffset);
+       return 1;
+}
 
+static int writenote(struct memelfnote *men, struct file *file,
+                       loff_t *foffset)
+{
+       struct elf_note en;
        en.n_namesz = strlen(men->name) + 1;
        en.n_descsz = men->datasz;
        en.n_type = men->type;
 
-       DUMP_WRITE(&en, sizeof(en));
-       DUMP_WRITE(men->name, en.n_namesz);
-       /* XXX - cast from long long to long to avoid need for libgcc.a */
-       DUMP_SEEK(roundup((unsigned long)file->f_pos, 4));      /* XXX */
-       DUMP_WRITE(men->data, men->datasz);
-       DUMP_SEEK(roundup((unsigned long)file->f_pos, 4));      /* XXX */
+       DUMP_WRITE(&en, sizeof(en), foffset);
+       DUMP_WRITE(men->name, en.n_namesz, foffset);
+       if (!alignfile(file, foffset))
+               return 0;
+       DUMP_WRITE(men->data, men->datasz, foffset);
+       if (!alignfile(file, foffset))
+               return 0;
 
        return 1;
 }
 #undef DUMP_WRITE
-#undef DUMP_SEEK
 
 #define DUMP_WRITE(addr, nr)   \
        if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
@@ -1297,7 +1325,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
@@ -1343,7 +1371,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;
@@ -1410,6 +1438,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
  *
@@ -1425,9 +1479,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;
+       loff_t offset = 0, dataoff, foffset;
        unsigned long limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
        int numnote;
        struct memelfnote *notes = NULL;
@@ -1479,20 +1533,19 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
 
        if (signr) {
                struct elf_thread_status *tmp;
-               read_lock(&tasklist_lock);
+               rcu_read_lock();
                do_each_thread(g,p)
                        if (current->mm == p->mm && current != p) {
                                tmp = kzalloc(sizeof(*tmp), GFP_ATOMIC);
                                if (!tmp) {
-                                       read_unlock(&tasklist_lock);
+                                       rcu_read_unlock();
                                        goto cleanup;
                                }
-                               INIT_LIST_HEAD(&tmp->list);
                                tmp->thread = p;
                                list_add(&tmp->list, &thread_list);
                        }
                while_each_thread(g,p);
-               read_unlock(&tasklist_lock);
+               rcu_read_unlock();
                list_for_each(t, &thread_list) {
                        struct elf_thread_status *tmp;
                        int sz;
@@ -1512,6 +1565,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 */
 
@@ -1554,7 +1611,8 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
 
        DUMP_WRITE(elf, sizeof(*elf));
        offset += sizeof(*elf);                         /* Elf header */
-       offset += (segs+1) * sizeof(struct elf_phdr);   /* Program headers */
+       offset += (segs + 1) * sizeof(struct elf_phdr); /* Program headers */
+       foffset = offset;
 
        /* Write notes phdr entry */
        {
@@ -1566,16 +1624,20 @@ 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
+
                fill_elf_note_phdr(&phdr, sz, offset);
                offset += sz;
                DUMP_WRITE(&phdr, sizeof(phdr));
        }
 
-       /* Page-align dumped data */
        dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
 
        /* 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;
 
@@ -1604,22 +1666,28 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
 
        /* write out the notes section */
        for (i = 0; i < numnote; i++)
-               if (!writenote(notes + i, file))
+               if (!writenote(notes + i, file, &foffset))
                        goto end_coredump;
 
+#ifdef ELF_CORE_WRITE_EXTRA_NOTES
+       ELF_CORE_WRITE_EXTRA_NOTES;
+#endif
+
        /* write out the thread status notes section */
        list_for_each(t, &thread_list) {
                struct elf_thread_status *tmp =
                                list_entry(t, struct elf_thread_status, list);
 
                for (i = 0; i < tmp->num_notes; i++)
-                       if (!writenote(&tmp->notes[i], file))
+                       if (!writenote(&tmp->notes[i], file, &foffset))
                                goto end_coredump;
        }
-       DUMP_SEEK(dataoff);
 
-       for (vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) {
+       /* Align to page */
+       DUMP_SEEK(dataoff - foffset);
+
+       for (vma = first_vma(current, gate_vma); vma != NULL;
+                       vma = next_vma(vma, gate_vma)) {
                unsigned long addr;
 
                if (!maydump(vma))
@@ -1633,10 +1701,13 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
 
                        if (get_user_pages(current, current->mm, addr, 1, 0, 1,
                                                &page, &vma) <= 0) {
-                               DUMP_SEEK(file->f_pos + PAGE_SIZE);
+                               DUMP_SEEK(PAGE_SIZE);
                        } else {
                                if (page == ZERO_PAGE(addr)) {
-                                       DUMP_SEEK(file->f_pos + PAGE_SIZE);
+                                       if (!dump_seek(file, PAGE_SIZE)) {
+                                               page_cache_release(page);
+                                               goto end_coredump;
+                                       }
                                } else {
                                        void *kaddr;
                                        flush_cache_page(vma, addr,
@@ -1660,13 +1731,6 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
        ELF_CORE_WRITE_EXTRA_DATA;
 #endif
 
-       if (file->f_pos != offset) {
-               /* Sanity check */
-               printk(KERN_WARNING
-                      "elf_core_dump: file->f_pos (%Ld) != offset (%Ld)\n",
-                      file->f_pos, offset);
-       }
-
 end_coredump:
        set_fs(fs);