linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [RFC] x86, mm: start mmap allocation for libs from low addresses
@ 2011-08-12 10:29 Vasiliy Kulikov
  2011-08-12 23:19 ` H. Peter Anvin
  0 siblings, 1 reply; 9+ messages in thread
From: Vasiliy Kulikov @ 2011-08-12 10:29 UTC (permalink / raw)
  To: Thomas Gleixner, Ingo Molnar
  Cc: kernel-hardening, H. Peter Anvin, Peter Zijlstra, Andrew Morton,
	x86, linux-kernel, linux-mm

This patch changes mmap base address allocator logic to incline to
allocate addresses from the first 16 Mbs of address space.  These
addresses start from zero byte (0x00AABBCC).  Using such addresses
breaks ret2libc exploits abusing string buffer overflows.  As library
addresses contain zero byte, addresses of library functions may not
present in the string, which is used to overflow the buffer.  As a
result, it makes it impossible to change the return address on the stack
to the address of some library function (e.g. system(3)).

The logic is applied to 32 bit tasks, both for 32 bit kernels and for 32
bit tasks running on 64 bit kernels.  64 bit tasks already have zero
bytes in addresses of library functions.  Other architectures may reuse
the logic.

The first Mb is excluded from the range because of the compatibility with
programs like Wine and Dosemu.

If the sum of libraries sizes plus executable size doesn't exceed 15 Mb,
the only pages out of ASCII-protected range are VDSO and vsyscall.
However, they don't provide enough material for obtaining arbitrary code
execution and are not dangerous without using other executable pages.

If 16 Mbs are over, we fallback to the old allocation algorithm.

Without the patch:

$ ldd /bin/ls
	linux-gate.so.1 =>  (0xf779c000)
        librt.so.1 => /lib/librt.so.1 (0xb7fcf000)
        libtermcap.so.2 => /lib/libtermcap.so.2 (0xb7fca000)
        libc.so.6 => /lib/libc.so.6 (0xb7eae000)
        libpthread.so.0 => /lib/libpthread.so.0 (0xb7e5b000)
        /lib/ld-linux.so.2 (0xb7fe6000)

With the patch:

$ ldd /bin/ls
	linux-gate.so.1 =>  (0xf772a000)
	librt.so.1 => /lib/librt.so.1 (0x0014a000)
	libtermcap.so.2 => /lib/libtermcap.so.2 (0x0015e000)
	libc.so.6 => /lib/libc.so.6 (0x00162000)
	libpthread.so.0 => /lib/libpthread.so.0 (0x00283000)
	/lib/ld-linux.so.2 (0x00131000)

The same logic was used in -ow patch for 2.0-2.4 kernels and in
exec-shield for 2.6.x kernels.  Parts of the code were taken from RHEL6
version of exec-shield.

Signed-off-by: Vasiliy Kulikov <segoon@openwall.com>
--
 arch/x86/mm/mmap.c       |    5 +++
 include/linux/mm_types.h |    4 +++
 include/linux/sched.h    |    3 ++
 mm/mmap.c                |   66 ++++++++++++++++++++++++++++++++++++++++++---
 4 files changed, 73 insertions(+), 5 deletions(-)

diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c
index 1dab519..0c82a94 100644
--- a/arch/x86/mm/mmap.c
+++ b/arch/x86/mm/mmap.c
@@ -131,6 +131,11 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
 	} else {
 		mm->mmap_base = mmap_base();
 		mm->get_unmapped_area = arch_get_unmapped_area_topdown;
+		if (mmap_is_ia32()) {
+			mm->get_unmapped_exec_area =
+				arch_get_unmapped_exec_area;
+			mm->lib_mmap_base = 0x00110000 + mmap_rnd();
+		}
 		mm->unmap_area = arch_unmap_area_topdown;
 	}
 }
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 027935c..5f2dca9 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -225,9 +225,13 @@ struct mm_struct {
 	unsigned long (*get_unmapped_area) (struct file *filp,
 				unsigned long addr, unsigned long len,
 				unsigned long pgoff, unsigned long flags);
+	unsigned long (*get_unmapped_exec_area) (struct file *filp,
+				unsigned long addr, unsigned long len,
+				unsigned long pgoff, unsigned long flags);
 	void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
 #endif
 	unsigned long mmap_base;		/* base of mmap area */
+	unsigned long lib_mmap_base;		/* base of mmap libraries area (includes zero symbol) */
 	unsigned long task_size;		/* size of task vm space */
 	unsigned long cached_hole_size; 	/* if non-zero, the largest hole below free_area_cache */
 	unsigned long free_area_cache;		/* first hole of size cached_hole_size or larger */
diff --git a/include/linux/sched.h b/include/linux/sched.h
index f024c63..8feaba9 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -394,6 +394,9 @@ arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
 			  unsigned long flags);
 extern void arch_unmap_area(struct mm_struct *, unsigned long);
 extern void arch_unmap_area_topdown(struct mm_struct *, unsigned long);
+extern unsigned long
+arch_get_unmapped_exec_area(struct file *, unsigned long,
+		unsigned long, unsigned long, unsigned long);
 #else
 static inline void arch_pick_mmap_layout(struct mm_struct *mm) {}
 #endif
diff --git a/mm/mmap.c b/mm/mmap.c
index d49736f..489510c 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -50,6 +50,10 @@ static void unmap_region(struct mm_struct *mm,
 		struct vm_area_struct *vma, struct vm_area_struct *prev,
 		unsigned long start, unsigned long end);
 
+static unsigned long
+get_unmapped_area_prot(struct file *file, unsigned long addr, unsigned long len,
+		unsigned long pgoff, unsigned long flags, bool exec);
+
 /*
  * WARNING: the debugging will use recursive algorithms so never enable this
  * unless you know what you are doing.
@@ -989,7 +993,8 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
 	/* Obtain the address to map to. we verify (or select) it and ensure
 	 * that it represents a valid section of the address space.
 	 */
-	addr = get_unmapped_area(file, addr, len, pgoff, flags);
+	addr = get_unmapped_area_prot(file, addr, len, pgoff, flags,
+			prot & PROT_EXEC);
 	if (addr & ~PAGE_MASK)
 		return addr;
 
@@ -1528,6 +1533,46 @@ bottomup:
 }
 #endif
 
+unsigned long
+arch_get_unmapped_exec_area(struct file *filp, unsigned long addr0,
+		unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+	unsigned long addr = addr0;
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+
+	if (len > TASK_SIZE)
+		return -ENOMEM;
+
+	if (flags & MAP_FIXED)
+		return addr;
+
+	/* We ALWAYS start from the beginning as base addresses
+	 * with zero high bits is a valued resource */
+	addr = max_t(unsigned long, mm->lib_mmap_base, mmap_min_addr);
+
+	for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
+		/* At this point:  (!vma || addr < vma->vm_end). */
+		if (TASK_SIZE - len < addr)
+			return -ENOMEM;
+
+		/* We don't want to touch brk of not DYNAMIC elf binaries */
+		if (mm->brk && addr > mm->brk)
+			goto failed;
+
+		if (!vma || addr + len <= vma->vm_start)
+			return addr;
+
+		addr = vma->vm_end;
+		/* If 0x01000000 is touched, the algo gives up */
+		if (addr >= 0x01000000)
+			goto failed;
+	}
+
+failed:
+	return current->mm->get_unmapped_area(filp, addr0, len, pgoff, flags);
+}
+
 void arch_unmap_area_topdown(struct mm_struct *mm, unsigned long addr)
 {
 	/*
@@ -1541,9 +1586,9 @@ void arch_unmap_area_topdown(struct mm_struct *mm, unsigned long addr)
 		mm->free_area_cache = mm->mmap_base;
 }
 
-unsigned long
-get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
-		unsigned long pgoff, unsigned long flags)
+static unsigned long
+get_unmapped_area_prot(struct file *file, unsigned long addr, unsigned long len,
+		unsigned long pgoff, unsigned long flags, bool exec)
 {
 	unsigned long (*get_area)(struct file *, unsigned long,
 				  unsigned long, unsigned long, unsigned long);
@@ -1556,7 +1601,11 @@ get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
 	if (len > TASK_SIZE)
 		return -ENOMEM;
 
-	get_area = current->mm->get_unmapped_area;
+	if (exec && current->mm->get_unmapped_exec_area)
+		get_area = current->mm->get_unmapped_exec_area;
+	else
+		get_area = current->mm->get_unmapped_area;
+
 	if (file && file->f_op && file->f_op->get_unmapped_area)
 		get_area = file->f_op->get_unmapped_area;
 	addr = get_area(file, addr, len, pgoff, flags);
@@ -1571,6 +1620,13 @@ get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
 	return arch_rebalance_pgtables(addr, len);
 }
 
+unsigned long
+get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
+		unsigned long pgoff, unsigned long flags)
+{
+	return get_unmapped_area_prot(file, addr, len, pgoff, flags, false);
+}
+
 EXPORT_SYMBOL(get_unmapped_area);
 
 /* Look up the first VMA which satisfies  addr < vm_end,  NULL if none. */
---

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [RFC] x86, mm: start mmap allocation for libs from low addresses
  2011-08-12 10:29 [RFC] x86, mm: start mmap allocation for libs from low addresses Vasiliy Kulikov
@ 2011-08-12 23:19 ` H. Peter Anvin
  2011-08-13  6:26   ` Vasiliy Kulikov
  2011-08-16  9:05   ` Vasiliy Kulikov
  0 siblings, 2 replies; 9+ messages in thread
From: H. Peter Anvin @ 2011-08-12 23:19 UTC (permalink / raw)
  To: Vasiliy Kulikov, Thomas Gleixner, Ingo Molnar
  Cc: kernel-hardening, Peter Zijlstra, Andrew Morton, x86,
	linux-kernel, linux-mm

Vasiliy Kulikov <segoon@openwall.com> wrote:

>This patch changes mmap base address allocator logic to incline to
>allocate addresses from the first 16 Mbs of address space.  These
>addresses start from zero byte (0x00AABBCC).  Using such addresses
>breaks ret2libc exploits abusing string buffer overflows.  As library
>addresses contain zero byte, addresses of library functions may not
>present in the string, which is used to overflow the buffer.  As a
>result, it makes it impossible to change the return address on the
>stack
>to the address of some library function (e.g. system(3)).
>
>The logic is applied to 32 bit tasks, both for 32 bit kernels and for
>32
>bit tasks running on 64 bit kernels.  64 bit tasks already have zero
>bytes in addresses of library functions.  Other architectures may reuse
>the logic.
>
>The first Mb is excluded from the range because of the compatibility
>with
>programs like Wine and Dosemu.
>
>If the sum of libraries sizes plus executable size doesn't exceed 15
>Mb,
>the only pages out of ASCII-protected range are VDSO and vsyscall.
>However, they don't provide enough material for obtaining arbitrary
>code
>execution and are not dangerous without using other executable pages.
>
>If 16 Mbs are over, we fallback to the old allocation algorithm.
>
>Without the patch:
>
>$ ldd /bin/ls
>	linux-gate.so.1 =>  (0xf779c000)
>        librt.so.1 => /lib/librt.so.1 (0xb7fcf000)
>        libtermcap.so.2 => /lib/libtermcap.so.2 (0xb7fca000)
>        libc.so.6 => /lib/libc.so.6 (0xb7eae000)
>        libpthread.so.0 => /lib/libpthread.so.0 (0xb7e5b000)
>        /lib/ld-linux.so.2 (0xb7fe6000)
>
>With the patch:
>
>$ ldd /bin/ls
>	linux-gate.so.1 =>  (0xf772a000)
>	librt.so.1 => /lib/librt.so.1 (0x0014a000)
>	libtermcap.so.2 => /lib/libtermcap.so.2 (0x0015e000)
>	libc.so.6 => /lib/libc.so.6 (0x00162000)
>	libpthread.so.0 => /lib/libpthread.so.0 (0x00283000)
>	/lib/ld-linux.so.2 (0x00131000)
>
>The same logic was used in -ow patch for 2.0-2.4 kernels and in
>exec-shield for 2.6.x kernels.  Parts of the code were taken from RHEL6
>version of exec-shield.
>
>Signed-off-by: Vasiliy Kulikov <segoon@openwall.com>
>--
> arch/x86/mm/mmap.c       |    5 +++
> include/linux/mm_types.h |    4 +++
> include/linux/sched.h    |    3 ++
>mm/mmap.c                |   66
>++++++++++++++++++++++++++++++++++++++++++---
> 4 files changed, 73 insertions(+), 5 deletions(-)
>
>diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c
>index 1dab519..0c82a94 100644
>--- a/arch/x86/mm/mmap.c
>+++ b/arch/x86/mm/mmap.c
>@@ -131,6 +131,11 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
> 	} else {
> 		mm->mmap_base = mmap_base();
> 		mm->get_unmapped_area = arch_get_unmapped_area_topdown;
>+		if (mmap_is_ia32()) {
>+			mm->get_unmapped_exec_area =
>+				arch_get_unmapped_exec_area;
>+			mm->lib_mmap_base = 0x00110000 + mmap_rnd();
>+		}
> 		mm->unmap_area = arch_unmap_area_topdown;
> 	}
> }
>diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
>index 027935c..5f2dca9 100644
>--- a/include/linux/mm_types.h
>+++ b/include/linux/mm_types.h
>@@ -225,9 +225,13 @@ struct mm_struct {
> 	unsigned long (*get_unmapped_area) (struct file *filp,
> 				unsigned long addr, unsigned long len,
> 				unsigned long pgoff, unsigned long flags);
>+	unsigned long (*get_unmapped_exec_area) (struct file *filp,
>+				unsigned long addr, unsigned long len,
>+				unsigned long pgoff, unsigned long flags);
> 	void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
> #endif
> 	unsigned long mmap_base;		/* base of mmap area */
>+	unsigned long lib_mmap_base;		/* base of mmap libraries area
>(includes zero symbol) */
> 	unsigned long task_size;		/* size of task vm space */
>	unsigned long cached_hole_size; 	/* if non-zero, the largest hole
>below free_area_cache */
>	unsigned long free_area_cache;		/* first hole of size cached_hole_size
>or larger */
>diff --git a/include/linux/sched.h b/include/linux/sched.h
>index f024c63..8feaba9 100644
>--- a/include/linux/sched.h
>+++ b/include/linux/sched.h
>@@ -394,6 +394,9 @@ arch_get_unmapped_area_topdown(struct file *filp,
>unsigned long addr,
> 			  unsigned long flags);
> extern void arch_unmap_area(struct mm_struct *, unsigned long);
>extern void arch_unmap_area_topdown(struct mm_struct *, unsigned long);
>+extern unsigned long
>+arch_get_unmapped_exec_area(struct file *, unsigned long,
>+		unsigned long, unsigned long, unsigned long);
> #else
> static inline void arch_pick_mmap_layout(struct mm_struct *mm) {}
> #endif
>diff --git a/mm/mmap.c b/mm/mmap.c
>index d49736f..489510c 100644
>--- a/mm/mmap.c
>+++ b/mm/mmap.c
>@@ -50,6 +50,10 @@ static void unmap_region(struct mm_struct *mm,
> 		struct vm_area_struct *vma, struct vm_area_struct *prev,
> 		unsigned long start, unsigned long end);
> 
>+static unsigned long
>+get_unmapped_area_prot(struct file *file, unsigned long addr, unsigned
>long len,
>+		unsigned long pgoff, unsigned long flags, bool exec);
>+
> /*
>* WARNING: the debugging will use recursive algorithms so never enable
>this
>  * unless you know what you are doing.
>@@ -989,7 +993,8 @@ unsigned long do_mmap_pgoff(struct file *file,
>unsigned long addr,
> 	/* Obtain the address to map to. we verify (or select) it and ensure
> 	 * that it represents a valid section of the address space.
> 	 */
>-	addr = get_unmapped_area(file, addr, len, pgoff, flags);
>+	addr = get_unmapped_area_prot(file, addr, len, pgoff, flags,
>+			prot & PROT_EXEC);
> 	if (addr & ~PAGE_MASK)
> 		return addr;
> 
>@@ -1528,6 +1533,46 @@ bottomup:
> }
> #endif
> 
>+unsigned long
>+arch_get_unmapped_exec_area(struct file *filp, unsigned long addr0,
>+		unsigned long len, unsigned long pgoff, unsigned long flags)
>+{
>+	unsigned long addr = addr0;
>+	struct mm_struct *mm = current->mm;
>+	struct vm_area_struct *vma;
>+
>+	if (len > TASK_SIZE)
>+		return -ENOMEM;
>+
>+	if (flags & MAP_FIXED)
>+		return addr;
>+
>+	/* We ALWAYS start from the beginning as base addresses
>+	 * with zero high bits is a valued resource */
>+	addr = max_t(unsigned long, mm->lib_mmap_base, mmap_min_addr);
>+
>+	for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
>+		/* At this point:  (!vma || addr < vma->vm_end). */
>+		if (TASK_SIZE - len < addr)
>+			return -ENOMEM;
>+
>+		/* We don't want to touch brk of not DYNAMIC elf binaries */
>+		if (mm->brk && addr > mm->brk)
>+			goto failed;
>+
>+		if (!vma || addr + len <= vma->vm_start)
>+			return addr;
>+
>+		addr = vma->vm_end;
>+		/* If 0x01000000 is touched, the algo gives up */
>+		if (addr >= 0x01000000)
>+			goto failed;
>+	}
>+
>+failed:
>+	return current->mm->get_unmapped_area(filp, addr0, len, pgoff,
>flags);
>+}
>+
> void arch_unmap_area_topdown(struct mm_struct *mm, unsigned long addr)
> {
> 	/*
>@@ -1541,9 +1586,9 @@ void arch_unmap_area_topdown(struct mm_struct
>*mm, unsigned long addr)
> 		mm->free_area_cache = mm->mmap_base;
> }
> 
>-unsigned long
>-get_unmapped_area(struct file *file, unsigned long addr, unsigned long
>len,
>-		unsigned long pgoff, unsigned long flags)
>+static unsigned long
>+get_unmapped_area_prot(struct file *file, unsigned long addr, unsigned
>long len,
>+		unsigned long pgoff, unsigned long flags, bool exec)
> {
> 	unsigned long (*get_area)(struct file *, unsigned long,
> 				  unsigned long, unsigned long, unsigned long);
>@@ -1556,7 +1601,11 @@ get_unmapped_area(struct file *file, unsigned
>long addr, unsigned long len,
> 	if (len > TASK_SIZE)
> 		return -ENOMEM;
> 
>-	get_area = current->mm->get_unmapped_area;
>+	if (exec && current->mm->get_unmapped_exec_area)
>+		get_area = current->mm->get_unmapped_exec_area;
>+	else
>+		get_area = current->mm->get_unmapped_area;
>+
> 	if (file && file->f_op && file->f_op->get_unmapped_area)
> 		get_area = file->f_op->get_unmapped_area;
> 	addr = get_area(file, addr, len, pgoff, flags);
>@@ -1571,6 +1620,13 @@ get_unmapped_area(struct file *file, unsigned
>long addr, unsigned long len,
> 	return arch_rebalance_pgtables(addr, len);
> }
> 
>+unsigned long
>+get_unmapped_area(struct file *file, unsigned long addr, unsigned long
>len,
>+		unsigned long pgoff, unsigned long flags)
>+{
>+	return get_unmapped_area_prot(file, addr, len, pgoff, flags, false);
>+}
>+
> EXPORT_SYMBOL(get_unmapped_area);
> 
>/* Look up the first VMA which satisfies  addr < vm_end,  NULL if none.
>*/
>---

This also greatly reduces the address space available for randomization, and may get in the way of the default brk.  Is this a net win or lose?  Also, this zero byte is going to be at the last address, which means it might not help.  How about addresses of the form 0xAA00B000 instead?  The last bits are always 000 for a page address, of course...
-- 
Sent from my mobile phone. Please excuse my brevity and lack of formatting.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC] x86, mm: start mmap allocation for libs from low addresses
  2011-08-12 23:19 ` H. Peter Anvin
@ 2011-08-13  6:26   ` Vasiliy Kulikov
  2011-08-16  9:05   ` Vasiliy Kulikov
  1 sibling, 0 replies; 9+ messages in thread
From: Vasiliy Kulikov @ 2011-08-13  6:26 UTC (permalink / raw)
  To: H. Peter Anvin
  Cc: Thomas Gleixner, Ingo Molnar, kernel-hardening, Peter Zijlstra,
	Andrew Morton, x86, linux-kernel, linux-mm

On Fri, Aug 12, 2011 at 18:19 -0500, H. Peter Anvin wrote:
> This also greatly reduces the address space available for randomization,
> and may get in the way of the default brk.  Is this a net win or lose?

If the executable image is not randomized and is located out of
ASCII-armor, then yes, such allocation doesn't help much.

>  Also, this zero byte is going to be at the last address, which means it might not help.  How about addresses of the form 0xAA00B000 instead?  The last bits are always 000 for a page address, of course...

It leaves only 64kb of library protected, which is useless for most of
programs.

Thanks,

-- 
Vasiliy Kulikov
http://www.openwall.com - bringing security into open computing environments

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC] x86, mm: start mmap allocation for libs from low addresses
  2011-08-12 23:19 ` H. Peter Anvin
  2011-08-13  6:26   ` Vasiliy Kulikov
@ 2011-08-16  9:05   ` Vasiliy Kulikov
  2011-08-22 10:17     ` Vasiliy Kulikov
  1 sibling, 1 reply; 9+ messages in thread
From: Vasiliy Kulikov @ 2011-08-16  9:05 UTC (permalink / raw)
  To: H. Peter Anvin
  Cc: Thomas Gleixner, Ingo Molnar, kernel-hardening, Peter Zijlstra,
	Andrew Morton, x86, linux-kernel, linux-mm

On Fri, Aug 12, 2011 at 18:19 -0500, H. Peter Anvin wrote:
> Vasiliy Kulikov <segoon@openwall.com> wrote:
> 
> >This patch changes mmap base address allocator logic to incline to
> >allocate addresses from the first 16 Mbs of address space.  These
> >addresses start from zero byte (0x00AABBCC).
...

To make it clear:

The VM space is not significantly reduced - an additional gap, which
is used for ASCII-protected region, is calculated the same way as the
common mmap gap is calculated.  The maximum size of the gap is 1MB for
the upstream kernel default ASLR entropy - a trifle IMO.

If the new allocator fails to find appropriate vma in the protected
zone, the old one tries to do the job.  So, no visible changes for
userspace.


As to the benefit:

1) For small PIE programs, which don't use much libraries, all
executable regions are moved to the protected zone.

2) For non-PIE programs if image starts from 0x00AABBCC address and fits
into the zone the same rule of small libs applies.

3) For non-PIE programs with images above 0x01000000 and/or programs
with much libraries some code sections are outsize of the protected region.

The protection works for (1) and (2) programs.  It doesn't work for (3).


(1) is not too seldom.  Programs, which need such protection (network
daemons, programs parsing untrusted input, etc.), are usually small
enough.  In our distro, Openwall GNU/*/Linux, almost all daemon programs
fit into the region.

As the changes are not intrusive, we'd want to see this feature in the
upstream kernel.  If you know why the patch cannot be a part of the
upstream kernel - please tell me, I'll try to address the issues.

Thanks,

-- 
Vasiliy Kulikov
http://www.openwall.com - bringing security into open computing environments

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC] x86, mm: start mmap allocation for libs from low addresses
  2011-08-16  9:05   ` Vasiliy Kulikov
@ 2011-08-22 10:17     ` Vasiliy Kulikov
  2011-08-22 17:24       ` H. Peter Anvin
  0 siblings, 1 reply; 9+ messages in thread
From: Vasiliy Kulikov @ 2011-08-22 10:17 UTC (permalink / raw)
  To: H. Peter Anvin
  Cc: Thomas Gleixner, Ingo Molnar, kernel-hardening, Peter Zijlstra,
	Andrew Morton, x86, linux-kernel, linux-mm

Hi Ingo, Peter, Thomas,

On Tue, Aug 16, 2011 at 13:05 +0400, Vasiliy Kulikov wrote:
> As the changes are not intrusive, we'd want to see this feature in the
> upstream kernel.  If you know why the patch cannot be a part of the
> upstream kernel - please tell me, I'll try to address the issues.

Any comments on the RFC?  Otherwise, may I resend it as a PATCH for
inclusion?

Thanks!

-- 
Vasiliy Kulikov
http://www.openwall.com - bringing security into open computing environments

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC] x86, mm: start mmap allocation for libs from low addresses
  2011-08-22 10:17     ` Vasiliy Kulikov
@ 2011-08-22 17:24       ` H. Peter Anvin
  2011-08-22 20:14         ` Vasiliy Kulikov
  0 siblings, 1 reply; 9+ messages in thread
From: H. Peter Anvin @ 2011-08-22 17:24 UTC (permalink / raw)
  To: Vasiliy Kulikov
  Cc: Thomas Gleixner, Ingo Molnar, kernel-hardening, Peter Zijlstra,
	Andrew Morton, x86, linux-kernel, linux-mm

On 08/22/2011 03:17 AM, Vasiliy Kulikov wrote:
> Hi Ingo, Peter, Thomas,
> 
> On Tue, Aug 16, 2011 at 13:05 +0400, Vasiliy Kulikov wrote:
>> As the changes are not intrusive, we'd want to see this feature in the
>> upstream kernel.  If you know why the patch cannot be a part of the
>> upstream kernel - please tell me, I'll try to address the issues.
> 
> Any comments on the RFC?  Otherwise, may I resend it as a PATCH for
> inclusion?
> 
> Thanks!

Conceptually:

I also have to admit to being somewhat skeptical to the concept on a
littleendian architecture like x86.

Code-wise:

The code is horrific; it is full of open-coded magic numbers; it also
puts a function called arch_get_unmapped_exec_area() in a generic file,
which could best be described as "WTF" -- the arch_ prefix we use
specifically to denote a per-architecture hook function.

As such, your claim that the changes are not intrusive is plain false.

	-hpa

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC] x86, mm: start mmap allocation for libs from low addresses
  2011-08-22 17:24       ` H. Peter Anvin
@ 2011-08-22 20:14         ` Vasiliy Kulikov
  2011-08-22 20:17           ` H. Peter Anvin
  0 siblings, 1 reply; 9+ messages in thread
From: Vasiliy Kulikov @ 2011-08-22 20:14 UTC (permalink / raw)
  To: H. Peter Anvin
  Cc: Thomas Gleixner, Ingo Molnar, kernel-hardening, Peter Zijlstra,
	Andrew Morton, x86, linux-kernel, linux-mm

On Mon, Aug 22, 2011 at 10:24 -0700, H. Peter Anvin wrote:
> Conceptually:
> 
> I also have to admit to being somewhat skeptical to the concept on a
> littleendian architecture like x86.

Sorry, I was too short at my statement.  This is a quote from Solar
Designer:

"Requiring NUL as the most significant byte of a 32-bit address achieves
two things:

1. The overflow length has to be inferred/guessed exactly, because only
one NUL may be written.  Simply using a repeated pattern (with function
address and arguments) no longer works.

2. Passing function arguments in the straightforward manner no longer
works, because copying stops after the NUL.  The attacker's best bet may
be to find an entry point not at function boundary that sets registers
and then proceeds with or branches to the desired library code.  The
easiest way to set registers and branch would be a function epilogue -
pop/pop/.../ret - but then there's the difficulty in passing the address
to ret to (we have just one NUL and we've already used it to get to this
code).  Similarly, even via such pop's we can't pass an argument that
contains a NUL in it - e.g., the address of "/bin/sh" in libc (it
contains a NUL most significant byte too) or a zero value for root's
uid.  A possible bypass is via multiple overflows - if the overflow may
be triggered more than once before the vulnerable function returns, then
multiple NULs may be written, exactly one per overflow.  But this is
hopefully relatively rare."

I'll extend the patch description to explain the motivation more
clearly.


> Code-wise:
> 
> The code is horrific; it is full of open-coded magic numbers;

Agreed, the magic needs macro definition and comments.

> it also
> puts a function called arch_get_unmapped_exec_area() in a generic file,
> which could best be described as "WTF" -- the arch_ prefix we use
> specifically to denote a per-architecture hook function.

Agreed.  But I'd want to leave it in mm/mmap.c as it's likely be used by
other archs - the changes are bitness specific, not arch specific.  Is
it OK if I do this?

#ifndef HAVE_ARCH_UNMAPPED_EXEC_AREA
void *arch_get_unmapped_exec_area(...)
{
    ...
}
#endif


Thank you for the review!

-- 
Vasiliy Kulikov
http://www.openwall.com - bringing security into open computing environments

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC] x86, mm: start mmap allocation for libs from low addresses
  2011-08-22 20:14         ` Vasiliy Kulikov
@ 2011-08-22 20:17           ` H. Peter Anvin
  2011-08-23  6:41             ` Vasiliy Kulikov
  0 siblings, 1 reply; 9+ messages in thread
From: H. Peter Anvin @ 2011-08-22 20:17 UTC (permalink / raw)
  To: Vasiliy Kulikov
  Cc: Thomas Gleixner, Ingo Molnar, kernel-hardening, Peter Zijlstra,
	Andrew Morton, x86, linux-kernel, linux-mm

On 08/22/2011 01:14 PM, Vasiliy Kulikov wrote:
> 
>> Code-wise:
>>
>> The code is horrific; it is full of open-coded magic numbers;
> 
> Agreed, the magic needs macro definition and comments.
> 
>> it also
>> puts a function called arch_get_unmapped_exec_area() in a generic file,
>> which could best be described as "WTF" -- the arch_ prefix we use
>> specifically to denote a per-architecture hook function.
> 
> Agreed.  But I'd want to leave it in mm/mmap.c as it's likely be used by
> other archs - the changes are bitness specific, not arch specific.  Is
> it OK if I do this?
> 
> #ifndef HAVE_ARCH_UNMAPPED_EXEC_AREA
> void *arch_get_unmapped_exec_area(...)
> {
>     ...
> }
> #endif
> 

Only if this is really an architecture-specific function overridden in
specific architectures.  I'm not so sure that applies here.
Furthermore, I'm not even all that sure what this function *does*.

	-hpa

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [RFC] x86, mm: start mmap allocation for libs from low addresses
  2011-08-22 20:17           ` H. Peter Anvin
@ 2011-08-23  6:41             ` Vasiliy Kulikov
  0 siblings, 0 replies; 9+ messages in thread
From: Vasiliy Kulikov @ 2011-08-23  6:41 UTC (permalink / raw)
  To: H. Peter Anvin
  Cc: Thomas Gleixner, Ingo Molnar, kernel-hardening, Peter Zijlstra,
	Andrew Morton, x86, linux-kernel, linux-mm

On Mon, Aug 22, 2011 at 13:17 -0700, H. Peter Anvin wrote:
> On 08/22/2011 01:14 PM, Vasiliy Kulikov wrote:
> > 
> >> Code-wise:
> >>
> >> The code is horrific; it is full of open-coded magic numbers;
> > 
> > Agreed, the magic needs macro definition and comments.
> > 
> >> it also
> >> puts a function called arch_get_unmapped_exec_area() in a generic file,
> >> which could best be described as "WTF" -- the arch_ prefix we use
> >> specifically to denote a per-architecture hook function.
> > 
> > Agreed.  But I'd want to leave it in mm/mmap.c as it's likely be used by
> > other archs - the changes are bitness specific, not arch specific.  Is
> > it OK if I do this?
> > 
> > #ifndef HAVE_ARCH_UNMAPPED_EXEC_AREA
> > void *arch_get_unmapped_exec_area(...)
> > {
> >     ...
> > }
> > #endif
> > 
> 
> Only if this is really an architecture-specific function overridden in
> specific architectures.  I'm not so sure that applies here.

It is a more or less generic allocator.  Arch specific constants will be
moved to arch headers, so it will be a 32-bit specific function, not
arch specific (64 bit architectures don't need ASCII shield at all as
mmap addresses already contain a zero byte).  It will not be overriden
by x86 as it is "enough generic" for x86.

I've defined it as arch_* looking at other allocator implementations.
All of them are arch_* and are located in mm/mmap.c with the ability to
override them in architecture specific files.  Probably nobody will
override it, but I tried to make it consistent with the existing code.
If this HAVE_ARCH_*/arch_* logic is not suitable for exec_area, I'll
remove arch_ prefix.


> Furthermore, I'm not even all that sure what this function *does*.

This is a bottom-up allocator, which tries to reuse all holes in the
ASCII-protected region.  It differs from arch_get_unmapped_area() in the
priority of the first 16 Mb - arch_get_unmapped_area() tries to walk
through all vmas in the whole VM space, arch_get_unmapped_exec_area()
tries to reuse all memory from the first 16 Mb and only then allocating
arbitrary addressed by fallbacking to the default allocator (top down in
case of x86).

I'll add the comment for the allocator.

Thank you,

-- 
Vasiliy Kulikov
http://www.openwall.com - bringing security into open computing environments

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org.  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Fight unfair telecom internet charges in Canada: sign http://stopthemeter.ca/
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2011-08-23  6:41 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-08-12 10:29 [RFC] x86, mm: start mmap allocation for libs from low addresses Vasiliy Kulikov
2011-08-12 23:19 ` H. Peter Anvin
2011-08-13  6:26   ` Vasiliy Kulikov
2011-08-16  9:05   ` Vasiliy Kulikov
2011-08-22 10:17     ` Vasiliy Kulikov
2011-08-22 17:24       ` H. Peter Anvin
2011-08-22 20:14         ` Vasiliy Kulikov
2011-08-22 20:17           ` H. Peter Anvin
2011-08-23  6:41             ` Vasiliy Kulikov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).