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

Dan Rosenberg
2011-05-25T02:05:43Z

On Tue, 2011-05-24 at 19:08 -0400, Dan Rosenberg wrote:
> 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.
>

Also thanks to someone who prefers to remain nameless for pointing out
that this logic also results in the kernel being loaded at
LOAD_PHYSICAL_ADDR about one in four times (because it rounds up). This
will be fixed as well.

-Dan