public inbox for kernel-hardening@lists.openwall.com
 help / color / mirror / Atom feed
* [kernel-hardening] base address for shared libs
@ 2011-07-23 16:22 Solar Designer
  2011-07-24  8:51 ` Vasiliy Kulikov
                   ` (2 more replies)
  0 siblings, 3 replies; 16+ messages in thread
From: Solar Designer @ 2011-07-23 16:22 UTC (permalink / raw)
  To: kernel-hardening

Vasiliy,

At least on rhel5/openvz kernels, 32-bit processes get their shared libs
loaded at different kinds of addresses on i686 vs. x86_64 kernels.

Here's an example:

32-bit kernel and userland (OpenVZ container):

$ ldd /bin/ls
        librt.so.1 => /lib/librt.so.1 (0x00a99000)
        libtermcap.so.2 => /lib/libtermcap.so.2 (0x00c1a000)
        libc.so.6 => /lib/libc.so.6 (0x0014d000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x00617000)
        /lib/ld-linux.so.2 (0x0012e000)

64-bit kernel, 32-bit userland (OpenVZ container):

$ ldd /bin/ls
        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)

Notice how the 32-bit kernel produces addresses that are safer against
attacks via C strings (contain NULs).  This is the approach I used in
-ow patches (using 0x00110000 as the base address, considering vm86
needs for the first 1 MB + 64 KB).  I'd like 64-bit kernels to do the
same when running 32-bit binaries.

Can you please look into this and likely fix it for mainline, as well as
for rhel6/openvz when we're ready to move to those kernels?  A fix for
rhel5/openvz would also be welcome if it's easy to do.

Thanks,

Alexander

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

* Re: [kernel-hardening] base address for shared libs
  2011-07-23 16:22 [kernel-hardening] base address for shared libs Solar Designer
@ 2011-07-24  8:51 ` Vasiliy Kulikov
  2011-07-24 14:27   ` Solar Designer
  2011-07-29  9:27 ` Vasiliy Kulikov
  2011-07-30 18:38 ` Vasiliy Kulikov
  2 siblings, 1 reply; 16+ messages in thread
From: Vasiliy Kulikov @ 2011-07-24  8:51 UTC (permalink / raw)
  To: kernel-hardening

Solar,

On Sat, Jul 23, 2011 at 20:22 +0400, Solar Designer wrote:
> At least on rhel5/openvz kernels, 32-bit processes get their shared libs
> loaded at different kinds of addresses on i686 vs. x86_64 kernels.
[...]
> Can you please look into this and likely fix it for mainline, as well as
> for rhel6/openvz when we're ready to move to those kernels?  A fix for
> rhel5/openvz would also be welcome if it's easy to do.

I'll look into it.  However, I don't know whether upstream is OK with
force zeroing high order byte of libs address and artificially limiting
effective task's vm size.  If not, it's probably should be made
configurable via kernel.randomize_va_space sysctl.

Thanks,

-- 
Vasiliy

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

* Re: [kernel-hardening] base address for shared libs
  2011-07-24  8:51 ` Vasiliy Kulikov
@ 2011-07-24 14:27   ` Solar Designer
  2011-07-24 18:18     ` Vasiliy Kulikov
  2011-07-25 19:20     ` Vasiliy Kulikov
  0 siblings, 2 replies; 16+ messages in thread
From: Solar Designer @ 2011-07-24 14:27 UTC (permalink / raw)
  To: kernel-hardening

Vasiliy, all -

On Sun, Jul 24, 2011 at 12:51:42PM +0400, Vasiliy Kulikov wrote:
> On Sat, Jul 23, 2011 at 20:22 +0400, Solar Designer wrote:
> > At least on rhel5/openvz kernels, 32-bit processes get their shared libs
> > loaded at different kinds of addresses on i686 vs. x86_64 kernels.
> [...]
> > Can you please look into this and likely fix it for mainline, as well as
> > for rhel6/openvz when we're ready to move to those kernels?  A fix for
> > rhel5/openvz would also be welcome if it's easy to do.
> 
> I'll look into it.  However, I don't know whether upstream is OK with
> force zeroing high order byte of libs address and artificially limiting
> effective task's vm size.  If not, it's probably should be made
> configurable via kernel.randomize_va_space sysctl.

I think you misunderstood me.  I don't suggest forcing the high order
byte to be zero; I merely suggest that the starting address should be
0x00110000.  If the combined size of shared libs plus the random offset
exceed 15 MB - 64 KB, then some addresses won't have their high order
byte at NUL, and that's life.  I think this is what happens on i686
kernels now, and I'd like the same to be happening with x86_64 kernels
running 32-bit programs.

I think something like 8 MB may be used for randomization (11 bits at
4 KB page size), which leaves almost 7 MB for shared libs until we no
longer have NULs on some of their addresses.  So programs with
relatively few shared libs will benefit not only from randomization, but
also from the NULs.

A drawback is that we'd be unnecessarily limiting the amount of
randomization for programs that have lots of shared libs (way beyond 7 MB).

Or maybe we should use 4 MB for randomization (10 bits), leaving almost
11 MB for NUL-protected shared libs.

Oh, when vm86 is disallowed, I think we can start at mmap_min_addr,
which gives us an extra megabyte (if mmap_min_addr is relatively low,
like 96 KB, which is what we use on Owl).  Similarly, when mmap_min_addr
is not 0 and we're not running with CAP_SYS_RAWIO, perhaps we can start
at mmap_min_addr because some low addresses would not be available to
the process anyway (even if it wanted to use them to run DOS).

I'd appreciate comments and other opinions on all of the above.

What does PaX do here?

Thanks,

Alexander

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

* Re: [kernel-hardening] base address for shared libs
  2011-07-24 14:27   ` Solar Designer
@ 2011-07-24 18:18     ` Vasiliy Kulikov
  2011-07-25 19:20     ` Vasiliy Kulikov
  1 sibling, 0 replies; 16+ messages in thread
From: Vasiliy Kulikov @ 2011-07-24 18:18 UTC (permalink / raw)
  To: kernel-hardening

Solar,

On Sun, Jul 24, 2011 at 18:27 +0400, Solar Designer wrote:
> On Sun, Jul 24, 2011 at 12:51:42PM +0400, Vasiliy Kulikov wrote:
> > On Sat, Jul 23, 2011 at 20:22 +0400, Solar Designer wrote:
> > > At least on rhel5/openvz kernels, 32-bit processes get their shared libs
> > > loaded at different kinds of addresses on i686 vs. x86_64 kernels.
> > [...]
> > > Can you please look into this and likely fix it for mainline, as well as
> > > for rhel6/openvz when we're ready to move to those kernels?  A fix for
> > > rhel5/openvz would also be welcome if it's easy to do.
> > 
> > I'll look into it.  However, I don't know whether upstream is OK with
> > force zeroing high order byte of libs address and artificially limiting
> > effective task's vm size.  If not, it's probably should be made
> > configurable via kernel.randomize_va_space sysctl.
> 
> I think you misunderstood me.  I don't suggest forcing the high order
> byte to be zero; I merely suggest that the starting address should be
> 0x00110000.

Ah, sure.  Best effort vs. enforcement.


> Oh, when vm86 is disallowed,

BTW, vm86 support is not even compiled on x86-64, even for x86-32
compatibility:

config VM86
	bool "Enable VM86 support" if EXPERT
	default y
	depends on X86_32


> What does PaX do here?

I didn't hear PaX does some NUL protection of lib addresses.  I'll look
into this.

Thanks,

-- 
Vasiliy

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

* Re: [kernel-hardening] base address for shared libs
  2011-07-24 14:27   ` Solar Designer
  2011-07-24 18:18     ` Vasiliy Kulikov
@ 2011-07-25 19:20     ` Vasiliy Kulikov
  2011-08-11  8:32       ` Vasiliy Kulikov
  1 sibling, 1 reply; 16+ messages in thread
From: Vasiliy Kulikov @ 2011-07-25 19:20 UTC (permalink / raw)
  To: kernel-hardening

Solar,

On Sun, Jul 24, 2011 at 18:27 +0400, Solar Designer wrote:
> What does PaX do here?

Spender's reaction: 

"The PaX position on the null bytes in mmap addresses is: the tradeoff
between stopping certain vulns involving string functions and having low
entropy for every other vuln type, and having the same high entropy for
all types is weighed in favor of the latter."

-- 
Vasiliy

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

* Re: [kernel-hardening] base address for shared libs
  2011-07-23 16:22 [kernel-hardening] base address for shared libs Solar Designer
  2011-07-24  8:51 ` Vasiliy Kulikov
@ 2011-07-29  9:27 ` Vasiliy Kulikov
  2011-07-30 18:38 ` Vasiliy Kulikov
  2 siblings, 0 replies; 16+ messages in thread
From: Vasiliy Kulikov @ 2011-07-29  9:27 UTC (permalink / raw)
  To: kernel-hardening

Solar,

On Sat, Jul 23, 2011 at 20:22 +0400, Solar Designer wrote:
> At least on rhel5/openvz kernels, 32-bit processes get their shared libs
> loaded at different kinds of addresses on i686 vs. x86_64 kernels.

Looking into RHEL6 kernel:

    void arch_pick_mmap_layout(struct mm_struct *mm)
    {
        ...
            if (!(current->personality & READ_IMPLIES_EXEC)
                && mmap_is_ia32())
                mm->get_unmapped_exec_area = arch_get_unmapped_exec_area;
        ...
    }

    #define SHLIB_BASE	0x00110000

    unsigned long
    arch_get_unmapped_exec_area(struct file *filp, unsigned long addr0,
            unsigned long len0, unsigned long pgoff, unsigned long flags)
    {
        ...
        if (!addr)
            addr = !should_randomize() ? SHLIB_BASE :
                randomize_range(SHLIB_BASE, 0x01000000, len);
        ...
    }

Looks like it is considered as a way to easily mmap libraries in CS
limited area by exec-shield, and not as a C-string barrier.


The comment says the common bottom-up doesn't support randomization:

    /*
     * Bottom-up (legacy) layout on X86_32 did not support randomization, X86_64
     * does, but not when emulating X86_32
     */

So, IMO the bottom-up layout allocator should be patched.


Thanks,

-- 
Vasiliy

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

* Re: [kernel-hardening] base address for shared libs
  2011-07-23 16:22 [kernel-hardening] base address for shared libs Solar Designer
  2011-07-24  8:51 ` Vasiliy Kulikov
  2011-07-29  9:27 ` Vasiliy Kulikov
@ 2011-07-30 18:38 ` Vasiliy Kulikov
  2011-07-30 18:43   ` Vasiliy Kulikov
  2 siblings, 1 reply; 16+ messages in thread
From: Vasiliy Kulikov @ 2011-07-30 18:38 UTC (permalink / raw)
  To: kernel-hardening

Solar,

This is a patch, which should solve the problem.  Note that the default
base address allocation policy for the mainline is top-down, so
0x00110000 was not considered even in x86-32.  Now it should work for
both 32-bit systems and 32-bit tasks in 64-bit systems.

I used some code from Exec Shield part of RHEL6 patch.


Shortly:

By default the kernel calculates a random gap size (8 bits of entropy
for 32 bits and 28 for 64 bits) and skips this size from the top
addresses.  Then it merely fills the address space (top-down) with
libraries without any gaps between the libraries.

In Exec Shield the base address for each library is a random value from
0x00110000-0x01000000.  If it fails, a simple bottom-up algo is used,
whitout any gaps AND any random.  IMO it is weird as for some specific
library sequences the base addresses are somewhat guessable (I haven't
got strict numbers, though).

So, I've implemented an analog of upstream's top-down allocation algo,
but bottom-up, with the same gap (with the same entropy).


diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c
index 1dab519..20d6085 100644
--- a/arch/x86/mm/mmap.c
+++ b/arch/x86/mm/mmap.c
@@ -131,6 +131,10 @@ 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..3e39165 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,49 @@ 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;
+
+	pr_err("mmap (pid = %lu): addr = %p, len = %lu\n", (long)current->pid, (void *)addr, (long)len);
+
+	/* We ALWAYS start from the beginning as base addresses
+	 * with zero high bits is a valued resource */
+	addr = mm->lib_mmap_base;
+
+	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 +1589,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 +1604,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 +1623,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. */
--

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

* Re: [kernel-hardening] base address for shared libs
  2011-07-30 18:38 ` Vasiliy Kulikov
@ 2011-07-30 18:43   ` Vasiliy Kulikov
  0 siblings, 0 replies; 16+ messages in thread
From: Vasiliy Kulikov @ 2011-07-30 18:43 UTC (permalink / raw)
  To: kernel-hardening

Solar,

On Sat, Jul 30, 2011 at 22:38 +0400, Vasiliy Kulikov wrote:
> This is a patch, which should solve the problem.  Note that the default
> base address allocation policy for the mainline is top-down, so
> 0x00110000 was not considered even in x86-32.  Now it should work for
> both 32-bit systems and 32-bit tasks in 64-bit systems.

One note: if watch for ldd output, approx. every 20th output shows that
some library gets 0xffAABBCC address.  If watch for the real task's maps
(via /proc/pid/maps), all libs are located before 0x01AABBCC.

I don't know for sure how ldd allocated memory, but I suspect it wastes
too much 0x00AABBCC addresses, so sometimes there is no place for them.

So, I don't think it is an issue.

Thanks,

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

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

* Re: [kernel-hardening] base address for shared libs
  2011-07-25 19:20     ` Vasiliy Kulikov
@ 2011-08-11  8:32       ` Vasiliy Kulikov
  2011-08-12  3:57         ` Solar Designer
  0 siblings, 1 reply; 16+ messages in thread
From: Vasiliy Kulikov @ 2011-08-11  8:32 UTC (permalink / raw)
  To: kernel-hardening

Solar,

The problem with ASCII-armor is that it contradicts good ALSR entropy.
E.g. with 12 bits of entropy the maximum library size to fit in low 16
MB is zero bytes.

PaX has 16 bits of entropy, so ASCII-armor would only hurt entropy.

Ubuntu (and most of distros) has 12 bits.  Kees says it makes sense to
enable ASCII-armor for exec-shield only, as e-s already has very poor
entropy.

Upstream has 8 bits for some reason, but AFAICS every distro increase
the limit.


It makes sense to have e.g. 10 bits of entropy for 32-bit environments,
but it would help only for tiny programs (hopefully, most of Owl
programs).  It would not work for any python/GUI/Java, Apache with many
modules, TomCat, etc.

pipacs' position is that exploitation of C-string buffer overflows is
rather rare nowadays, and heap overflows, buffer overflows, math
miscalculation, etc. are much more often => C-string buffer overflow is
not something which worth specific protection.


IMO it makes sense for Owl to have e.g. 10 bits of entropy for libs and
use ASCII-armor.  But it makes very little sense for upstream.  Or even
use 16 bits as PaX does and don't use ASCII-armor at all.  ASCII-armor
has rather limited usage - prevention of ret2libc via exploitation of
C-string buffer overflows.  But it is already included in ASLR, which
does much more.  Probably we should concentrate on more generic things,
like ASLR?

Solar, what do you think?


Thanks,

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

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

* Re: [kernel-hardening] base address for shared libs
  2011-08-11  8:32       ` Vasiliy Kulikov
@ 2011-08-12  3:57         ` Solar Designer
  2011-08-12  4:21           ` Solar Designer
  0 siblings, 1 reply; 16+ messages in thread
From: Solar Designer @ 2011-08-12  3:57 UTC (permalink / raw)
  To: kernel-hardening

Vasiliy,

Thank you for conducting and summarizing this research.  It helps.  Most
of the things I was aware of, but a few I was not (specific entropy
sizes of different kernels).

On Thu, Aug 11, 2011 at 12:32:59PM +0400, Vasiliy Kulikov wrote:
> The problem with ASCII-armor is that it contradicts good ALSR entropy.
> E.g. with 12 bits of entropy the maximum library size to fit in low 16
> MB is zero bytes.

Yes, so we have to choose between 10 and 11.

> PaX has 16 bits of entropy, so ASCII-armor would only hurt entropy.

Yes, there's a tradeoff here.

> It makes sense to have e.g. 10 bits of entropy for 32-bit environments,
> but it would help only for tiny programs (hopefully, most of Owl
> programs).  It would not work for any python/GUI/Java, Apache with many
> modules, TomCat, etc.

For programs with many/large libs, we would not get ASCII-armor for some
of the libs, but we would still have our 10 bits of ASLR.  So we'd lose
at the tradeoff - having sold some entropy, but having received only
partial ASCII-armor - but we would not completely leave those programs
unprotected.

Arguably, in those large-app cases mostly remote attacks are relevant, and
for them 10 bits is already somewhat difficult.  It is not obvious
whether there's a lot of difference between 10 and 16 bits for them or not.
If they're crashing from the first attempt or otherwise make the attack
easy to notice, then 10 bits may be enough.  If they don't crash and
don't show any misbehavior, nor logging, then 16 may not be enough.

Also, 16 bits means that libs are scattered over a 256 MB range, which
reduces the maximum continuous allocation size accordingly.  I think many
(upstream, RHEL) would find this cost prohibitive and would go for less
entropy anyway.

Arguably, ASCII-armor is more effective than entropy against some local
attacks.  16 bits is easy to search locally anyway (well, not exactly
"search 16 bits" - rather, you have a 63% chance of success in 65536
tries, but it's good enough for our discussion).  However, if you have
no read access to the program binary (say, mode 4711), but you do to the
libraries (say, mode 755), then ret2libc may be the most reliable attack
to use.

> pipacs' position is that exploitation of C-string buffer overflows is
> rather rare nowadays, and heap overflows, buffer overflows, math
> miscalculation, etc. are much more often => C-string buffer overflow is
> not something which worth specific protection.

I do feel that overflows via C strings became relatively less common,
yet they're still of some relevance.

Blocking further attempts to run a program binary after SIGSEGV or not
doing it is also part of the tradeoff.  With this in place, local
attacks on ASLR may become ineffective - however, 10 bits would likely
be enough then.

> IMO it makes sense for Owl to have e.g. 10 bits of entropy for libs and
> use ASCII-armor.  But it makes very little sense for upstream.

I think it makes sense for upstream because we have little chance to get
upstream to accept higher entropy (lowering the maximum continuous
allocation size).

> Or even use 16 bits as PaX does and don't use ASCII-armor at all.

I think it'd be great to have this as an option.  In fact, the code
could simply try to do ASCII-armor, but also allow one to configure more
than 11 bits of entropy, in which case the ASCII-armor would be usually
ineffective.  That is, start at max(0x00110000, mmap_min_addr), but add
to this a random page-aligned offset in a configurable range
(configurable as number of entropy bits).  When the setting is 11 or
less (and mmap_min_addr is not set unusually high), ASCII-armor is
guaranteed for libs not exceeding a certain size.  When the setting is
12 to 14, ASCII-armor is not guaranteed in any case (but may happen in
some cases nevertheless).  For 15+, you have to use a different starting
address or use two ranges (if the random number has no "1" bits beyond
bits 0-13, then use the low range, otherwise use a high range).

What specific address range(s) does PaX use?

> ASCII-armor
> has rather limited usage - prevention of ret2libc via exploitation of
> C-string buffer overflows.

Yes.

> But it is already included in ASLR, which does much more.

Is it?  Not exactly.  Just invoke the binary thousands of time, and you
have a good chance to hit the right libc code.

> Probably we should concentrate on more generic things, like ASLR?

I'd like us to support both.  Sounds easy enough to do.

Thanks again,

Alexander

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

* Re: [kernel-hardening] base address for shared libs
  2011-08-12  3:57         ` Solar Designer
@ 2011-08-12  4:21           ` Solar Designer
  2011-08-12  8:20             ` Vasiliy Kulikov
  0 siblings, 1 reply; 16+ messages in thread
From: Solar Designer @ 2011-08-12  4:21 UTC (permalink / raw)
  To: kernel-hardening

Vasiliy,

On Fri, Aug 12, 2011 at 07:57:29AM +0400, Solar Designer wrote:
> On Thu, Aug 11, 2011 at 12:32:59PM +0400, Vasiliy Kulikov wrote:
> > Or even use 16 bits as PaX does and don't use ASCII-armor at all.
> 
> I think it'd be great to have this as an option.  In fact, the code
> could simply try to do ASCII-armor, but also allow one to configure more
> than 11 bits of entropy, in which case the ASCII-armor would be usually
> ineffective.  That is, start at max(0x00110000, mmap_min_addr), but add
> to this a random page-aligned offset in a configurable range
> (configurable as number of entropy bits).  When the setting is 11 or
> less (and mmap_min_addr is not set unusually high), ASCII-armor is
> guaranteed for libs not exceeding a certain size.  When the setting is
> 12 to 14, ASCII-armor is not guaranteed in any case (but may happen in
> some cases nevertheless).  For 15+, you have to use a different starting
> address or use two ranges (if the random number has no "1" bits beyond
> bits 0-13, then use the low range, otherwise use a high range).

I think this should be configurable in the 0 to 19 bits range.  The
default might be 15, which would mostly fit under the starting address
of non-PIE binaries, taking up only a megabyte above them (you'd need to
use two ranges - 127 MB and 1 MB).  So the cost in reduction of maximum
continuous allocation size would be negligible for non-PIE (just 1 MB).

The starting address of 0x00110000 could also be configurable.  When
dosemu and Win16 apps in Wine are not needed (are these the users of
this range?), you could simply start at mmap_min_addr and fit all 128 MB
before the binary start.

Alexander

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

* Re: [kernel-hardening] base address for shared libs
  2011-08-12  4:21           ` Solar Designer
@ 2011-08-12  8:20             ` Vasiliy Kulikov
  2011-08-12  9:20               ` Solar Designer
  0 siblings, 1 reply; 16+ messages in thread
From: Vasiliy Kulikov @ 2011-08-12  8:20 UTC (permalink / raw)
  To: kernel-hardening

Solar,

On Fri, Aug 12, 2011 at 08:21 +0400, Solar Designer wrote:
[...]
> I think this should be configurable in the 0 to 19 bits range.
[...]

Yes, I agree this is a trade-off and depends on program needs (address
space size) and security threads.

However, some upstream guys don't agree it should be configurable:

https://lkml.org/lkml/2006/5/19/219

https://lkml.org/lkml/2006/5/22/207:

"Because if it is configurable, someone _will_ configure it wrong, and
then ask us why it does not work."

And similar.


Probably it worth trying to bring up the discussion of configurable ASLR
entropy again - the code to configure it is simple anyway.  However, I
expect one nasty answer: "everybody should use x86-64 for good ASLR and
other things, for x86-32 it is bad anyway, so don't bother to fix things
broken by design."


So, to summarize:

For upstream we want to start mmap addresses allocation from 0x1100000,
bottom up.  Probably, make entropy configurable.

For Owl we want to make entropy size configurable.  Depending on the
entropy, use ASCII-armor or fallback to the default allocator
instantly.

Correct?

-- 
Vasiliy

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

* Re: [kernel-hardening] base address for shared libs
  2011-08-12  8:20             ` Vasiliy Kulikov
@ 2011-08-12  9:20               ` Solar Designer
  2011-08-12  9:52                 ` Vasiliy Kulikov
  0 siblings, 1 reply; 16+ messages in thread
From: Solar Designer @ 2011-08-12  9:20 UTC (permalink / raw)
  To: kernel-hardening

On Fri, Aug 12, 2011 at 12:20:24PM +0400, Vasiliy Kulikov wrote:
> However, some upstream guys don't agree it should be configurable:
> 
> https://lkml.org/lkml/2006/5/19/219
> 
> https://lkml.org/lkml/2006/5/22/207:
> 
> "Because if it is configurable, someone _will_ configure it wrong, and
> then ask us why it does not work."

This is easily dealt with by limiting the allowable range to "correct"
values.  Say, instead of 0 to 19 use 9 to 18 or 10 to 16.  Then we'll
need to patch only the allowable range and not any code in Owl.

> Probably it worth trying to bring up the discussion of configurable ASLR
> entropy again - the code to configure it is simple anyway.

Yes, please - with a patch.

> However, I
> expect one nasty answer: "everybody should use x86-64 for good ASLR and
> other things, for x86-32 it is bad anyway, so don't bother to fix things
> broken by design."

You may simply reply that you disagree.  Maybe someone else will as well.

> So, to summarize:
> 
> For upstream we want to start mmap addresses allocation from 0x1100000,

You meant from 0x110000 (one zero less).

> bottom up.

Huh?  I don't think you used the right words here.

> Probably, make entropy configurable.

Yes.

> For Owl we want to make entropy size configurable.  Depending on the
> entropy, use ASCII-armor or fallback to the default allocator
> instantly.

Not exactly.  Both for upstream and for Owl, when the entropy size
exceeds what we can provide ASCII-armor for, we start at 0x110000
anyway, but we just happen to go to non-armored addresses if we get such
random numbers.  For example, if we're configured to use 12 bits and our
binary uses just one library of 3 MB in size, then there's an approx.
75% chance that on a given invocation of the binary we have ASCII armor
for the library anyway.  This is just not guaranteed.

Alexander

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

* Re: [kernel-hardening] base address for shared libs
  2011-08-12  9:20               ` Solar Designer
@ 2011-08-12  9:52                 ` Vasiliy Kulikov
  2011-08-12 10:04                   ` Solar Designer
  0 siblings, 1 reply; 16+ messages in thread
From: Vasiliy Kulikov @ 2011-08-12  9:52 UTC (permalink / raw)
  To: kernel-hardening

On Fri, Aug 12, 2011 at 13:20 +0400, Solar Designer wrote:
> > bottom up.
> 
> Huh?  I don't think you used the right words here.

There are 2 allocation logics, top down and bottom up:

http://lxr.free-electrons.com/source/mm/mmap.c#L1372

http://lxr.free-electrons.com/source/mm/mmap.c#L1444

If use top down logic (start from 0x01000000 as the end of the library)
then some gap at 0x00110000 will be wasted.  With bottom up logic I'll
simply have the last library partly being in ASCII-armor zone, the end
of it will be located after 0x01000000, but no waste of vm space.

Or you mean anything else?


> > For Owl we want to make entropy size configurable.  Depending on the
> > entropy, use ASCII-armor or fallback to the default allocator
> > instantly.
> 
> Not exactly.  Both for upstream and for Owl, when the entropy size
> exceeds what we can provide ASCII-armor for, we start at 0x110000
> anyway, but we just happen to go to non-armored addresses if we get such
> random numbers.  For example, if we're configured to use 12 bits and our
> binary uses just one library of 3 MB in size, then there's an approx.
> 75% chance that on a given invocation of the binary we have ASCII armor
> for the library anyway.  This is just not guaranteed.

OK.  However, I don't see much sense in sizes between 10 and 16.  If we
want to use ASCII-armor or warried about vm-hungry apps, then use 10
bits.  If not, use all power of ASLR.

But if use distros with their default 12 bits in containers, it makes
sense to protect them with a probabilistic measure, though.


Thanks,

-- 
Vasiliy

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

* Re: [kernel-hardening] base address for shared libs
  2011-08-12  9:52                 ` Vasiliy Kulikov
@ 2011-08-12 10:04                   ` Solar Designer
  2011-08-12 10:06                     ` Vasiliy Kulikov
  0 siblings, 1 reply; 16+ messages in thread
From: Solar Designer @ 2011-08-12 10:04 UTC (permalink / raw)
  To: kernel-hardening

On Fri, Aug 12, 2011 at 01:52:19PM +0400, Vasiliy Kulikov wrote:
> There are 2 allocation logics, top down and bottom up:
> 
> http://lxr.free-electrons.com/source/mm/mmap.c#L1372
> 
> http://lxr.free-electrons.com/source/mm/mmap.c#L1444
> 
> If use top down logic (start from 0x01000000 as the end of the library)
> then some gap at 0x00110000 will be wasted.  With bottom up logic I'll
> simply have the last library partly being in ASCII-armor zone, the end
> of it will be located after 0x01000000, but no waste of vm space.
> 
> Or you mean anything else?

You're right.  I just didn't realize the words "bottom up" were used in
kernel source to mean that.

> OK.  However, I don't see much sense in sizes between 10 and 16.  If we
> want to use ASCII-armor or warried about vm-hungry apps, then use 10
> bits.

Why not use 14 in such cases, which still fits in the below-binary range
and thus does not reduce the maximum continuous allocation size?

> But if use distros with their default 12 bits in containers, it makes
> sense to protect them with a probabilistic measure, though.

I don't understand what you mean here.  The distros' default 12 bits -
are they patched into those distros' kernels?  If so, they do not apply
to use in containers (where only userlands are used).

Alexander

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

* Re: [kernel-hardening] base address for shared libs
  2011-08-12 10:04                   ` Solar Designer
@ 2011-08-12 10:06                     ` Vasiliy Kulikov
  0 siblings, 0 replies; 16+ messages in thread
From: Vasiliy Kulikov @ 2011-08-12 10:06 UTC (permalink / raw)
  To: kernel-hardening

On Fri, Aug 12, 2011 at 14:04 +0400, Solar Designer wrote:
> > But if use distros with their default 12 bits in containers, it makes
> > sense to protect them with a probabilistic measure, though.
> 
> I don't understand what you mean here.  The distros' default 12 bits -
> are they patched into those distros' kernels?  If so, they do not apply
> to use in containers (where only userlands are used).

Oh, sure.  Please ignore this statement :-)

-- 
Vasiliy

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

end of thread, other threads:[~2011-08-12 10:06 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-07-23 16:22 [kernel-hardening] base address for shared libs Solar Designer
2011-07-24  8:51 ` Vasiliy Kulikov
2011-07-24 14:27   ` Solar Designer
2011-07-24 18:18     ` Vasiliy Kulikov
2011-07-25 19:20     ` Vasiliy Kulikov
2011-08-11  8:32       ` Vasiliy Kulikov
2011-08-12  3:57         ` Solar Designer
2011-08-12  4:21           ` Solar Designer
2011-08-12  8:20             ` Vasiliy Kulikov
2011-08-12  9:20               ` Solar Designer
2011-08-12  9:52                 ` Vasiliy Kulikov
2011-08-12 10:04                   ` Solar Designer
2011-08-12 10:06                     ` Vasiliy Kulikov
2011-07-29  9:27 ` Vasiliy Kulikov
2011-07-30 18:38 ` Vasiliy Kulikov
2011-07-30 18:43   ` Vasiliy Kulikov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox