Dave Jones c62acbf
Before:
Dave Jones c62acbf
Heap randomisation test (PIE)            : 16 bits (guessed)
Dave Jones c62acbf
Main executable randomisation (PIE)      : 8 bits (guessed)
Dave Jones c62acbf
Dave Jones c62acbf
after:
Dave Jones c62acbf
Heap randomisation test (PIE)            : 19 bits (guessed)
Dave Jones c62acbf
Main executable randomisation (PIE)      : 12 bits (guessed)
Dave Jones c62acbf
Dave Jones c62acbf
Dave Jones c62acbf
Dave Jones c62acbf
--- b/include/linux/sched.h
Dave Jones c62acbf
+++ b/include/linux/sched.h
Dave Jones c62acbf
@@ -397,6 +397,10 @@
Dave Jones c62acbf
 extern unsigned long
Dave Jones c62acbf
 arch_get_unmapped_area(struct file *, unsigned long, unsigned long,
Dave Jones c62acbf
 		       unsigned long, unsigned long);
Dave Jones c62acbf
+
Dave Jones c62acbf
+extern unsigned long
Dave Jones c62acbf
+arch_get_unmapped_exec_area(struct file *, unsigned long, unsigned long,
Dave Jones c62acbf
+		       unsigned long, unsigned long);
Dave Jones c62acbf
 extern unsigned long
Dave Jones c62acbf
 arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
Dave Jones c62acbf
 			  unsigned long len, unsigned long pgoff,
Dave Jones c62acbf
--- b/mm/mmap.c
Dave Jones c62acbf
+++ b/mm/mmap.c
Dave Jones c62acbf
@@ -28,6 +28,7 @@
Dave Jones c62acbf
 #include <linux/perf_event.h>
Dave Jones c62acbf
 #include <linux/audit.h>
Dave Jones c62acbf
 #include <linux/khugepaged.h>
Dave Jones c62acbf
+#include <linux/random.h>
Dave Jones c62acbf
 
Dave Jones c62acbf
 #include <asm/uaccess.h>
Dave Jones c62acbf
 #include <asm/cacheflush.h>
Dave Jones c62acbf
@@ -1000,7 +1001,8 @@
Dave Jones c62acbf
 	/* Obtain the address to map to. we verify (or select) it and ensure
Dave Jones c62acbf
 	 * that it represents a valid section of the address space.
Dave Jones c62acbf
 	 */
Dave Jones c62acbf
-	addr = get_unmapped_area(file, addr, len, pgoff, flags);
Dave Jones c62acbf
+	addr = get_unmapped_area_prot(file, addr, len, pgoff, flags,
Dave Jones c62acbf
+		prot & PROT_EXEC);
Dave Jones c62acbf
 	if (addr & ~PAGE_MASK)
Dave Jones c62acbf
 		return addr;
Dave Jones c62acbf
 
Dave Jones c62acbf
@@ -1552,8 +1554,8 @@
Dave Jones c62acbf
 }
Dave Jones c62acbf
 
Dave Jones c62acbf
 unsigned long
Dave Jones c62acbf
-get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
Dave Jones c62acbf
-		unsigned long pgoff, unsigned long flags)
Dave Jones c62acbf
+get_unmapped_area_prot(struct file *file, unsigned long addr, unsigned long len,
Dave Jones c62acbf
+		unsigned long pgoff, unsigned long flags, int exec)
Dave Jones c62acbf
 {
Dave Jones c62acbf
 	unsigned long (*get_area)(struct file *, unsigned long,
Dave Jones c62acbf
 				  unsigned long, unsigned long, unsigned long);
Dave Jones c62acbf
@@ -1566,7 +1568,11 @@
Dave Jones c62acbf
 	if (len > TASK_SIZE)
Dave Jones c62acbf
 		return -ENOMEM;
Dave Jones c62acbf
 
Dave Jones c62acbf
-	get_area = current->mm->get_unmapped_area;
Dave Jones c62acbf
+	if (exec && current->mm->get_unmapped_exec_area)
Dave Jones c62acbf
+		get_area = current->mm->get_unmapped_exec_area;
Dave Jones c62acbf
+	else
Dave Jones c62acbf
+		get_area = current->mm->get_unmapped_area;
Dave Jones c62acbf
+
Dave Jones c62acbf
 	if (file && file->f_op && file->f_op->get_unmapped_area)
Dave Jones c62acbf
 		get_area = file->f_op->get_unmapped_area;
Dave Jones c62acbf
 	addr = get_area(file, addr, len, pgoff, flags);
Dave Jones c62acbf
@@ -1580,8 +1586,83 @@
Dave Jones c62acbf
 
Dave Jones c62acbf
 	return arch_rebalance_pgtables(addr, len);
Dave Jones c62acbf
 }
Dave Jones c62acbf
+EXPORT_SYMBOL(get_unmapped_area_prot);
Dave Jones c62acbf
+
Dave Jones c62acbf
+static bool should_randomize(void)
Dave Jones c62acbf
+{
Dave Jones c62acbf
+	return (current->flags & PF_RANDOMIZE) &&
Dave Jones c62acbf
+		!(current->personality & ADDR_NO_RANDOMIZE);
Dave Jones c62acbf
+}
Dave Jones c62acbf
+
Dave Jones c62acbf
+#define SHLIB_BASE	0x00110000
Dave Jones c62acbf
+
Dave Jones c62acbf
+unsigned long
Dave Jones c62acbf
+arch_get_unmapped_exec_area(struct file *filp, unsigned long addr0,
Dave Jones c62acbf
+		unsigned long len0, unsigned long pgoff, unsigned long flags)
Dave Jones c62acbf
+{
Dave Jones c62acbf
+	unsigned long addr = addr0, len = len0;
Dave Jones c62acbf
+	struct mm_struct *mm = current->mm;
Dave Jones c62acbf
+	struct vm_area_struct *vma;
Dave Jones c62acbf
+	unsigned long tmp;
Dave Jones c62acbf
+
Dave Jones c62acbf
+	if (len > TASK_SIZE)
Dave Jones c62acbf
+		return -ENOMEM;
Dave Jones c62acbf
+
Dave Jones c62acbf
+	if (flags & MAP_FIXED)
Dave Jones c62acbf
+		return addr;
Dave Jones c62acbf
+
Dave Jones c62acbf
+	if (!addr)
Dave Jones c62acbf
+		addr = !should_randomize() ? SHLIB_BASE :
Dave Jones c62acbf
+			randomize_range(SHLIB_BASE, 0x01000000, len);
Dave Jones c62acbf
+
Dave Jones c62acbf
+	if (addr) {
Dave Jones c62acbf
+		addr = PAGE_ALIGN(addr);
Dave Jones c62acbf
+		vma = find_vma(mm, addr);
Dave Jones c62acbf
+		if (TASK_SIZE - len >= addr &&
Dave Jones c62acbf
+		    (!vma || addr + len <= vma->vm_start))
Dave Jones c62acbf
+			return addr;
Dave Jones c62acbf
+	}
Dave Jones c62acbf
+
Dave Jones c62acbf
+	addr = SHLIB_BASE;
Dave Jones c62acbf
+	for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
Dave Jones c62acbf
+		/* At this point:  (!vma || addr < vma->vm_end). */
Dave Jones c62acbf
+		if (TASK_SIZE - len < addr)
Dave Jones c62acbf
+			return -ENOMEM;
Dave Jones c62acbf
+
Dave Jones c62acbf
+		if (!vma || addr + len <= vma->vm_start) {
Dave Jones c62acbf
+			/*
Dave Jones c62acbf
+			 * Must not let a PROT_EXEC mapping get into the
Dave Jones c62acbf
+			 * brk area:
Dave Jones c62acbf
+			 */
Dave Jones c62acbf
+			if (addr + len > mm->brk)
Dave Jones c62acbf
+				goto failed;
Dave Jones c62acbf
+
Dave Jones c62acbf
+			/*
Dave Jones c62acbf
+			 * Up until the brk area we randomize addresses
Dave Jones c62acbf
+			 * as much as possible:
Dave Jones c62acbf
+			 */
Dave Jones c62acbf
+			if (addr >= 0x01000000 && should_randomize()) {
Dave Jones c62acbf
+				tmp = randomize_range(0x01000000,
Dave Jones c62acbf
+					PAGE_ALIGN(max(mm->start_brk,
Dave Jones c62acbf
+					(unsigned long)0x08000000)), len);
Dave Jones c62acbf
+				vma = find_vma(mm, tmp);
Dave Jones c62acbf
+				if (TASK_SIZE - len >= tmp &&
Dave Jones c62acbf
+				    (!vma || tmp + len <= vma->vm_start))
Dave Jones c62acbf
+					return tmp;
Dave Jones c62acbf
+			}
Dave Jones c62acbf
+			/*
Dave Jones c62acbf
+			 * Ok, randomization didnt work out - return
Dave Jones c62acbf
+			 * the result of the linear search:
Dave Jones c62acbf
+			 */
Dave Jones c62acbf
+			return addr;
Dave Jones c62acbf
+		}
Dave Jones c62acbf
+		addr = vma->vm_end;
Dave Jones c62acbf
+	}
Dave Jones c62acbf
+
Dave Jones c62acbf
+failed:
Dave Jones c62acbf
+	return current->mm->get_unmapped_area(filp, addr0, len0, pgoff, flags);
Dave Jones c62acbf
+}
Dave Jones c62acbf
 
Dave Jones c62acbf
-EXPORT_SYMBOL(get_unmapped_area);
Dave Jones c62acbf
 
Dave Jones c62acbf
 /* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
Dave Jones c62acbf
 struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
Dave Jones c62acbf
--- a/arch/x86/mm/mmap.c
Dave Jones c62acbf
+++ b/arch/x86/mm/mmap.c
Dave Jones bd4ac46
@@ -124,13 +124,19 @@ static unsigned long mmap_legacy_base(void)
Dave Jones c62acbf
  */
Dave Jones c62acbf
 void arch_pick_mmap_layout(struct mm_struct *mm)
Dave Jones c62acbf
 {
Dave Jones c62acbf
 	if (mmap_is_legacy()) {
Dave Jones c62acbf
 		mm->mmap_base = mmap_legacy_base();
Dave Jones c62acbf
 		mm->get_unmapped_area = arch_get_unmapped_area;
Dave Jones c62acbf
 		mm->unmap_area = arch_unmap_area;
Dave Jones c62acbf
 	} else {
Dave Jones c62acbf
 		mm->mmap_base = mmap_base();
Dave Jones c62acbf
 		mm->get_unmapped_area = arch_get_unmapped_area_topdown;
Dave Jones bd4ac46
+#ifdef CONFIG_X86_32
Dave Jones c62acbf
+		if (!(current->personality & READ_IMPLIES_EXEC)
Dave Jones bd4ac46
+		    && !(__supported_pte_mask & _PAGE_NX)
Dave Jones c62acbf
+		    && mmap_is_ia32())
Dave Jones c62acbf
+			mm->get_unmapped_exec_area = arch_get_unmapped_exec_area;
Dave Jones bd4ac46
+#endif
Dave Jones c62acbf
 		mm->unmap_area = arch_unmap_area_topdown;
Dave Jones c62acbf
 	}
Dave Jones c62acbf
 }
Dave Jones c62acbf
--- a/arch/x86/vdso/vdso32-setup.c
Dave Jones c62acbf
+++ b/arch/x86/vdso/vdso32-setup.c
Dave Jones c62acbf
@@ -331,7 +331,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
Dave Jones c62acbf
 	if (compat)
Dave Jones c62acbf
 		addr = VDSO_HIGH_BASE;
Dave Jones c62acbf
 	else {
Dave Jones c62acbf
-		addr = get_unmapped_area(NULL, 0, PAGE_SIZE, 0, 0);
Dave Jones c62acbf
+		addr = get_unmapped_area_prot(NULL, 0, PAGE_SIZE, 0, 0, 1);
Dave Jones c62acbf
 		if (IS_ERR_VALUE(addr)) {
Dave Jones c62acbf
 			ret = addr;
Dave Jones c62acbf
 			goto up_fail;
Dave Jones c62acbf
--- a/include/linux/mm.h
Dave Jones c62acbf
+++ b/include/linux/mm.h
Dave Jones c62acbf
@@ -1263,7 +1263,13 @@ extern int install_special_mapping(struct mm_struct *mm,
Dave Jones c62acbf
 				   unsigned long addr, unsigned long len,
Dave Jones c62acbf
 				   unsigned long flags, struct page **pages);
Dave Jones c62acbf
 
Dave Jones c62acbf
-extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
Dave Jones c62acbf
+extern unsigned long get_unmapped_area_prot(struct file *, unsigned long, unsigned long, unsigned long, unsigned long, int);
Dave Jones c62acbf
+
Dave Jones c62acbf
+static inline unsigned long get_unmapped_area(struct file *file, unsigned long addr,
Dave Jones c62acbf
+		unsigned long len, unsigned long pgoff, unsigned long flags)
Dave Jones c62acbf
+{
Dave Jones c62acbf
+	return get_unmapped_area_prot(file, addr, len, pgoff, flags, 0);
Dave Jones c62acbf
+}
Dave Jones c62acbf
 
Dave Jones c62acbf
 extern unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
Dave Jones c62acbf
 	unsigned long len, unsigned long prot,
Dave Jones c62acbf
--- a/include/linux/mm_types.h
Dave Jones c62acbf
+++ b/include/linux/mm_types.h
Dave Jones c62acbf
@@ -227,6 +227,9 @@ struct mm_struct {
Dave Jones c62acbf
 	unsigned long (*get_unmapped_area) (struct file *filp,
Dave Jones c62acbf
 				unsigned long addr, unsigned long len,
Dave Jones c62acbf
 				unsigned long pgoff, unsigned long flags);
Dave Jones c62acbf
+       unsigned long (*get_unmapped_exec_area) (struct file *filp,
Dave Jones c62acbf
+				unsigned long addr, unsigned long len,
Dave Jones c62acbf
+				unsigned long pgoff, unsigned long flags);
Dave Jones c62acbf
 	void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
Dave Jones c62acbf
 #endif
Dave Jones c62acbf
 	unsigned long mmap_base;		/* base of mmap area */
Dave Jones c62acbf
--- a/mm/mremap.c
Dave Jones c62acbf
+++ b/mm/mremap.c
Dave Jones c62acbf
@@ -487,10 +487,10 @@ unsigned long do_mremap(unsigned long addr,
Dave Jones c62acbf
 		if (vma->vm_flags & VM_MAYSHARE)
Dave Jones c62acbf
 			map_flags |= MAP_SHARED;
Dave Jones c62acbf
 
Dave Jones c62acbf
-		new_addr = get_unmapped_area(vma->vm_file, 0, new_len,
Dave Jones c62acbf
+		new_addr = get_unmapped_area_prot(vma->vm_file, 0, new_len,
Dave Jones c62acbf
 					vma->vm_pgoff +
Dave Jones c62acbf
 					((addr - vma->vm_start) >> PAGE_SHIFT),
Dave Jones c62acbf
-					map_flags);
Dave Jones c62acbf
+					map_flags, vma->vm_flags & VM_EXEC);
Dave Jones c62acbf
 		if (new_addr & ~PAGE_MASK) {
Dave Jones c62acbf
 			ret = new_addr;
Dave Jones c62acbf
 			goto out;
Dave Jones bd4ac46
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
Dave Jones bd4ac46
index 57d1868..29c0c35 100644
Dave Jones bd4ac46
--- a/arch/x86/kernel/process.c
Dave Jones bd4ac46
+++ b/arch/x86/kernel/process.c
Dave Jones bd4ac46
@@ -669,6 +669,16 @@ unsigned long arch_align_stack(unsigned long sp)
Dave Jones bd4ac46
 unsigned long arch_randomize_brk(struct mm_struct *mm)
Dave Jones bd4ac46
 {
Dave Jones bd4ac46
 	unsigned long range_end = mm->brk + 0x02000000;
Dave Jones bd4ac46
-	return randomize_range(mm->brk, range_end, 0) ? : mm->brk;
Dave Jones bd4ac46
+	unsigned long bump = 0;
Dave Jones bd4ac46
+#ifdef CONFIG_X86_32
Dave Jones bd4ac46
+	/* in the case of NX emulation, shove the brk segment way out of the
Dave Jones bd4ac46
+	   way of the exec randomization area, since it can collide with
Dave Jones bd4ac46
+	   future allocations if not. */
Dave Jones bd4ac46
+	if ( (mm->get_unmapped_exec_area == arch_get_unmapped_exec_area) &&
Dave Jones bd4ac46
+	     (mm->brk < 0x08000000) ) {
Dave Jones bd4ac46
+		bump = (TASK_SIZE/6);
Dave Jones bd4ac46
+	}
Dave Jones bd4ac46
+#endif
Dave Jones bd4ac46
+	return bump + (randomize_range(mm->brk, range_end, 0) ? : mm->brk);
Dave Jones bd4ac46
 }
Dave Jones bd4ac46