Skip to contentSkip to conversation

ASLRv3: randomize_va_space=3 preventing offset2lib attack

PATCH In review Hector Marco 4 participants 6 messages Dec 5, 2014 – Dec 9, 2014

Subsystems (10)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 ASLRv3: randomize_va_space=3 preventing offset2lib attack

[PATCH] ASLRv3: randomize_va_space=3 preventing offset2lib attack

The issue appears on PIE linked executables when all memory areas of a process are randomized (randomize_va_space=2). In this case, the attack "offset2lib" de-randomizes all library areas on 64 bit Linux systems in less than one second.

Further details of the PoC attack at:
http://cybersecurity.upv.es/attacks/offset2lib/offset2lib.html

PIE linked applications are loaded side by side with the dynamic libraries, which is exploited by the offset2lib attack. Moving away the executable from the mmap_base area (libraries area) prevents the attack.

This patch loads the PIE linked executable in a different area than the libraries when randomize_va_space=3.

Patch implementation details:

- The ELF_ET_DYN_BASE address is used as the base to load randomly the PIE executable.

- The executable image has the same entropy than randomize_va_space=2.

If the randomize_va_space is set to 2 then this patch does not change any behavior when loading new processes.

The patch has been tested on x86_64/32 and ARM/ARM64.

arch/arm/mm/mmap.c +12 -0
index 5e85ed3..6602f5e 100644
@@ -10,6 +10,7 @@
 #include <linux/personality.h>
 #include <linux/random.h>
 #include <asm/cachetype.h>
+#include <asm/elf.h>
 
 #define COLOUR_ALIGN(addr,pgoff)		\
 	((((addr)+SHMLBA-1)&~(SHMLBA-1)) +	\
@@ -19,6 +20,14 @@
 #define MIN_GAP (128*1024*1024UL)
 #define MAX_GAP ((TASK_SIZE)/6*5)
 
+#if ELF_EXEC_PAGESIZE > PAGE_SIZE
+#define ELF_MIN_ALIGN   ELF_EXEC_PAGESIZE
+#else
+#define ELF_MIN_ALIGN   PAGE_SIZE
+#endif
+
+#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
+
 static int mmap_is_legacy(void)
 {
 	if (current->personality & ADDR_COMPAT_LAYOUT)
@@ -184,6 +193,9 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
 	} else {
 		mm->mmap_base = mmap_base(random_factor);
 		mm->get_unmapped_area = arch_get_unmapped_area_topdown;
+		if (randomize_va_space > 2)
+			mm->exec_base = ELF_PAGESTART(ELF_ET_DYN_BASE -
+				((get_random_int() % (1 << 8)) << PAGE_SHIFT));
 	}
 }
 
arch/arm64/mm/mmap.c +10 -0
index 1d73662..32be3fd 100644
@@ -28,6 +28,16 @@
 #include <linux/random.h>
 
 #include <asm/cputype.h>
+#include <asm/elf.h>
+
+
+#if ELF_EXEC_PAGESIZE > PAGE_SIZE
+#define ELF_MIN_ALIGN   ELF_EXEC_PAGESIZE
+#else
+#define ELF_MIN_ALIGN   PAGE_SIZE
+#endif
+
+#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
 
 /*
  * Leave enough space between the mmap area and the stack to honour

ulimit in
@@ -93,6 +103,8 @@ 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 (randomize_va_space > 2)
+ mm->exec_base = ELF_PAGESTART(ELF_ET_DYN_BASE - mmap_rnd());
}
}
EXPORT_SYMBOL_GPL(arch_pick_mmap_layout);

arch/x86/mm/mmap.c +10 -0
index 919b912..8cb9855 100644
@@ -31,6 +31,14 @@
 #include <linux/sched.h>
 #include <asm/elf.h>
 
+#if ELF_EXEC_PAGESIZE > PAGE_SIZE
+#define ELF_MIN_ALIGN   ELF_EXEC_PAGESIZE
+#else
+#define ELF_MIN_ALIGN   PAGE_SIZE
+#endif
+
+#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
+
 struct va_alignment __read_mostly va_align = {
 	.flags = -1,
 };
@@ -120,5 +128,7 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
 		mm->get_unmapped_area = arch_get_unmapped_area;
 	} else {
 		mm->get_unmapped_area = arch_get_unmapped_area_topdown;
+		if (randomize_va_space > 2)
+			mm->exec_base = ELF_PAGESTART(ELF_ET_DYN_BASE - mmap_rnd());
 	}
 }
fs/binfmt_elf.c +4 -1
index d8fc060..6f319c1 100644
@@ -804,8 +804,11 @@ static int load_elf_binary(struct linux_binprm *bprm)
 			 * load_bias value in order to establish proper
 			 * non-randomized mappings.
 			 */
-			if (current->flags & PF_RANDOMIZE)
+			if (current->flags & PF_RANDOMIZE) {
 				load_bias = 0;
+				if (randomize_va_space > 2)
+					load_bias = current->mm->exec_base;
+			}
 			else
 				load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
 #else
include/linux/mm_types.h +0 -0
index 6e0b286..dd052ec 100644
@@ -353,6 +353,7 @@ struct mm_struct {
 #endif
 	unsigned long mmap_base;		/* base of mmap area */
 	unsigned long mmap_legacy_base;         /* base of mmap area in

bottom-up allocations */
+ unsigned long exec_base; /* base of exec area */
unsigned long task_size; /* size of task vm space */
unsigned long highest_vm_end; /* highest vma end address */
pgd_t * pgd;

kernel/fork.c +0 -0
index 9b7d746..1fd4553 100644
@@ -551,6 +551,7 @@ static void mm_init_owner(struct mm_struct *mm,

struct task_struct *p)
static struct mm_struct *mm_init(struct mm_struct *mm, struct
task_struct *p)
{
mm->mmap = NULL;
+ mm->exec_base = 0;
mm->mm_rb = RB_ROOT;
mm->vmacache_seqnum = 0; atomic_set(&mm->mm_users, 1);

#1 lore raw

Hi,

Thanks for working on this!

Quoted 23 lines · Hector Marco

[PATCH] ASLRv3: randomize_va_space=3 preventing offset2lib attack

The issue appears on PIE linked executables when all memory areas of a process are randomized (randomize_va_space=2). In this case, the attack "offset2lib" de-randomizes all library areas on 64 bit Linux systems in less than one second.

Further details of the PoC attack at:
http://cybersecurity.upv.es/attacks/offset2lib/offset2lib.html

PIE linked applications are loaded side by side with the dynamic libraries, which is exploited by the offset2lib attack. Moving away the executable from the mmap_base area (libraries area) prevents the attack.

This patch loads the PIE linked executable in a different area than the libraries when randomize_va_space=3.

Patch implementation details:

- The ELF_ET_DYN_BASE address is used as the base to load randomly the PIE executable.

Quoted 8 lines

- The executable image has the same entropy than randomize_va_space=2.

If the randomize_va_space is set to 2 then this patch does not change any behavior when loading new processes.

The patch has been tested on x86_64/32 and ARM/ARM64.

Please move your Signed-off-by lines to here, otherwise tools like "git am" may miss them.

Quoted 16 lines
arch/arm/mm/mmap.c +1 -0
index 5e85ed3..6602f5e 100644
@@ -10,6 +10,7 @@
 #include <linux/personality.h>
 #include <linux/random.h>
 #include <asm/cachetype.h>
+#include <asm/elf.h>
 
 #define COLOUR_ALIGN(addr,pgoff)               \
        ((((addr)+SHMLBA-1)&~(SHMLBA-1)) +      \
@@ -19,6 +20,14 @@
 #define MIN_GAP (128*1024*1024UL)

arch/arm/mm/mmap.c line 30

Quoted 10 lines
arch/arm/mm/mmap.c +8 -0
 #define MAX_GAP ((TASK_SIZE)/6*5)
 
+#if ELF_EXEC_PAGESIZE > PAGE_SIZE
+#define ELF_MIN_ALIGN   ELF_EXEC_PAGESIZE
+#else
+#define ELF_MIN_ALIGN   PAGE_SIZE
+#endif
+
+#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
+

Instead of repeating these defines in each location, I would recommend moving them from fs/binfmt_elf.c into include/linux/elf.h and including that in the mmap.c files instead.

arch/arm/mm/mmap.c void arch_pick_mmap_layout(struct mm_struct *mm) · line 201

Quoted 13 lines
arch/arm/mm/mmap.c +3 -0
 static int mmap_is_legacy(void)
 {
        if (current->personality & ADDR_COMPAT_LAYOUT)
@@ -184,6 +193,9 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
        } else {
                mm->mmap_base = mmap_base(random_factor);
                mm->get_unmapped_area = arch_get_unmapped_area_topdown;
+               if (randomize_va_space > 2)
+                       mm->exec_base = ELF_PAGESTART(ELF_ET_DYN_BASE -
+                               ((get_random_int() % (1 << 8)) << PAGE_SHIFT));
        }
 }
 

I would extract the random_factor logic here into an arm-specific mmap_rnd() function so this looks more like the other architectures. Then you can reuse mmap_rnd() instead of open-coding the get_random_int() call again here.

Quoted 63 lines
arch/arm64/mm/mmap.c +10 -0
index 1d73662..32be3fd 100644
@@ -28,6 +28,16 @@
 #include <linux/random.h>
 
 #include <asm/cputype.h>
+#include <asm/elf.h>
+
+
+#if ELF_EXEC_PAGESIZE > PAGE_SIZE
+#define ELF_MIN_ALIGN   ELF_EXEC_PAGESIZE
+#else
+#define ELF_MIN_ALIGN   PAGE_SIZE
+#endif
+
+#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
 
 /*
  * Leave enough space between the mmap area and the stack to honour

ulimit in

arch/arm64/mm/mmap.c +2 -0
@@ -93,6 +103,8 @@ 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 (randomize_va_space > 2)
+                       mm->exec_base = ELF_PAGESTART(ELF_ET_DYN_BASE - mmap_rnd());
        }
 }
 EXPORT_SYMBOL_GPL(arch_pick_mmap_layout);
arch/x86/mm/mmap.c +10 -0
index 919b912..8cb9855 100644
@@ -31,6 +31,14 @@
 #include <linux/sched.h>
 #include <asm/elf.h>
 
+#if ELF_EXEC_PAGESIZE > PAGE_SIZE
+#define ELF_MIN_ALIGN   ELF_EXEC_PAGESIZE
+#else
+#define ELF_MIN_ALIGN   PAGE_SIZE
+#endif
+
+#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
+
 struct va_alignment __read_mostly va_align = {
        .flags = -1,
 };
@@ -120,5 +128,7 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
                mm->get_unmapped_area = arch_get_unmapped_area;
        } else {
                mm->get_unmapped_area = arch_get_unmapped_area_topdown;
+               if (randomize_va_space > 2)
+                       mm->exec_base = ELF_PAGESTART(ELF_ET_DYN_BASE - mmap_rnd());
        }
 }
fs/binfmt_elf.c +0 -0
index d8fc060..6f319c1 100644

fs/binfmt_elf.c static int load_elf_binary(struct linux_binprm *bprm) · line 810

Quoted 8 lines
fs/binfmt_elf.c +3 -1
@@ -804,8 +804,11 @@ static int load_elf_binary(struct linux_binprm *bprm)
                         * load_bias value in order to establish proper
                         * non-randomized mappings.
                         */
-                       if (current->flags & PF_RANDOMIZE)
+                       if (current->flags & PF_RANDOMIZE) {
                                load_bias = 0;
+                               if (randomize_va_space > 2)
+                                       load_bias = current->mm->exec_base;

Your exec_base will already only be non-zero when randomize_va_space > 2, so this if statement is not needed. Instead of "load_bias = 0" this can just be "load_bias = current->mm->exec_base".

Quoted 33 lines
fs/binfmt_elf.c +1 -0
+                       }
                        else
                                load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
 #else
include/linux/mm_types.h +0 -0
index 6e0b286..dd052ec 100644
@@ -353,6 +353,7 @@ struct mm_struct {
 #endif
        unsigned long mmap_base;                /* base of mmap area */
        unsigned long mmap_legacy_base;         /* base of mmap area in

bottom-up allocations */

include/linux/mm_types.h +1 -0
+       unsigned long exec_base;                /* base of exec area */
        unsigned long task_size;                /* size of task vm space */
        unsigned long highest_vm_end;           /* highest vma end address */
        pgd_t * pgd;
kernel/fork.c +1 -0
index 9b7d746..1fd4553 100644
@@ -551,6 +551,7 @@ static void mm_init_owner(struct mm_struct *mm,
 struct task_struct *p)
 static struct mm_struct *mm_init(struct mm_struct *mm, struct
 task_struct *p)
 {
        mm->mmap = NULL;
+       mm->exec_base = 0;
        mm->mm_rb = RB_ROOT;
        mm->vmacache_seqnum = 0;
        atomic_set(&mm->mm_users, 1);
Quoted 2 lines

Please update Documentation/sysctl/kernel.txt to describe the new "3" value of randomize_va_space. And given that this shouldn't break any existing userspace configuration, I think randomize_va_space should also have its default flipped from 2 to 3.

Thanks again!

-Kees

Signature · 2 lines
Kees Cook
Chrome OS Security
#3 lore raw
In reply to Hector Marco
Quoted 10 lines · Hector Marco

[PATCH] ASLRv3: randomize_va_space=3 preventing offset2lib attack

The issue appears on PIE linked executables when all memory areas of a process are randomized (randomize_va_space=2). In this case, the attack "offset2lib" de-randomizes all library areas on 64 bit Linux systems in less than one second.

Further details of the PoC attack at:
http://cybersecurity.upv.es/attacks/offset2lib/offset2lib.html

Quoted 7 lines

PIE linked applications are loaded side by side with the dynamic libraries, which is exploited by the offset2lib attack. Moving away the executable from the mmap_base area (libraries area) prevents the attack.

This patch loads the PIE linked executable in a different area than the libraries when randomize_va_space=3.

Can we do much better if we add randomly-sized guard regions between all libraries? I don't understand why libraries are mapped consecutively, other than to save a tiny bit of RAM by using fewer page tables.

--Andy

Quoted 159 lines

Patch implementation details:

- The ELF_ET_DYN_BASE address is used as the base to load randomly the PIE executable.

- The executable image has the same entropy than randomize_va_space=2.

If the randomize_va_space is set to 2 then this patch does not change any behavior when loading new processes.

The patch has been tested on x86_64/32 and ARM/ARM64.

arch/arm/mm/mmap.c +12 -0
index 5e85ed3..6602f5e 100644
@@ -10,6 +10,7 @@
 #include <linux/personality.h>
 #include <linux/random.h>
 #include <asm/cachetype.h>
+#include <asm/elf.h>
 
 #define COLOUR_ALIGN(addr,pgoff)               \
        ((((addr)+SHMLBA-1)&~(SHMLBA-1)) +      \
@@ -19,6 +20,14 @@
 #define MIN_GAP (128*1024*1024UL)
 #define MAX_GAP ((TASK_SIZE)/6*5)
 
+#if ELF_EXEC_PAGESIZE > PAGE_SIZE
+#define ELF_MIN_ALIGN   ELF_EXEC_PAGESIZE
+#else
+#define ELF_MIN_ALIGN   PAGE_SIZE
+#endif
+
+#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
+
 static int mmap_is_legacy(void)
 {
        if (current->personality & ADDR_COMPAT_LAYOUT)
@@ -184,6 +193,9 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
        } else {
                mm->mmap_base = mmap_base(random_factor);
                mm->get_unmapped_area = arch_get_unmapped_area_topdown;
+               if (randomize_va_space > 2)
+                       mm->exec_base = ELF_PAGESTART(ELF_ET_DYN_BASE -
+                               ((get_random_int() % (1 << 8)) << PAGE_SHIFT));
        }
 }
 
arch/arm64/mm/mmap.c +10 -0
index 1d73662..32be3fd 100644
@@ -28,6 +28,16 @@
 #include <linux/random.h>
 
 #include <asm/cputype.h>
+#include <asm/elf.h>
+
+
+#if ELF_EXEC_PAGESIZE > PAGE_SIZE
+#define ELF_MIN_ALIGN   ELF_EXEC_PAGESIZE
+#else
+#define ELF_MIN_ALIGN   PAGE_SIZE
+#endif
+
+#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
 
 /*
  * Leave enough space between the mmap area and the stack to honour

ulimit in

arch/arm64/mm/mmap.c +2 -0
@@ -93,6 +103,8 @@ 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 (randomize_va_space > 2)
+                       mm->exec_base = ELF_PAGESTART(ELF_ET_DYN_BASE - mmap_rnd());
        }
 }
 EXPORT_SYMBOL_GPL(arch_pick_mmap_layout);
arch/x86/mm/mmap.c +10 -0
index 919b912..8cb9855 100644
@@ -31,6 +31,14 @@
 #include <linux/sched.h>
 #include <asm/elf.h>
 
+#if ELF_EXEC_PAGESIZE > PAGE_SIZE
+#define ELF_MIN_ALIGN   ELF_EXEC_PAGESIZE
+#else
+#define ELF_MIN_ALIGN   PAGE_SIZE
+#endif
+
+#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
+
 struct va_alignment __read_mostly va_align = {
        .flags = -1,
 };
@@ -120,5 +128,7 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
                mm->get_unmapped_area = arch_get_unmapped_area;
        } else {
                mm->get_unmapped_area = arch_get_unmapped_area_topdown;
+               if (randomize_va_space > 2)
+                       mm->exec_base = ELF_PAGESTART(ELF_ET_DYN_BASE - mmap_rnd());
        }
 }
fs/binfmt_elf.c +4 -1
index d8fc060..6f319c1 100644
@@ -804,8 +804,11 @@ static int load_elf_binary(struct linux_binprm *bprm)
                         * load_bias value in order to establish proper
                         * non-randomized mappings.
                         */
-                       if (current->flags & PF_RANDOMIZE)
+                       if (current->flags & PF_RANDOMIZE) {
                                load_bias = 0;
+                               if (randomize_va_space > 2)
+                                       load_bias = current->mm->exec_base;
+                       }
                        else
                                load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
 #else
include/linux/mm_types.h +0 -0
index 6e0b286..dd052ec 100644
@@ -353,6 +353,7 @@ struct mm_struct {
 #endif
        unsigned long mmap_base;                /* base of mmap area */
        unsigned long mmap_legacy_base;         /* base of mmap area in

bottom-up allocations */

include/linux/mm_types.h +1 -0
+       unsigned long exec_base;                /* base of exec area */
        unsigned long task_size;                /* size of task vm space */
        unsigned long highest_vm_end;           /* highest vma end address */
        pgd_t * pgd;
kernel/fork.c +1 -0
index 9b7d746..1fd4553 100644
@@ -551,6 +551,7 @@ static void mm_init_owner(struct mm_struct *mm,
 struct task_struct *p)
 static struct mm_struct *mm_init(struct mm_struct *mm, struct
 task_struct *p)
 {
        mm->mmap = NULL;
+       mm->exec_base = 0;
        mm->mm_rb = RB_ROOT;
        mm->vmacache_seqnum = 0;
        atomic_set(&mm->mm_users, 1);
Signature · 2 lines
Andy Lutomirski
AMA Capital Management, LLC
#4 lore raw
In reply to Hector Marco

Am 05.12.2014 um 01:07 schrieb Hector Marco:

Quoted 25 lines

[PATCH] ASLRv3: randomize_va_space=3 preventing offset2lib attack

The issue appears on PIE linked executables when all memory areas of a process are randomized (randomize_va_space=2). In this case, the attack "offset2lib" de-randomizes all library areas on 64 bit Linux systems in less than one second.

Further details of the PoC attack at:
http://cybersecurity.upv.es/attacks/offset2lib/offset2lib.html

PIE linked applications are loaded side by side with the dynamic libraries, which is exploited by the offset2lib attack. Moving away the executable from the mmap_base area (libraries area) prevents the attack.

This patch loads the PIE linked executable in a different area than the libraries when randomize_va_space=3.

Patch implementation details:

- The ELF_ET_DYN_BASE address is used as the base to load randomly the PIE executable.

- The executable image has the same entropy than randomize_va_space=2.

[...]

Quoted 2 lines
arch/arm/mm/mmap.c +0 -0

[...]

Quoted 2 lines
arch/arm64/mm/mmap.c +0 -0

[...]

arch/x86/mm/mmap.c

Quoted 2 lines
arch/x86/mm/mmap.c +0 -0

[...]

FWIW, please note that s390 and power (maybe others?) also have PIE support done differently, e.g. commit d2c9dfccbc3 ("[S390] Randomize PIEs") and commit 501cb16d3cfdc ("powerpc: Randomise PIEs")

What I can tell from a quick look both architectures should be fine regarding offsetlib, as they place the executable already in a different section and randomize those differently even with randomize_va_space=2.

Would it make sense to unify the implementations again?

Christian

#2 lore raw
In reply to Kees Cook

[PATCH] ASLRv3: randomize_va_space=3 preventing offset2lib attack

The issue appears on PIE linked executables when all memory areas of a process are randomized (randomize_va_space=2). In this case, the attack "offset2lib" de-randomizes all library areas on 64 bit Linux systems in less than one second.

Further details of the PoC attack at:
http://cybersecurity.upv.es/attacks/offset2lib/offset2lib.html

PIE linked applications are loaded side by side with the dynamic libraries, which is exploited by the offset2lib attack. Moving away the executable from the mmap_base area (libraries area) prevents the attack.

This patch loads the PIE linked executable in a different area than the libraries when randomize_va_space=3. By default randomize_va_space is set to 3 because this shouldn't break any existing userspace configuration.

Patch implementation details:

- The ELF_ET_DYN_BASE address is used as the base to load randomly the PIE executable.

- The executable image has the same entropy than randomize_va_space=2.

If the randomize_va_space is set to 2 then this patch does not change any behavior when loading new processes.

The patch has been tested on x86_64/32 and ARM/ARM64.

Documentation/sysctl/kernel.txt +13 -0
index 57baff5..1068492 100644
@@ -690,6 +690,19 @@ that support this feature.
     with CONFIG_COMPAT_BRK enabled, which excludes the heap from process
     address space randomization.
 
+3 - It is an extension of the previous option, which only affects
+    PIE-linked binaries. If enabled, the application executable is
+    located to a random area on its own. Note that with options 1
+    and 2, the executable is placed side by side with the rest of the
+    mmaped objects (libraries, maped files, etc.).
+
+    An address leak from an area does not compromise the others. So,
+    this option removes the offset2lib weakness.
+
+    As far as we know it shall not break backward compatibility and
+    does not introduce overhead on the execution. Therefore, it is
+    advisable to enable it.
+
 ==============================================================
 
 reboot-cmd: (Sparc only)
arch/arm/mm/mmap.c +18 -11
index 5e85ed3..0b1e18c 100644
@@ -9,6 +9,7 @@
 #include <linux/io.h>
 #include <linux/personality.h>
 #include <linux/random.h>
+#include <linux/elf.h>
 #include <asm/cachetype.h>
 
 #define COLOUR_ALIGN(addr,pgoff)		\
@@ -19,6 +20,17 @@
 #define MIN_GAP (128*1024*1024UL)
 #define MAX_GAP ((TASK_SIZE)/6*5)
 
+/* 8 bits of randomness in 20 address space bits */
+static unsigned long mmap_rnd(void)
+{
+	unsigned long rnd = 0;
+	if ((current->flags & PF_RANDOMIZE) &&
+		!(current->personality & ADDR_NO_RANDOMIZE))
+		rnd = (get_random_int() % (1 << 8)) << PAGE_SHIFT;
+
+	return rnd;
+}
+
 static int mmap_is_legacy(void)
 {
 	if (current->personality & ADDR_COMPAT_LAYOUT)
@@ -30,7 +42,7 @@ static int mmap_is_legacy(void)
 	return sysctl_legacy_va_layout;
 }
 
-static unsigned long mmap_base(unsigned long rnd)
+static unsigned long mmap_base(void)
 {
 	unsigned long gap = rlimit(RLIMIT_STACK);
 
@@ -39,7 +51,7 @@ static unsigned long mmap_base(unsigned long rnd)
 	else if (gap > MAX_GAP)
 		gap = MAX_GAP;
 
-	return PAGE_ALIGN(TASK_SIZE - gap - rnd);
+	return PAGE_ALIGN(TASK_SIZE - gap - mmap_rnd());
 }
 
 /*
@@ -171,19 +183,14 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
 
 void arch_pick_mmap_layout(struct mm_struct *mm)
 {
-	unsigned long random_factor = 0UL;
-
-	/* 8 bits of randomness in 20 address space bits */
-	if ((current->flags & PF_RANDOMIZE) &&
-	    !(current->personality & ADDR_NO_RANDOMIZE))
-		random_factor = (get_random_int() % (1 << 8)) << PAGE_SHIFT;
-
 	if (mmap_is_legacy()) {
-		mm->mmap_base = TASK_UNMAPPED_BASE + random_factor;
+		mm->mmap_base = TASK_UNMAPPED_BASE + mmap_rnd();
 		mm->get_unmapped_area = arch_get_unmapped_area;
 	} else {
-		mm->mmap_base = mmap_base(random_factor);
+		mm->mmap_base = mmap_base();
 		mm->get_unmapped_area = arch_get_unmapped_area_topdown;
+		if (randomize_va_space > 2)
+			mm->exec_base = ELF_PAGESTART(ELF_ET_DYN_BASE - mmap_rnd());
 	}
 }
 
arch/arm64/mm/mmap.c +2 -0
index 1d73662..793d4b6 100644
@@ -93,6 +93,8 @@ 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 (randomize_va_space > 2)
+			mm->exec_base = ELF_PAGESTART(ELF_ET_DYN_BASE - mmap_rnd());
 	}
 }
 EXPORT_SYMBOL_GPL(arch_pick_mmap_layout);
arch/x86/mm/mmap.c +3 -0
index 919b912..06202d3 100644
@@ -29,6 +29,7 @@
 #include <linux/random.h>
 #include <linux/limits.h>
 #include <linux/sched.h>
+#include <linux/elf.h>
 #include <asm/elf.h>
 
 struct va_alignment __read_mostly va_align = {
@@ -120,5 +121,7 @@ void arch_pick_mmap_layout(struct mm_struct *mm)
 		mm->get_unmapped_area = arch_get_unmapped_area;
 	} else {
 		mm->get_unmapped_area = arch_get_unmapped_area_topdown;
+		if (randomize_va_space > 2)
+			mm->exec_base = ELF_PAGESTART(ELF_ET_DYN_BASE - mmap_rnd());
 	}
 }
fs/binfmt_elf.c +1 -7
index d8fc060..8a89afe 100644
@@ -65,17 +65,11 @@ static int elf_core_dump(struct coredump_params *cprm);
 #define elf_core_dump	NULL
 #endif
 
-#if ELF_EXEC_PAGESIZE > PAGE_SIZE
-#define ELF_MIN_ALIGN	ELF_EXEC_PAGESIZE
-#else
-#define ELF_MIN_ALIGN	PAGE_SIZE
-#endif
 
 #ifndef ELF_CORE_EFLAGS
 #define ELF_CORE_EFLAGS	0
 #endif
 
-#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
 #define ELF_PAGEOFFSET(_v) ((_v) & (ELF_MIN_ALIGN-1))
 #define ELF_PAGEALIGN(_v) (((_v) + ELF_MIN_ALIGN - 1) & ~(ELF_MIN_ALIGN - 1))
 
@@ -805,7 +799,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
 			 * non-randomized mappings.
 			 */
 			if (current->flags & PF_RANDOMIZE)
-				load_bias = 0;
+				load_bias = current->mm->exec_base;
 			else
 				load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
 #else
include/linux/elf.h +10 -0
index 67a5fa7..46fb890 100644
@@ -3,6 +3,7 @@
 
 #include <asm/elf.h>
 #include <uapi/linux/elf.h>
+#include <asm/page.h>
 
 #ifndef elf_read_implies_exec
   /* Executables for which elf_read_implies_exec() returns TRUE will
@@ -48,4 +49,13 @@ static inline int elf_coredump_extra_notes_write(struct coredump_params *cprm) {
 extern int elf_coredump_extra_notes_size(void);
 extern int elf_coredump_extra_notes_write(struct coredump_params *cprm);
 #endif
+
+#if ELF_EXEC_PAGESIZE > PAGE_SIZE
+#define ELF_MIN_ALIGN   ELF_EXEC_PAGESIZE
+#else
+#define ELF_MIN_ALIGN   PAGE_SIZE
+#endif
+
+#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
+
 #endif /* _LINUX_ELF_H */
include/linux/mm_types.h +0 -0
index 6e0b286..dd052ec 100644
@@ -353,6 +353,7 @@ struct mm_struct {
 #endif
 	unsigned long mmap_base;		/* base of mmap area */
 	unsigned long mmap_legacy_base;         /* base of mmap area in  

bottom-up allocations */
+ unsigned long exec_base; /* base of exec area */
unsigned long task_size; /* size of task vm space */
unsigned long highest_vm_end; /* highest vma end address */
pgd_t * pgd;

kernel/fork.c +1 -0
index 9b7d746..1fd4553 100644
@@ -551,6 +551,7 @@ static void mm_init_owner(struct mm_struct *mm, struct task_struct *p)
 static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p)
 {
 	mm->mmap = NULL;
+	mm->exec_base = 0;
 	mm->mm_rb = RB_ROOT;
 	mm->vmacache_seqnum = 0;
 	atomic_set(&mm->mm_users, 1);
mm/memory.c +6 -1
index d5f2ae9..47a185f 100644
@@ -100,12 +100,17 @@ EXPORT_SYMBOL(high_memory);
  *
  * ( When CONFIG_COMPAT_BRK=y we exclude brk from randomization,
  *   as ancient (libc5 based) binaries can segfault. )
+ *
+ *  The default when is not COMPAT is moved from 2 to 3 to
+ *  improve PIE-linked binaries randomization.
+ *  (This prevent the offset2lib attack)
+ *
  */
 int randomize_va_space __read_mostly =
 #ifdef CONFIG_COMPAT_BRK
 					1;
 #else
-					2;
+					3;
 #endif
 
 static int __init disable_randmaps(char *s)
#5 lore raw
In reply to Christian Borntraeger
Quoted 38 lines · Christian Borntraeger

Am 05.12.2014 um 01:07 schrieb Hector Marco:

Quoted 25 lines

[PATCH] ASLRv3: randomize_va_space=3 preventing offset2lib attack

The issue appears on PIE linked executables when all memory areas of a process are randomized (randomize_va_space=2). In this case, the attack "offset2lib" de-randomizes all library areas on 64 bit Linux systems in less than one second.

Further details of the PoC attack at:
http://cybersecurity.upv.es/attacks/offset2lib/offset2lib.html

PIE linked applications are loaded side by side with the dynamic libraries, which is exploited by the offset2lib attack. Moving away the executable from the mmap_base area (libraries area) prevents the attack.

This patch loads the PIE linked executable in a different area than the libraries when randomize_va_space=3.

Patch implementation details:

- The ELF_ET_DYN_BASE address is used as the base to load randomly the PIE executable.

- The executable image has the same entropy than randomize_va_space=2.

[...]

Quoted 2 lines
arch/arm/mm/mmap.c +0 -0

[...]

Quoted 2 lines
arch/arm64/mm/mmap.c +0 -0

[...]

Quoted 2 lines
arch/x86/mm/mmap.c +0 -0

[...]

Quoted 6 lines

FWIW, please note that s390 and power (maybe others?) also have PIE support done differently, e.g. commit d2c9dfccbc3 ("[S390] Randomize PIEs") and commit 501cb16d3cfdc ("powerpc: Randomise PIEs")

What I can tell from a quick look both architectures should be fine regarding offsetlib, as they place the executable already in a different section and randomize those differently even with randomize_va_space=2.

Would it make sense to unify the implementations again?

Seems like that would be best. If this is already being done on non-x86 and non-arm, we should just fix x86 and arm to DTRT. :)

-Kees

Signature · 2 lines
Kees Cook
Chrome OS Security