[PATCH] binfmt_elf: randomize PIE binaries (2nd try)
[sfrench/cifs-2.6.git] / fs / binfmt_elf.c
index 672a3b90bc556628874d5ab1494b654dc486a74b..b2efbaead6c3154fd0f12b364cdea9b42fa5f65d 100644 (file)
@@ -46,7 +46,6 @@
 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
@@ -515,7 +514,8 @@ static unsigned long randomize_stack_top(unsigned long stack_top)
 {
        unsigned int random_variable = 0;
 
-       if (current->flags & PF_RANDOMIZE) {
+       if ((current->flags & PF_RANDOMIZE) &&
+               !(current->personality & ADDR_NO_RANDOMIZE)) {
                random_variable = get_random_int() & STACK_RND_MASK;
                random_variable <<= PAGE_SHIFT;
        }
@@ -856,7 +856,13 @@ 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.  */
-                       load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
+                       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);
                }
 
                error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
@@ -1037,10 +1043,8 @@ out_free_interp:
 out_free_file:
        sys_close(elf_exec_fileno);
 out_free_fh:
-       if (files) {
-               put_files_struct(current->files);
-               current->files = files;
-       }
+       if (files)
+               reset_files_struct(current, files);
 out_free_ph:
        kfree(elf_phdata);
        goto out;
@@ -1153,11 +1157,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
-               file->f_pos = off;
+       } else {
+               char *buf = (char *)get_zeroed_page(GFP_KERNEL);
+               if (!buf)
+                       return 0;
+               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;
 }
 
@@ -1205,30 +1221,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))) \
@@ -1262,7 +1283,7 @@ static void fill_elf_header(struct elfhdr *elf, int segs)
        return;
 }
 
-static 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, loff_t offset)
 {
        phdr->p_type = PT_NOTE;
        phdr->p_offset = offset;
@@ -1428,7 +1449,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file)
        int i;
        struct vm_area_struct *vma;
        struct elfhdr *elf = NULL;
-       off_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;
@@ -1480,20 +1501,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;
@@ -1555,7 +1575,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 */
        {
@@ -1567,12 +1588,15 @@ 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 */
@@ -1605,20 +1629,25 @@ 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);
+
+       /* Align to page */
+       DUMP_SEEK(dataoff - foffset);
 
        for (vma = current->mm->mmap; vma != NULL; vma = vma->vm_next) {
                unsigned long addr;
@@ -1634,10 +1663,10 @@ 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);
+                                       DUMP_SEEK(PAGE_SIZE);
                                } else {
                                        void *kaddr;
                                        flush_cache_page(vma, addr,
@@ -1661,13 +1690,6 @@ 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) {
-               /* Sanity check */
-               printk(KERN_WARNING
-                      "elf_core_dump: file->f_pos (%ld) != offset (%ld)\n",
-                      (off_t)file->f_pos, offset);
-       }
-
 end_coredump:
        set_fs(fs);