Re: [RFC][PATCH] Randomize kernel base address on boot

Dan Rosenberg
2011-05-24T23:08:57Z

On Tue, 2011-05-24 at 16:31 -0400, Dan Rosenberg wrote:
> This introduces CONFIG_RANDOMIZE_BASE, which randomizes the address at
> which the kernel is decompressed at boot as a security feature that
> deters exploit attempts relying on knowledge of the location of kernel
> internals. The default values of the kptr_restrict and dmesg_restrict
> sysctls are set to (1) when this is enabled, since hiding kernel
> pointers is necessary to preserve the secrecy of the randomized base
> address.

> diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S
> index 67a655a..2680db0 100644
> --- a/arch/x86/boot/compressed/head_32.S
> +++ b/arch/x86/boot/compressed/head_32.S
> @@ -69,12 +69,75 @@ ENTRY(startup_32)
> */
>
> #ifdef CONFIG_RELOCATABLE
> +#ifdef CONFIG_RANDOMIZE_BASE
> +
> + /* Standard check for cpuid */
> + pushfl
> + popl %eax
> + movl %eax, %ebx
> + xorl $0x200000, %eax
> + pushl %eax
> + popfl
> + pushfl
> + popl %eax
> + cmpl %eax, %ebx
> + jz 4f
> +
> + /* Check for cpuid 1 */
> + movl $0x0, %eax
> + cpuid
> + cmpl $0x1, %eax
> + jb 4f
> +
> + movl $0x1, %eax
> + cpuid
> + xor %eax, %eax
> +
> + /* RDRAND is bit 30 */
> + testl $0x4000000, %ecx
> + jnz 1f
> +
> + /* RDTSC is bit 4 */
> + testl $0x10, %edx
> + jnz 3f
> +
> + /* Nothing is supported */
> + jmp 4f
> +1:
> + /* RDRAND sets carry bit on success, otherwise we should try
> + * again. */
> + movl $0x10, %ecx
> +2:
> + /* rdrand %eax */
> + .byte 0x0f, 0xc7, 0xf0
> + jc 4f
> + loop 2b
> +
> + /* Fall through: if RDRAND is supported but fails, use RDTSC,
> + * which is guaranteed to be supported. */
> +3:
> + rdtsc
> + shll $0xc, %eax
> +4:
> + /* Maximum offset at 64mb to be safe */
> + andl $0x3ffffff, %eax
> + movl %ebp, %ebx
> + addl %eax, %ebx
> +#else
> movl %ebp, %ebx
> +#endif
> movl BP_kernel_alignment(%esi), %eax
> decl %eax
> addl %eax, %ebx
> notl %eax
> andl %eax, %ebx
> +
> + /* LOAD_PHSYICAL_ADDR is the minimum safe address we can
> + * decompress at. */
> + cmpl $LOAD_PHYSICAL_ADDR, %ebx
> + jae 1f
> + movl $LOAD_PHYSICAL_ADDR, %ebx
> +1:
> #else
> movl $LOAD_PHYSICAL_ADDR, %ebx
> #endif
> diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
> index 35af09d..6a05219 100644
> --- a/arch/x86/boot/compressed/head_64.S
> +++ b/arch/x86/boot/compressed/head_64.S
> @@ -90,6 +90,13 @@ ENTRY(startup_32)
> addl %eax, %ebx
> notl %eax
> andl %eax, %ebx
> +
> + /* LOAD_PHYSICAL_ADDR is the minimum safe address we can
> + * decompress at. */
> + cmpl $LOAD_PHYSICAL_ADDR, %ebx
> + jae 1f
> + movl $LOAD_PHYSICAL_ADDR, %ebx
> +1:
> #else
> movl $LOAD_PHYSICAL_ADDR, %ebx
> #endif
> @@ -191,7 +198,7 @@ no_longmode:
> * it may change in the future.
> */
> .code64
> - .org 0x200
> + .org 0x300
> ENTRY(startup_64)
> /*
> * We come here either from startup_32 or directly from a
> @@ -232,6 +239,13 @@ ENTRY(startup_64)
> addq %rax, %rbp
> notq %rax
> andq %rax, %rbp
> +
> + /* LOAD_PHYSICAL_ADDR is the minimum safe address we can
> + * decompress at. */
> + cmpq $LOAD_PHYSICAL_ADDR, %rbp
> + jae 1f
> + movq $LOAD_PHYSICAL_ADDR, %rbp
> +1:
> #else
> movq $LOAD_PHYSICAL_ADDR, %rbp
> #endif

Thanks to Kees Cook for noticing that I didn't clear %eax before jumping
to my "nothing supported" (4) label. This would have just used the
flags as "randomness", but it's still wrong and I'll fix it. Next
version will have a fallback of using the BIOS signature instead anyway.

-Dan