[azure] x86/KASLR: Fix kexec kernel boot crash when KASLR randomization fails

Message ID 1504900337-21269-1-git-send-email-marcelo.cerri@canonical.com
State New
Headers show
Series
  • [azure] x86/KASLR: Fix kexec kernel boot crash when KASLR randomization fails
Related show

Commit Message

Marcelo Henrique Cerri Sept. 8, 2017, 7:52 p.m.
From: Baoquan He <bhe@redhat.com>

BugLink: http://bugs.launchpad.net/bugs/1702515

Dave found that a kdump kernel with KASLR enabled will reset to the BIOS
immediately if physical randomization failed to find a new position for
the kernel. A kernel with the 'nokaslr' option works in this case.

The reason is that KASLR will install a new page table for the identity
mapping, while it missed building it for the original kernel location
if KASLR physical randomization fails.

This only happens in the kexec/kdump kernel, because the identity mapping
has been built for kexec/kdump in the 1st kernel for the whole memory by
calling init_pgtable(). Here if physical randomizaiton fails, it won't build
the identity mapping for the original area of the kernel but change to a
new page table '_pgtable'. Then the kernel will triple fault immediately
caused by no identity mappings.

The normal kernel won't see this bug, because it comes here via startup_32()
and CR3 will be set to _pgtable already. In startup_32() the identity
mapping is built for the 0~4G area. In KASLR we just append to the existing
area instead of entirely overwriting it for on-demand identity mapping
building. So the identity mapping for the original area of kernel is still
there.

To fix it we just switch to the new identity mapping page table when physical
KASLR succeeds. Otherwise we keep the old page table unchanged just like
"nokaslr" does.

Signed-off-by: Baoquan He <bhe@redhat.com>
Signed-off-by: Dave Young <dyoung@redhat.com>
Acked-by: Kees Cook <keescook@chromium.org>
Cc: Borislav Petkov <bp@suse.de>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Garnier <thgarnie@google.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Yinghai Lu <yinghai@kernel.org>
Link: http://lkml.kernel.org/r/1493278940-5885-1-git-send-email-bhe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
(cherry picked from commit da63b6b20077469bd6bd96e07991ce145fc4fbc4)
Signed-off-by: Marcelo Henrique Cerri <marcelo.cerri@canonical.com>
---
 arch/x86/boot/compressed/kaslr.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

Comments

Marcelo Henrique Cerri Sept. 9, 2017, 1:44 a.m. | #1
On Fri, Sep 08, 2017 at 04:52:17PM -0300, Marcelo Henrique Cerri wrote:
> From: Baoquan He <bhe@redhat.com>
> 
> BugLink: http://bugs.launchpad.net/bugs/1702515

BugLink: http://bugs.launchpad.net/bugs/1712867

> 
> Dave found that a kdump kernel with KASLR enabled will reset to the BIOS
> immediately if physical randomization failed to find a new position for
> the kernel. A kernel with the 'nokaslr' option works in this case.
> 
> The reason is that KASLR will install a new page table for the identity
> mapping, while it missed building it for the original kernel location
> if KASLR physical randomization fails.
> 
> This only happens in the kexec/kdump kernel, because the identity mapping
> has been built for kexec/kdump in the 1st kernel for the whole memory by
> calling init_pgtable(). Here if physical randomizaiton fails, it won't build
> the identity mapping for the original area of the kernel but change to a
> new page table '_pgtable'. Then the kernel will triple fault immediately
> caused by no identity mappings.
> 
> The normal kernel won't see this bug, because it comes here via startup_32()
> and CR3 will be set to _pgtable already. In startup_32() the identity
> mapping is built for the 0~4G area. In KASLR we just append to the existing
> area instead of entirely overwriting it for on-demand identity mapping
> building. So the identity mapping for the original area of kernel is still
> there.
> 
> To fix it we just switch to the new identity mapping page table when physical
> KASLR succeeds. Otherwise we keep the old page table unchanged just like
> "nokaslr" does.
> 
> Signed-off-by: Baoquan He <bhe@redhat.com>
> Signed-off-by: Dave Young <dyoung@redhat.com>
> Acked-by: Kees Cook <keescook@chromium.org>
> Cc: Borislav Petkov <bp@suse.de>
> Cc: Dave Jiang <dave.jiang@intel.com>
> Cc: Linus Torvalds <torvalds@linux-foundation.org>
> Cc: Peter Zijlstra <peterz@infradead.org>
> Cc: Thomas Garnier <thgarnie@google.com>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Yinghai Lu <yinghai@kernel.org>
> Link: http://lkml.kernel.org/r/1493278940-5885-1-git-send-email-bhe@redhat.com
> Signed-off-by: Ingo Molnar <mingo@kernel.org>
> (cherry picked from commit da63b6b20077469bd6bd96e07991ce145fc4fbc4)
> Signed-off-by: Marcelo Henrique Cerri <marcelo.cerri@canonical.com>
> ---
>  arch/x86/boot/compressed/kaslr.c | 11 +++++++++--
>  1 file changed, 9 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
> index 8fad8a64d670..1199a74f867c 100644
> --- a/arch/x86/boot/compressed/kaslr.c
> +++ b/arch/x86/boot/compressed/kaslr.c
> @@ -594,10 +594,17 @@ void choose_random_location(unsigned long input,
>  			add_identity_map(random_addr, output_size);
>  			*output = random_addr;
>  		}
> +
> +		/*
> +		 * This loads the identity mapping page table.
> +		 * This should only be done if a new physical address
> +		 * is found for the kernel, otherwise we should keep
> +		 * the old page table to make it be like the "nokaslr"
> +		 * case.
> +		 */
> +		finalize_identity_maps();
>  	}
>  
> -	/* This actually loads the identity pagetable on x86_64. */
> -	finalize_identity_maps();
>  
>  	/* Pick random virtual address starting from LOAD_PHYSICAL_ADDR. */
>  	if (IS_ENABLED(CONFIG_X86_64))
> -- 
> 2.7.4
>
Colin Ian King Sept. 11, 2017, 1 p.m. | #2
On 09/09/17 02:44, Marcelo Henrique Cerri wrote:
> On Fri, Sep 08, 2017 at 04:52:17PM -0300, Marcelo Henrique Cerri wrote:
>> From: Baoquan He <bhe@redhat.com>
>>
>> BugLink: http://bugs.launchpad.net/bugs/1702515
> 
> BugLink: http://bugs.launchpad.net/bugs/1712867
> 
>>
>> Dave found that a kdump kernel with KASLR enabled will reset to the BIOS
>> immediately if physical randomization failed to find a new position for
>> the kernel. A kernel with the 'nokaslr' option works in this case.
>>
>> The reason is that KASLR will install a new page table for the identity
>> mapping, while it missed building it for the original kernel location
>> if KASLR physical randomization fails.
>>
>> This only happens in the kexec/kdump kernel, because the identity mapping
>> has been built for kexec/kdump in the 1st kernel for the whole memory by
>> calling init_pgtable(). Here if physical randomizaiton fails, it won't build
>> the identity mapping for the original area of the kernel but change to a
>> new page table '_pgtable'. Then the kernel will triple fault immediately
>> caused by no identity mappings.
>>
>> The normal kernel won't see this bug, because it comes here via startup_32()
>> and CR3 will be set to _pgtable already. In startup_32() the identity
>> mapping is built for the 0~4G area. In KASLR we just append to the existing
>> area instead of entirely overwriting it for on-demand identity mapping
>> building. So the identity mapping for the original area of kernel is still
>> there.
>>
>> To fix it we just switch to the new identity mapping page table when physical
>> KASLR succeeds. Otherwise we keep the old page table unchanged just like
>> "nokaslr" does.
>>
>> Signed-off-by: Baoquan He <bhe@redhat.com>
>> Signed-off-by: Dave Young <dyoung@redhat.com>
>> Acked-by: Kees Cook <keescook@chromium.org>
>> Cc: Borislav Petkov <bp@suse.de>
>> Cc: Dave Jiang <dave.jiang@intel.com>
>> Cc: Linus Torvalds <torvalds@linux-foundation.org>
>> Cc: Peter Zijlstra <peterz@infradead.org>
>> Cc: Thomas Garnier <thgarnie@google.com>
>> Cc: Thomas Gleixner <tglx@linutronix.de>
>> Cc: Yinghai Lu <yinghai@kernel.org>
>> Link: http://lkml.kernel.org/r/1493278940-5885-1-git-send-email-bhe@redhat.com
>> Signed-off-by: Ingo Molnar <mingo@kernel.org>
>> (cherry picked from commit da63b6b20077469bd6bd96e07991ce145fc4fbc4)
>> Signed-off-by: Marcelo Henrique Cerri <marcelo.cerri@canonical.com>
>> ---
>>  arch/x86/boot/compressed/kaslr.c | 11 +++++++++--
>>  1 file changed, 9 insertions(+), 2 deletions(-)
>>
>> diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
>> index 8fad8a64d670..1199a74f867c 100644
>> --- a/arch/x86/boot/compressed/kaslr.c
>> +++ b/arch/x86/boot/compressed/kaslr.c
>> @@ -594,10 +594,17 @@ void choose_random_location(unsigned long input,
>>  			add_identity_map(random_addr, output_size);
>>  			*output = random_addr;
>>  		}
>> +
>> +		/*
>> +		 * This loads the identity mapping page table.
>> +		 * This should only be done if a new physical address
>> +		 * is found for the kernel, otherwise we should keep
>> +		 * the old page table to make it be like the "nokaslr"
>> +		 * case.
>> +		 */
>> +		finalize_identity_maps();
>>  	}
>>  
>> -	/* This actually loads the identity pagetable on x86_64. */
>> -	finalize_identity_maps();
>>  
>>  	/* Pick random virtual address starting from LOAD_PHYSICAL_ADDR. */
>>  	if (IS_ENABLED(CONFIG_X86_64))
>> -- 
>> 2.7.4
>>
>>
>>
Has positive test results. Just needs the buglink to be corrected before
applying.

Acked-by: Colin Ian King <colin.king@canonical.com>
Marcelo Henrique Cerri Sept. 15, 2017, 9:25 p.m. | #3
Applied with the correct BugLink.

Acked-by: Marcelo Henrique Cerri <marcelo.cerri@canonical.com>

Patch

diff --git a/arch/x86/boot/compressed/kaslr.c b/arch/x86/boot/compressed/kaslr.c
index 8fad8a64d670..1199a74f867c 100644
--- a/arch/x86/boot/compressed/kaslr.c
+++ b/arch/x86/boot/compressed/kaslr.c
@@ -594,10 +594,17 @@  void choose_random_location(unsigned long input,
 			add_identity_map(random_addr, output_size);
 			*output = random_addr;
 		}
+
+		/*
+		 * This loads the identity mapping page table.
+		 * This should only be done if a new physical address
+		 * is found for the kernel, otherwise we should keep
+		 * the old page table to make it be like the "nokaslr"
+		 * case.
+		 */
+		finalize_identity_maps();
 	}
 
-	/* This actually loads the identity pagetable on x86_64. */
-	finalize_identity_maps();
 
 	/* Pick random virtual address starting from LOAD_PHYSICAL_ADDR. */
 	if (IS_ENABLED(CONFIG_X86_64))