ASLRv3: randomize_va_space=3 preventing offset2lib attack
Subsystems (10)
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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, structtask_struct *p)
{
mm->mmap = NULL;
+ mm->exec_base = 0;
mm->mm_rb = RB_ROOT;
mm->vmacache_seqnum = 0; atomic_set(&mm->mm_users, 1);
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.htmlPIE 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_basearea (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_BASEaddress 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_spaceis 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@@ -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
-0static 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@@ -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@@ -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
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@@ -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@@ -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
Signed-off-by Hector Marco-Gisbert <[email protected]>Signed-off-by Ismael Ripoll <[email protected]>
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
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_basearea (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_BASEaddress 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_spaceis 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@@ -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@@ -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@@ -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@@ -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@@ -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@@ -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);Signed-off-by Hector Marco-Gisbert <[email protected]>Signed-off-by Ismael Ripoll <[email protected]>
Signature · 2 lines
Andy Lutomirski AMA Capital Management, LLC
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.htmlPIE 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_basearea (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_BASEaddress 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
[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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
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.htmlPIE 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_basearea (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_BASEaddress 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 commit501cb16d3cfdc("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