@@ -348,4 +348,11 @@ struct pt_regs; /* forward declaration... */
#define ELF_HWCAP 0
+/* Stack randomization: 1GB for 64bit, 8MB for 32bit */
+#ifdef CONFIG_64BIT
+# define STACK_RND_MASK (test_thread_flag(TIF_32BIT) ? 0x7ff : 0x3fffff)
+#else
+# define STACK_RND_MASK (0x7ff)
+#endif
+
#endif
@@ -511,6 +511,7 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm, unsigned long addr,
/* We provide our own get_unmapped_area to provide cache coherency */
#define HAVE_ARCH_UNMAPPED_AREA
+#define HAVE_ARCH_UNMAPPED_AREA_TOPDOWN
#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
#define __HAVE_ARCH_PTEP_GET_AND_CLEAR
@@ -30,6 +30,8 @@
#endif
#define current_text_addr() ({ void *pc; current_ia(pc); pc; })
+#define HAVE_ARCH_PICK_MMAP_LAYOUT
+
#define TASK_SIZE_OF(tsk) ((tsk)->thread.task_size)
#define TASK_SIZE TASK_SIZE_OF(current)
#define TASK_UNMAPPED_BASE (current->thread.map_base)
@@ -23,6 +23,7 @@
*/
#include <asm/uaccess.h>
+#include <asm/elf.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/linkage.h>
@@ -32,19 +33,7 @@
#include <linux/syscalls.h>
#include <linux/utsname.h>
#include <linux/personality.h>
-
-static unsigned long get_unshared_area(unsigned long addr, unsigned long len)
-{
- struct vm_unmapped_area_info info;
-
- info.flags = 0;
- info.length = len;
- info.low_limit = PAGE_ALIGN(addr);
- info.high_limit = TASK_SIZE;
- info.align_mask = 0;
- info.align_offset = 0;
- return vm_unmapped_area(&info);
-}
+#include <linux/random.h>
/*
* We need to know the offset to use. Old scheme was to look for
@@ -58,7 +47,7 @@ static unsigned long get_unshared_area(unsigned long addr, unsigned long len)
*/
static int get_offset(struct address_space *mapping)
{
- return (unsigned long) mapping >> 8;
+ return ((unsigned long) mapping >> 8) & ((SHMLBA-1) >> PAGE_SHIFT);
}
static unsigned long shared_align_offset(struct file *filp, unsigned long pgoff)
@@ -68,42 +57,215 @@ static unsigned long shared_align_offset(struct file *filp, unsigned long pgoff)
return (get_offset(mapping) + pgoff) << PAGE_SHIFT;
}
-static unsigned long get_shared_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff)
+static inline unsigned long COLOR_ALIGN(unsigned long addr,
+ struct file *filp, unsigned long pgoff)
+{
+ unsigned long base = (addr+SHMLBA-1)&~(SHMLBA-1);
+ unsigned long off = (shared_align_offset(filp, pgoff) << PAGE_SHIFT)
+ & (SHMLBA-1);
+
+ return base + off;
+}
+
+/*
+ * Top of mmap area (just below the process stack).
+ */
+
+static unsigned long mmap_upper_limit(void)
+{
+ unsigned long stack_base;
+
+ /* Limit stack size to 1GB - see setup_arg_pages() in fs/exec.c */
+ stack_base = rlimit_max(RLIMIT_STACK);
+ if (stack_base > (1 << 30))
+ stack_base = 1 << 30;
+
+ return PAGE_ALIGN(STACK_TOP - stack_base);
+}
+
+
+unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
+ unsigned long len, unsigned long pgoff, unsigned long flags)
{
+ struct mm_struct *mm = current->mm;
+ struct vm_area_struct *vma;
+ unsigned long task_size = TASK_SIZE;
+ int do_color_align;
struct vm_unmapped_area_info info;
+ if (len > task_size)
+ return -ENOMEM;
+ if (flags & MAP_FIXED) {
+ if ((flags & MAP_SHARED) &&
+ (addr - shared_align_offset(filp, pgoff)) & (SHMLBA - 1))
+ return -EINVAL;
+ return addr;
+ }
+
+ do_color_align = 0;
+ if (filp || (flags & MAP_SHARED))
+ do_color_align = 1;
+
+ if (addr) {
+ if (do_color_align)
+ addr = COLOR_ALIGN(addr, filp, pgoff);
+ else
+ addr = PAGE_ALIGN(addr);
+
+ vma = find_vma(mm, addr);
+ if (task_size - len >= addr &&
+ (!vma || addr + len <= vma->vm_start))
+ return addr;
+ }
+
info.flags = 0;
info.length = len;
- info.low_limit = PAGE_ALIGN(addr);
- info.high_limit = TASK_SIZE;
- info.align_mask = PAGE_MASK & (SHMLBA - 1);
+ info.low_limit = mm->mmap_legacy_base;
+ info.high_limit = mmap_upper_limit();
+ info.align_mask = do_color_align ? (PAGE_MASK & (SHMLBA - 1)) : 0;
info.align_offset = shared_align_offset(filp, pgoff);
- return vm_unmapped_area(&info);
+ addr = vm_unmapped_area(&info);
+
+ return addr;
}
-unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
- unsigned long len, unsigned long pgoff, unsigned long flags)
+unsigned long
+arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
+ const unsigned long len, const unsigned long pgoff,
+ const unsigned long flags)
{
+ struct vm_area_struct *vma;
+ struct mm_struct *mm = current->mm;
+ unsigned long addr = addr0;
+ int do_color_align;
+ struct vm_unmapped_area_info info;
+
+ /* This should only ever run for 32-bit processes. */
+ BUG_ON(!test_thread_flag(TIF_32BIT));
+
+ /* requested length too big for entire address space */
if (len > TASK_SIZE)
return -ENOMEM;
+
if (flags & MAP_FIXED) {
if ((flags & MAP_SHARED) &&
(addr - shared_align_offset(filp, pgoff)) & (SHMLBA - 1))
return -EINVAL;
return addr;
}
- if (!addr)
- addr = TASK_UNMAPPED_BASE;
+ do_color_align = 0;
if (filp || (flags & MAP_SHARED))
- addr = get_shared_area(filp, addr, len, pgoff);
- else
- addr = get_unshared_area(addr, len);
+ do_color_align = 1;
+
+ /* requesting a specific address */
+ if (addr) {
+ if (do_color_align)
+ addr = COLOR_ALIGN(addr, filp, pgoff);
+ else
+ addr = PAGE_ALIGN(addr);
+ vma = find_vma(mm, addr);
+ if (TASK_SIZE - len >= addr &&
+ (!vma || addr + len <= vma->vm_start))
+ return addr;
+ }
- return addr;
+ info.flags = VM_UNMAPPED_AREA_TOPDOWN;
+ info.length = len;
+ info.low_limit = PAGE_SIZE;
+ info.high_limit = mm->mmap_base;
+ info.align_mask = do_color_align ? (PAGE_MASK & (SHMLBA - 1)) : 0;
+ info.align_offset = shared_align_offset(filp, pgoff);
+ addr = vm_unmapped_area(&info);
+ if (!(addr & ~PAGE_MASK))
+ return addr;
+ VM_BUG_ON(addr != -ENOMEM);
+
+ /*
+ * A failed mmap() very likely causes application failure,
+ * so fall back to the bottom-up function here. This scenario
+ * can happen with large stack limits and large mmap()
+ * allocations.
+ */
+ return arch_get_unmapped_area(filp, addr0, len, pgoff, flags);
+}
+
+static int mmap_is_legacy(void)
+{
+ if (current->personality & ADDR_COMPAT_LAYOUT)
+ return 1;
+
+ /* parisc stack always grows up - so a unlimited stack should
+ * not be an indicator to use the legacy memory layout. */
+ if (rlimit(RLIMIT_STACK) == RLIM_INFINITY)
+ return 1;
+
+ return sysctl_legacy_va_layout;
+}
+
+/*
+ * True if 32bit task
+ */
+static inline int mmap_is_32bit(void)
+{
+#ifndef CONFIG_64BIT
+ return 1;
+#endif
+#ifdef CONFIG_COMPAT
+ if (test_thread_flag(TIF_32BIT))
+ return 1;
+#endif
+ return 0;
}
+static unsigned long mmap_rnd(void)
+{
+ unsigned long rnd = 0;
+
+ /*
+ * 8 bits of randomness in 32bit mmaps, 20 address space bits
+ * 28 bits of randomness in 64bit mmaps, 40 address space bits
+ */
+ if (current->flags & PF_RANDOMIZE) {
+ if (mmap_is_32bit())
+ rnd = get_random_int() % (1<<8);
+ else
+ rnd = get_random_int() % (1<<28);
+ }
+ return rnd << PAGE_SHIFT;
+}
+
+static unsigned long mmap_legacy_base(void)
+{
+ return TASK_UNMAPPED_BASE + mmap_rnd();
+}
+
+/*
+ * Flexible mmap layout support
+ * http://lwn.net/Articles/91829
+ * echo 0/1 /proc/sys/vm/legacy_va_layout
+ *
+ */
+
+
+/*
+ * This function, called very early during the creation of a new
+ * process VM image, sets up which VM layout function to use:
+ */
+void arch_pick_mmap_layout(struct mm_struct *mm)
+{
+ mm->mmap_legacy_base = mmap_legacy_base();
+ mm->mmap_base = mmap_upper_limit();
+
+ if (mmap_is_legacy()) {
+ mm->mmap_base = mm->mmap_legacy_base;
+ mm->get_unmapped_area = arch_get_unmapped_area;
+ } else {
+ mm->get_unmapped_area = arch_get_unmapped_area_topdown;
+ }
+}
+
+
asmlinkage unsigned long sys_mmap2(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long fd,
unsigned long pgoff)
- initial version - add mmap address & stack randomization - probably fixes a few other bugs in the mmap code - can be enabled/turned off by "echo 0/1 > /proc/sys/vm/legacy_va_layout - new flexible mmap layout is by default turned on Signed-off-by: Helge Deller <deller@gmx.de> -- To unsubscribe from this list: send the line "unsubscribe linux-parisc" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html