[PATCH v2 24/29] ARM: kernel: implement randomization of the kernel load address
Nicolas Pitre
nicolas.pitre at linaro.org
Mon Sep 4 11:44:15 PDT 2017
On Sun, 3 Sep 2017, Ard Biesheuvel wrote:
> This implements randomization of the placement of the kernel image
> inside the lowmem region. It is intended to work together with the
> decompressor to place the kernel at an offset in physical memory
> that is a multiple of 2 MB, and to take the same offset into account
> when creating the virtual mapping.
>
> This uses runtime relocation of the kernel built as a PIE binary, to
> fix up all absolute symbol references to refer to their runtime virtual
> address. The physical-to-virtual mapping remains unchanged.
>
> In order to allow the decompressor to hand over to the core kernel
> without making assumptions that are not guaranteed to hold when
> invoking the core kernel directly using bootloaders that are not
> KASLR aware, the KASLR offset is expected to be placed in r3 when
> entering the kernel 4 bytes past the entry point, skipping the first
> instruction.
>
> Cc: Russell King <linux at armlinux.org.uk>
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel at linaro.org>
> ---
> arch/arm/Kconfig | 15 +++
> arch/arm/kernel/head.S | 103 ++++++++++++++++++--
> 2 files changed, 109 insertions(+), 9 deletions(-)
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 300add3b8023..fe4a2cd1f15c 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -1825,6 +1825,21 @@ config XEN
> help
> Say Y if you want to run Linux in a Virtual Machine on Xen on ARM.
>
> +config RANDOMIZE_BASE
> + bool "Randomize the address of the kernel image"
> + depends on MMU && AUTO_ZRELADDR
> + depends on !XIP_KERNEL && !ZBOOT_ROM
This should work even if ZBOOT_ROM is selected. The ZBOOT_ROM option
allows for the decompressor executing directly from ROM and
decompressing the kernel anywhere in RAM.
> + select RELOCATABLE
> + help
> + Randomizes the virtual and physical address at which the kernel
> + image is loaded, as a security feature that deters exploit attempts
> + relying on knowledge of the location of kernel internals.
> +
> + This relies on the UEFI stub to invoke the EFI_RNG_PROTOCOL to
> + randomize the load address of the decompressed kernel in the
> + physical space. The same offset is applied to the virtual mapping
> + of the kernel in the virtual space by the kernel proper.
> +
> endmenu
>
> menu "Boot options"
> diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
> index 71bc0d037bc9..0795da990dde 100644
> --- a/arch/arm/kernel/head.S
> +++ b/arch/arm/kernel/head.S
> @@ -48,6 +48,28 @@
> #define PMD_ORDER 2
> #endif
>
> + .macro get_kaslr_offset, reg
> +#ifdef CONFIG_RANDOMIZE_BASE
> + ldr_l \reg, __kaslr_offset
> +#else
> + mov \reg, #0
> +#endif
> + .endm
> +
> + .macro add_kaslr_offset, reg, tmp
> +#ifdef CONFIG_RANDOMIZE_BASE
> + get_kaslr_offset \tmp
> + add \reg, \reg, \tmp
> +#endif
> + .endm
> +
> + .macro sub_kaslr_offset, reg, tmp
> +#ifdef CONFIG_RANDOMIZE_BASE
> + get_kaslr_offset \tmp
> + sub \reg, \reg, \tmp
> +#endif
> + .endm
> +
> /*
> * Kernel startup entry point.
> * ---------------------------
> @@ -73,6 +95,7 @@
> .equ swapper_pg_dir, . - PG_DIR_SIZE
>
> ENTRY(stext)
> + mov r3, #0 @ normal entry point - clear r3
> ARM_BE8(setend be ) @ ensure we are in BE8 mode
>
> THUMB( badr r9, 1f ) @ Kernel is always entered in ARM.
> @@ -80,6 +103,16 @@ ENTRY(stext)
> THUMB( .thumb ) @ switch to Thumb now.
> THUMB(1: )
>
> +#ifdef CONFIG_RANDOMIZE_BASE
> + str_l r3, __kaslr_offset, r9 @ offset in r3 if entered via kaslr ep
> +
> + .section ".bss", "aw", %nobits
> + .align 2
> +__kaslr_offset:
> + .long 0 @ will be wiped before entering C code
> + .previous
> +#endif
> +
> #ifdef CONFIG_ARM_VIRT_EXT
> bl __hyp_stub_install
> #endif
> @@ -103,6 +136,7 @@ ENTRY(stext)
> #ifndef CONFIG_XIP_KERNEL
> adr_l r8, _text @ __pa(_text)
> sub r8, r8, #TEXT_OFFSET @ PHYS_OFFSET
> + sub_kaslr_offset r8, r12
> #else
> ldr r8, =PLAT_PHYS_OFFSET @ always constant in this case
> #endif
> @@ -139,8 +173,8 @@ ENTRY(stext)
> * r0 will hold the CPU control register value, r1, r2, r4, and
> * r9 will be preserved. r5 will also be preserved if LPAE.
> */
> - ldr r13, =__mmap_switched @ address to jump to after
> - @ mmu has been enabled
> + adr_l lr, __primary_switch @ address to jump to after
> + mov r13, lr @ mmu has been enabled
> badr lr, 1f @ return (PIC) address
> #ifdef CONFIG_ARM_LPAE
> mov r5, #0 @ high TTBR0
> @@ -151,7 +185,8 @@ ENTRY(stext)
> ldr r12, [r10, #PROCINFO_INITFUNC]
> add r12, r12, r10
> ret r12
> -1: b __enable_mmu
> +1: get_kaslr_offset r12 @ get before turning MMU on
> + b __enable_mmu
> ENDPROC(stext)
> .ltorg
>
> @@ -230,9 +265,14 @@ __create_page_tables:
> /*
> * Map our RAM from the start to the end of the kernel .bss section.
> */
> - add r0, r4, #PAGE_OFFSET >> (SECTION_SHIFT - PMD_ORDER)
> - ldr r6, =(_end - 1)
> - orr r3, r8, r7
> + get_kaslr_offset r3
> + add r0, r3, #PAGE_OFFSET
> + add r0, r4, r0, lsr #(SECTION_SHIFT - PMD_ORDER)
> + adr_l r6, _end - 1
> + sub r6, r6, r8
> + add r6, r6, #PAGE_OFFSET
> + add r3, r3, r8
> + orr r3, r3, r7
> add r6, r4, r6, lsr #(SECTION_SHIFT - PMD_ORDER)
> 1: str r3, [r0], #1 << PMD_ORDER
> add r3, r3, #1 << SECTION_SHIFT
> @@ -376,7 +416,7 @@ ENTRY(secondary_startup)
> * Use the page tables supplied from __cpu_up.
> */
> adr_l r3, secondary_data
> - mov_l r12, __secondary_switched
> + adr_l r12, __secondary_switch
> ldrd r4, [r3, #0] @ get secondary_data.pgdir
> ARM_BE8(eor r4, r4, r5) @ Swap r5 and r4 in BE:
> ARM_BE8(eor r5, r4, r5) @ it can be done in 3 steps
> @@ -414,6 +454,7 @@ ENDPROC(__secondary_switched)
> * r4 = TTBR pointer (low word)
> * r5 = TTBR pointer (high word if LPAE)
> * r9 = processor ID
> + * r12 = KASLR offset
> * r13 = *virtual* address to jump to upon completion
> */
> __enable_mmu:
> @@ -451,6 +492,7 @@ ENDPROC(__enable_mmu)
> * r1 = machine ID
> * r2 = atags or dtb pointer
> * r9 = processor ID
> + * r12 = KASLR offset
> * r13 = *virtual* address to jump to upon completion
> *
> * other registers depend on the function called upon completion
> @@ -466,10 +508,52 @@ ENTRY(__turn_mmu_on)
> mov r3, r3
> mov r3, r13
> ret r3
> -__turn_mmu_on_end:
> ENDPROC(__turn_mmu_on)
> - .popsection
>
> +__primary_switch:
> +#ifdef CONFIG_RELOCATABLE
> + adr_l r7, _text @ r7 := __pa(_text)
> + sub r7, r7, #TEXT_OFFSET @ r7 := PHYS_OFFSET
> +
> + adr_l r5, __rel_begin
> + adr_l r6, __rel_end
> + sub r5, r5, r7
> + sub r6, r6, r7
> +
> + add r5, r5, #PAGE_OFFSET
> + add r6, r6, #PAGE_OFFSET
> + add r5, r5, r12
> + add r6, r6, r12
> +
> + adr_l r3, __stubs_start @ __pa(__stubs_start)
> + sub r3, r3, r7 @ offset of __stubs_start
> + add r3, r3, #PAGE_OFFSET @ __va(__stubs_start)
> + sub r3, r3, #0xffff1000 @ subtract VA of stubs section
> +
> +0: cmp r5, r6
> + bge 1f
> + ldm r5!, {r7, r8} @ load next relocation entry
> + cmp r8, #23 @ R_ARM_RELATIVE
> + bne 0b
> + cmp r7, #0xff000000 @ vector page?
> + addgt r7, r7, r3 @ fix up VA offset
> + ldr r8, [r7, r12]
> + add r8, r8, r12
> + str r8, [r7, r12]
> + b 0b
> +1:
> +#endif
> + ldr pc, =__mmap_switched
> +ENDPROC(__primary_switch)
> +
> +#ifdef CONFIG_SMP
> +__secondary_switch:
> + ldr pc, =__secondary_switched
> +ENDPROC(__secondary_switch)
> +#endif
> + .ltorg
> +__turn_mmu_on_end:
> + .popsection
>
> #ifdef CONFIG_SMP_ON_UP
> __HEAD
> @@ -570,6 +654,7 @@ __fixup_pv_table:
> adr_l r6, __pv_phys_pfn_offset
> adr_l r7, __pv_offset @ __pa(__pv_offset)
> mov_l r3, __pv_offset @ __va(__pv_offset)
> + add_kaslr_offset r3, ip
> mvn ip, #0
> subs r3, r7, r3 @ PHYS_OFFSET - PAGE_OFFSET
> mov r0, r8, lsr #PAGE_SHIFT @ convert to PFN
> --
> 2.11.0
>
>
More information about the linux-arm-kernel
mailing list