Jesse Keating 7a32965
--- b/include/linux/sched.h
Jesse Keating 7a32965
+++ b/include/linux/sched.h
Jesse Keating 7a32965
@@ -397,6 +397,10 @@
Jesse Keating 7a32965
 extern unsigned long
Jesse Keating 7a32965
 arch_get_unmapped_area(struct file *, unsigned long, unsigned long,
Jesse Keating 7a32965
 		       unsigned long, unsigned long);
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+extern unsigned long
Jesse Keating 7a32965
+arch_get_unmapped_exec_area(struct file *, unsigned long, unsigned long,
Jesse Keating 7a32965
+		       unsigned long, unsigned long);
Jesse Keating 7a32965
 extern unsigned long
Jesse Keating 7a32965
 arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
Jesse Keating 7a32965
 			  unsigned long len, unsigned long pgoff,
Jesse Keating 7a32965
--- b/mm/mmap.c
Jesse Keating 7a32965
+++ b/mm/mmap.c
Jesse Keating 7a32965
@@ -28,6 +28,7 @@
Jesse Keating 7a32965
 #include <linux/rmap.h>
Jesse Keating 7a32965
 #include <linux/mmu_notifier.h>
Jesse Keating 7a32965
 #include <linux/perf_event.h>
Jesse Keating 7a32965
+#include <linux/random.h>
Jesse Keating 7a32965
 
Jesse Keating 7a32965
 #include <asm/uaccess.h>
Jesse Keating 7a32965
 #include <asm/cacheflush.h>
Jesse Keating 7a32965
@@ -1000,7 +1001,8 @@
Jesse Keating 7a32965
 	/* Obtain the address to map to. we verify (or select) it and ensure
Jesse Keating 7a32965
 	 * that it represents a valid section of the address space.
Jesse Keating 7a32965
 	 */
Jesse Keating 7a32965
-	addr = get_unmapped_area(file, addr, len, pgoff, flags);
Jesse Keating 7a32965
+	addr = get_unmapped_area_prot(file, addr, len, pgoff, flags,
Jesse Keating 7a32965
+		prot & PROT_EXEC);
Jesse Keating 7a32965
 	if (addr & ~PAGE_MASK)
Jesse Keating 7a32965
 		return addr;
Jesse Keating 7a32965
 
Jesse Keating 7a32965
@@ -1552,8 +1554,8 @@
Jesse Keating 7a32965
 }
Jesse Keating 7a32965
 
Jesse Keating 7a32965
 unsigned long
Jesse Keating 7a32965
-get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
Jesse Keating 7a32965
-		unsigned long pgoff, unsigned long flags)
Jesse Keating 7a32965
+get_unmapped_area_prot(struct file *file, unsigned long addr, unsigned long len,
Jesse Keating 7a32965
+		unsigned long pgoff, unsigned long flags, int exec)
Jesse Keating 7a32965
 {
Jesse Keating 7a32965
 	unsigned long (*get_area)(struct file *, unsigned long,
Jesse Keating 7a32965
 				  unsigned long, unsigned long, unsigned long);
Jesse Keating 7a32965
@@ -1566,7 +1568,11 @@
Jesse Keating 7a32965
 	if (len > TASK_SIZE)
Jesse Keating 7a32965
 		return -ENOMEM;
Jesse Keating 7a32965
 
Jesse Keating 7a32965
-	get_area = current->mm->get_unmapped_area;
Jesse Keating 7a32965
+	if (exec && current->mm->get_unmapped_exec_area)
Jesse Keating 7a32965
+		get_area = current->mm->get_unmapped_exec_area;
Jesse Keating 7a32965
+	else
Jesse Keating 7a32965
+		get_area = current->mm->get_unmapped_area;
Jesse Keating 7a32965
+
Jesse Keating 7a32965
 	if (file && file->f_op && file->f_op->get_unmapped_area)
Jesse Keating 7a32965
 		get_area = file->f_op->get_unmapped_area;
Jesse Keating 7a32965
 	addr = get_area(file, addr, len, pgoff, flags);
Jesse Keating 7a32965
@@ -1580,8 +1586,83 @@
Jesse Keating 7a32965
 
Jesse Keating 7a32965
 	return arch_rebalance_pgtables(addr, len);
Jesse Keating 7a32965
 }
Jesse Keating 7a32965
+EXPORT_SYMBOL(get_unmapped_area_prot);
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+static bool should_randomize(void)
Jesse Keating 7a32965
+{
Jesse Keating 7a32965
+	return (current->flags & PF_RANDOMIZE) &&
Jesse Keating 7a32965
+		!(current->personality & ADDR_NO_RANDOMIZE);
Jesse Keating 7a32965
+}
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+#define SHLIB_BASE	0x00110000
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+unsigned long
Jesse Keating 7a32965
+arch_get_unmapped_exec_area(struct file *filp, unsigned long addr0,
Jesse Keating 7a32965
+		unsigned long len0, unsigned long pgoff, unsigned long flags)
Jesse Keating 7a32965
+{
Jesse Keating 7a32965
+	unsigned long addr = addr0, len = len0;
Jesse Keating 7a32965
+	struct mm_struct *mm = current->mm;
Jesse Keating 7a32965
+	struct vm_area_struct *vma;
Jesse Keating 7a32965
+	unsigned long tmp;
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+	if (len > TASK_SIZE)
Jesse Keating 7a32965
+		return -ENOMEM;
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+	if (flags & MAP_FIXED)
Jesse Keating 7a32965
+		return addr;
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+	if (!addr)
Jesse Keating 7a32965
+		addr = !should_randomize() ? SHLIB_BASE :
Jesse Keating 7a32965
+			randomize_range(SHLIB_BASE, 0x01000000, len);
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+	if (addr) {
Jesse Keating 7a32965
+		addr = PAGE_ALIGN(addr);
Jesse Keating 7a32965
+		vma = find_vma(mm, addr);
Jesse Keating 7a32965
+		if (TASK_SIZE - len >= addr &&
Jesse Keating 7a32965
+		    (!vma || addr + len <= vma->vm_start))
Jesse Keating 7a32965
+			return addr;
Jesse Keating 7a32965
+	}
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+	addr = SHLIB_BASE;
Jesse Keating 7a32965
+	for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
Jesse Keating 7a32965
+		/* At this point:  (!vma || addr < vma->vm_end). */
Jesse Keating 7a32965
+		if (TASK_SIZE - len < addr)
Jesse Keating 7a32965
+			return -ENOMEM;
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+		if (!vma || addr + len <= vma->vm_start) {
Jesse Keating 7a32965
+			/*
Jesse Keating 7a32965
+			 * Must not let a PROT_EXEC mapping get into the
Jesse Keating 7a32965
+			 * brk area:
Jesse Keating 7a32965
+			 */
Jesse Keating 7a32965
+			if (addr + len > mm->brk)
Jesse Keating 7a32965
+				goto failed;
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+			/*
Jesse Keating 7a32965
+			 * Up until the brk area we randomize addresses
Jesse Keating 7a32965
+			 * as much as possible:
Jesse Keating 7a32965
+			 */
Jesse Keating 7a32965
+			if (addr >= 0x01000000 && should_randomize()) {
Jesse Keating 7a32965
+				tmp = randomize_range(0x01000000,
Jesse Keating 7a32965
+					PAGE_ALIGN(max(mm->start_brk,
Jesse Keating 7a32965
+					(unsigned long)0x08000000)), len);
Jesse Keating 7a32965
+				vma = find_vma(mm, tmp);
Jesse Keating 7a32965
+				if (TASK_SIZE - len >= tmp &&
Jesse Keating 7a32965
+				    (!vma || tmp + len <= vma->vm_start))
Jesse Keating 7a32965
+					return tmp;
Jesse Keating 7a32965
+			}
Jesse Keating 7a32965
+			/*
Jesse Keating 7a32965
+			 * Ok, randomization didnt work out - return
Jesse Keating 7a32965
+			 * the result of the linear search:
Jesse Keating 7a32965
+			 */
Jesse Keating 7a32965
+			return addr;
Jesse Keating 7a32965
+		}
Jesse Keating 7a32965
+		addr = vma->vm_end;
Jesse Keating 7a32965
+	}
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+failed:
Jesse Keating 7a32965
+	return current->mm->get_unmapped_area(filp, addr0, len0, pgoff, flags);
Jesse Keating 7a32965
+}
Jesse Keating 7a32965
 
Jesse Keating 7a32965
-EXPORT_SYMBOL(get_unmapped_area);
Jesse Keating 7a32965
 
Jesse Keating 7a32965
 /* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
Jesse Keating 7a32965
 struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
Jesse Keating 7a32965
--- a/arch/x86/mm/mmap.c
Jesse Keating 7a32965
+++ b/arch/x86/mm/mmap.c
Jesse Keating 7a32965
@@ -124,13 +124,16 @@ static unsigned long mmap_legacy_base(void)
Jesse Keating 7a32965
  */
Jesse Keating 7a32965
 void arch_pick_mmap_layout(struct mm_struct *mm)
Jesse Keating 7a32965
 {
Jesse Keating 7a32965
 	if (mmap_is_legacy()) {
Jesse Keating 7a32965
 		mm->mmap_base = mmap_legacy_base();
Jesse Keating 7a32965
 		mm->get_unmapped_area = arch_get_unmapped_area;
Jesse Keating 7a32965
 		mm->unmap_area = arch_unmap_area;
Jesse Keating 7a32965
 	} else {
Jesse Keating 7a32965
 		mm->mmap_base = mmap_base();
Jesse Keating 7a32965
 		mm->get_unmapped_area = arch_get_unmapped_area_topdown;
Jesse Keating 7a32965
+		if (!(current->personality & READ_IMPLIES_EXEC)
Jesse Keating 7a32965
+		    && mmap_is_ia32())
Jesse Keating 7a32965
+			mm->get_unmapped_exec_area = arch_get_unmapped_exec_area;
Jesse Keating 7a32965
 		mm->unmap_area = arch_unmap_area_topdown;
Jesse Keating 7a32965
 	}
Jesse Keating 7a32965
 }
Jesse Keating 7a32965
--- a/arch/x86/vdso/vdso32-setup.c
Jesse Keating 7a32965
+++ b/arch/x86/vdso/vdso32-setup.c
Jesse Keating 7a32965
@@ -331,7 +331,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
Jesse Keating 7a32965
 	if (compat)
Jesse Keating 7a32965
 		addr = VDSO_HIGH_BASE;
Jesse Keating 7a32965
 	else {
Jesse Keating 7a32965
-		addr = get_unmapped_area(NULL, 0, PAGE_SIZE, 0, 0);
Jesse Keating 7a32965
+		addr = get_unmapped_area_prot(NULL, 0, PAGE_SIZE, 0, 0, 1);
Jesse Keating 7a32965
 		if (IS_ERR_VALUE(addr)) {
Jesse Keating 7a32965
 			ret = addr;
Jesse Keating 7a32965
 			goto up_fail;
Jesse Keating 7a32965
--- a/include/linux/mm.h
Jesse Keating 7a32965
+++ b/include/linux/mm.h
Jesse Keating 7a32965
@@ -1263,7 +1263,13 @@ extern int install_special_mapping(struct mm_struct *mm,
Jesse Keating 7a32965
 				   unsigned long addr, unsigned long len,
Jesse Keating 7a32965
 				   unsigned long flags, struct page **pages);
Jesse Keating 7a32965
 
Jesse Keating 7a32965
-extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
Jesse Keating 7a32965
+extern unsigned long get_unmapped_area_prot(struct file *, unsigned long, unsigned long, unsigned long, unsigned long, int);
Jesse Keating 7a32965
+
Jesse Keating 7a32965
+static inline unsigned long get_unmapped_area(struct file *file, unsigned long addr,
Jesse Keating 7a32965
+		unsigned long len, unsigned long pgoff, unsigned long flags)
Jesse Keating 7a32965
+{
Jesse Keating 7a32965
+	return get_unmapped_area_prot(file, addr, len, pgoff, flags, 0);
Jesse Keating 7a32965
+}
Jesse Keating 7a32965
 
Jesse Keating 7a32965
 extern unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
Jesse Keating 7a32965
 	unsigned long len, unsigned long prot,
Jesse Keating 7a32965
--- a/include/linux/mm_types.h
Jesse Keating 7a32965
+++ b/include/linux/mm_types.h
Jesse Keating 7a32965
@@ -227,6 +227,9 @@ struct mm_struct {
Jesse Keating 7a32965
 	unsigned long (*get_unmapped_area) (struct file *filp,
Jesse Keating 7a32965
 				unsigned long addr, unsigned long len,
Jesse Keating 7a32965
 				unsigned long pgoff, unsigned long flags);
Jesse Keating 7a32965
+       unsigned long (*get_unmapped_exec_area) (struct file *filp,
Jesse Keating 7a32965
+				unsigned long addr, unsigned long len,
Jesse Keating 7a32965
+				unsigned long pgoff, unsigned long flags);
Jesse Keating 7a32965
 	void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
Jesse Keating 7a32965
 #endif
Jesse Keating 7a32965
 	unsigned long mmap_base;		/* base of mmap area */
Jesse Keating 7a32965
--- a/mm/mremap.c
Jesse Keating 7a32965
+++ b/mm/mremap.c
Jesse Keating 7a32965
@@ -487,10 +487,10 @@ unsigned long do_mremap(unsigned long addr,
Jesse Keating 7a32965
 		if (vma->vm_flags & VM_MAYSHARE)
Jesse Keating 7a32965
 			map_flags |= MAP_SHARED;
Jesse Keating 7a32965
 
Jesse Keating 7a32965
-		new_addr = get_unmapped_area(vma->vm_file, 0, new_len,
Jesse Keating 7a32965
+		new_addr = get_unmapped_area_prot(vma->vm_file, 0, new_len,
Jesse Keating 7a32965
 					vma->vm_pgoff +
Jesse Keating 7a32965
 					((addr - vma->vm_start) >> PAGE_SHIFT),
Jesse Keating 7a32965
-					map_flags);
Jesse Keating 7a32965
+					map_flags, vma->vm_flags & VM_EXEC);
Jesse Keating 7a32965
 		if (new_addr & ~PAGE_MASK) {
Jesse Keating 7a32965
 			ret = new_addr;
Jesse Keating 7a32965
 			goto out;