Skip to content

Commit 4e26d11

Browse files
Hector Marco-GisbertIngo Molnar
authored andcommitted
x86/mm: Improve AMD Bulldozer ASLR workaround
The ASLR implementation needs to special-case AMD F15h processors by clearing out bits [14:12] of the virtual address in order to avoid I$ cross invalidations and thus performance penalty for certain workloads. For details, see: dfb09f9 ("x86, amd: Avoid cache aliasing penalties on AMD family 15h") This special case reduces the mmapped file's entropy by 3 bits. The following output is the run on an AMD Opteron 62xx class CPU processor under x86_64 Linux 4.0.0: $ for i in `seq 1 10`; do cat /proc/self/maps | grep "r-xp.*libc" ; done b7588000-b7736000 r-xp 00000000 00:01 4924 /lib/i386-linux-gnu/libc.so.6 b7570000-b771e000 r-xp 00000000 00:01 4924 /lib/i386-linux-gnu/libc.so.6 b75d0000-b777e000 r-xp 00000000 00:01 4924 /lib/i386-linux-gnu/libc.so.6 b75b0000-b775e000 r-xp 00000000 00:01 4924 /lib/i386-linux-gnu/libc.so.6 b7578000-b7726000 r-xp 00000000 00:01 4924 /lib/i386-linux-gnu/libc.so.6 ... Bits [12:14] are always 0, i.e. the address always ends in 0x8000 or 0x0000. 32-bit systems, as in the example above, are especially sensitive to this issue because 32-bit randomness for VA space is 8 bits (see mmap_rnd()). With the Bulldozer special case, this diminishes to only 32 different slots of mmap virtual addresses. This patch randomizes per boot the three affected bits rather than setting them to zero. Since all the shared pages have the same value at bits [12..14], there is no cache aliasing problems. This value gets generated during system boot and it is thus not known to a potential remote attacker. Therefore, the impact from the Bulldozer workaround gets diminished and ASLR randomness increased. More details at: http://hmarco.org/bugs/AMD-Bulldozer-linux-ASLR-weakness-reducing-mmaped-files-by-eight.html Original white paper by AMD dealing with the issue: http://developer.amd.com/wordpress/media/2012/10/SharedL1InstructionCacheonAMD15hCPU.pdf Mentored-by: Ismael Ripoll <iripoll@disca.upv.es> Signed-off-by: Hector Marco-Gisbert <hecmargi@upv.es> Signed-off-by: Borislav Petkov <bp@suse.de> Acked-by: Kees Cook <keescook@chromium.org> Cc: Alexander Viro <viro@zeniv.linux.org.uk> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Jan-Simon <dl9pf@gmx.de> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: linux-fsdevel@vger.kernel.org Link: http://lkml.kernel.org/r/1427456301-3764-1-git-send-email-hecmargi@upv.es Signed-off-by: Ingo Molnar <mingo@kernel.org>
1 parent c709fed commit 4e26d11

File tree

3 files changed

+32
-3
lines changed

3 files changed

+32
-3
lines changed

arch/x86/include/asm/elf.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ enum align_flags {
365365
struct va_alignment {
366366
int flags;
367367
unsigned long mask;
368+
unsigned long bits;
368369
} ____cacheline_aligned;
369370

370371
extern struct va_alignment va_align;

arch/x86/kernel/cpu/amd.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#include <linux/io.h>
77
#include <linux/sched.h>
8+
#include <linux/random.h>
89
#include <asm/processor.h>
910
#include <asm/apic.h>
1011
#include <asm/cpu.h>
@@ -488,6 +489,9 @@ static void bsp_init_amd(struct cpuinfo_x86 *c)
488489

489490
va_align.mask = (upperbit - 1) & PAGE_MASK;
490491
va_align.flags = ALIGN_VA_32 | ALIGN_VA_64;
492+
493+
/* A random value per boot for bit slice [12:upper_bit) */
494+
va_align.bits = get_random_int() & va_align.mask;
491495
}
492496
}
493497

arch/x86/kernel/sys_x86_64.c

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,26 @@ static unsigned long get_align_mask(void)
3434
return va_align.mask;
3535
}
3636

37+
/*
38+
* To avoid aliasing in the I$ on AMD F15h, the bits defined by the
39+
* va_align.bits, [12:upper_bit), are set to a random value instead of
40+
* zeroing them. This random value is computed once per boot. This form
41+
* of ASLR is known as "per-boot ASLR".
42+
*
43+
* To achieve this, the random value is added to the info.align_offset
44+
* value before calling vm_unmapped_area() or ORed directly to the
45+
* address.
46+
*/
47+
static unsigned long get_align_bits(void)
48+
{
49+
return va_align.bits & get_align_mask();
50+
}
51+
3752
unsigned long align_vdso_addr(unsigned long addr)
3853
{
3954
unsigned long align_mask = get_align_mask();
40-
return (addr + align_mask) & ~align_mask;
55+
addr = (addr + align_mask) & ~align_mask;
56+
return addr | get_align_bits();
4157
}
4258

4359
static int __init control_va_addr_alignment(char *str)
@@ -135,8 +151,12 @@ arch_get_unmapped_area(struct file *filp, unsigned long addr,
135151
info.length = len;
136152
info.low_limit = begin;
137153
info.high_limit = end;
138-
info.align_mask = filp ? get_align_mask() : 0;
154+
info.align_mask = 0;
139155
info.align_offset = pgoff << PAGE_SHIFT;
156+
if (filp) {
157+
info.align_mask = get_align_mask();
158+
info.align_offset += get_align_bits();
159+
}
140160
return vm_unmapped_area(&info);
141161
}
142162

@@ -174,8 +194,12 @@ arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
174194
info.length = len;
175195
info.low_limit = PAGE_SIZE;
176196
info.high_limit = mm->mmap_base;
177-
info.align_mask = filp ? get_align_mask() : 0;
197+
info.align_mask = 0;
178198
info.align_offset = pgoff << PAGE_SHIFT;
199+
if (filp) {
200+
info.align_mask = get_align_mask();
201+
info.align_offset += get_align_bits();
202+
}
179203
addr = vm_unmapped_area(&info);
180204
if (!(addr & ~PAGE_MASK))
181205
return addr;

0 commit comments

Comments
 (0)