[linux] 08/08: [x86] efi: Prevent mixed mode boot corruption with CONFIG_VMAP_STACK=y

debian-kernel at lists.debian.org debian-kernel at lists.debian.org
Sun Nov 13 21:01:21 UTC 2016


This is an automated email from the git hooks/post-receive script.

benh pushed a commit to branch master
in repository linux.

commit 9ab8cb710bc5f7817c91cf46e39a6712143543e1
Author: Ben Hutchings <ben at decadent.org.uk>
Date:   Sun Nov 13 21:00:47 2016 +0000

    [x86] efi: Prevent mixed mode boot corruption with CONFIG_VMAP_STACK=y
---
 debian/changelog                                   |   1 +
 ...vent-mixed-mode-boot-corruption-with-conf.patch | 212 +++++++++++++++++++++
 debian/patches/series                              |   1 +
 3 files changed, 214 insertions(+)

diff --git a/debian/changelog b/debian/changelog
index 241903b..3fda4ef 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -19,6 +19,7 @@ linux (4.9~rc5-1~exp1) UNRELEASED; urgency=medium
   * [armel] Drop versatile flavour, which has been broken since version
     4.5~rc4-1~exp1
   * [x86] ethernet: Enable ENA_ETHERNET as module
+  * [x86] efi: Prevent mixed mode boot corruption with CONFIG_VMAP_STACK=y
 
  -- Aurelien Jarno <aurel32 at debian.org>  Sat, 05 Nov 2016 23:17:01 +0100
 
diff --git a/debian/patches/bugfix/x86/x86-efi-prevent-mixed-mode-boot-corruption-with-conf.patch b/debian/patches/bugfix/x86/x86-efi-prevent-mixed-mode-boot-corruption-with-conf.patch
new file mode 100644
index 0000000..959912d
--- /dev/null
+++ b/debian/patches/bugfix/x86/x86-efi-prevent-mixed-mode-boot-corruption-with-conf.patch
@@ -0,0 +1,212 @@
+From: Matt Fleming <matt at codeblueprint.co.uk>
+Date: Sat, 12 Nov 2016 21:04:24 +0000
+Subject: x86/efi: Prevent mixed mode boot corruption with CONFIG_VMAP_STACK=y
+Origin: https://git.kernel.org/linus/f6697df36bdf0bf7fce984605c2918d4a7b4269f
+
+Booting an EFI mixed mode kernel has been crashing since commit:
+
+  e37e43a497d5 ("x86/mm/64: Enable vmapped stacks (CONFIG_HAVE_ARCH_VMAP_STACK=y)")
+
+The user-visible effect in my test setup was the kernel being unable
+to find the root file system ramdisk. This was likely caused by silent
+memory or page table corruption.
+
+Enabling CONFIG_DEBUG_VIRTUAL=y immediately flagged the thunking code as
+abusing virt_to_phys() because it was passing addresses that were not
+part of the kernel direct mapping.
+
+Use the slow version instead, which correctly handles all memory
+regions by performing a page table walk.
+
+Suggested-by: Andy Lutomirski <luto at amacapital.net>
+Signed-off-by: Matt Fleming <matt at codeblueprint.co.uk>
+Cc: Andy Lutomirski <luto at kernel.org>
+Cc: Ard Biesheuvel <ard.biesheuvel at linaro.org>
+Cc: Borislav Petkov <bp at alien8.de>
+Cc: Brian Gerst <brgerst at gmail.com>
+Cc: Denys Vlasenko <dvlasenk at redhat.com>
+Cc: H. Peter Anvin <hpa at zytor.com>
+Cc: Josh Poimboeuf <jpoimboe at redhat.com>
+Cc: Linus Torvalds <torvalds at linux-foundation.org>
+Cc: Peter Zijlstra <peterz at infradead.org>
+Cc: Thomas Gleixner <tglx at linutronix.de>
+Cc: linux-efi at vger.kernel.org
+Link: http://lkml.kernel.org/r/20161112210424.5157-3-matt@codeblueprint.co.uk
+Signed-off-by: Ingo Molnar <mingo at kernel.org>
+---
+ arch/x86/platform/efi/efi_64.c | 80 ++++++++++++++++++++++++++++++------------
+ 1 file changed, 57 insertions(+), 23 deletions(-)
+
+--- a/arch/x86/platform/efi/efi_64.c
++++ b/arch/x86/platform/efi/efi_64.c
+@@ -31,6 +31,7 @@
+ #include <linux/io.h>
+ #include <linux/reboot.h>
+ #include <linux/slab.h>
++#include <linux/ucs2_string.h>
+ 
+ #include <asm/setup.h>
+ #include <asm/page.h>
+@@ -211,6 +212,35 @@ void efi_sync_low_kernel_mappings(void)
+ 	memcpy(pud_efi, pud_k, sizeof(pud_t) * num_entries);
+ }
+ 
++/*
++ * Wrapper for slow_virt_to_phys() that handles NULL addresses.
++ */
++static inline phys_addr_t
++virt_to_phys_or_null_size(void *va, unsigned long size)
++{
++	bool bad_size;
++
++	if (!va)
++		return 0;
++
++	if (virt_addr_valid(va))
++		return virt_to_phys(va);
++
++	/*
++	 * A fully aligned variable on the stack is guaranteed not to
++	 * cross a page bounary. Try to catch strings on the stack by
++	 * checking that 'size' is a power of two.
++	 */
++	bad_size = size > PAGE_SIZE || !is_power_of_2(size);
++
++	WARN_ON(!IS_ALIGNED((unsigned long)va, size) || bad_size);
++
++	return slow_virt_to_phys(va);
++}
++
++#define virt_to_phys_or_null(addr)				\
++	virt_to_phys_or_null_size((addr), sizeof(*(addr)))
++
+ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
+ {
+ 	unsigned long pfn, text;
+@@ -494,8 +524,8 @@ static efi_status_t efi_thunk_get_time(e
+ 
+ 	spin_lock(&rtc_lock);
+ 
+-	phys_tm = virt_to_phys(tm);
+-	phys_tc = virt_to_phys(tc);
++	phys_tm = virt_to_phys_or_null(tm);
++	phys_tc = virt_to_phys_or_null(tc);
+ 
+ 	status = efi_thunk(get_time, phys_tm, phys_tc);
+ 
+@@ -511,7 +541,7 @@ static efi_status_t efi_thunk_set_time(e
+ 
+ 	spin_lock(&rtc_lock);
+ 
+-	phys_tm = virt_to_phys(tm);
++	phys_tm = virt_to_phys_or_null(tm);
+ 
+ 	status = efi_thunk(set_time, phys_tm);
+ 
+@@ -529,9 +559,9 @@ efi_thunk_get_wakeup_time(efi_bool_t *en
+ 
+ 	spin_lock(&rtc_lock);
+ 
+-	phys_enabled = virt_to_phys(enabled);
+-	phys_pending = virt_to_phys(pending);
+-	phys_tm = virt_to_phys(tm);
++	phys_enabled = virt_to_phys_or_null(enabled);
++	phys_pending = virt_to_phys_or_null(pending);
++	phys_tm = virt_to_phys_or_null(tm);
+ 
+ 	status = efi_thunk(get_wakeup_time, phys_enabled,
+ 			     phys_pending, phys_tm);
+@@ -549,7 +579,7 @@ efi_thunk_set_wakeup_time(efi_bool_t ena
+ 
+ 	spin_lock(&rtc_lock);
+ 
+-	phys_tm = virt_to_phys(tm);
++	phys_tm = virt_to_phys_or_null(tm);
+ 
+ 	status = efi_thunk(set_wakeup_time, enabled, phys_tm);
+ 
+@@ -558,6 +588,10 @@ efi_thunk_set_wakeup_time(efi_bool_t ena
+ 	return status;
+ }
+ 
++static unsigned long efi_name_size(efi_char16_t *name)
++{
++	return ucs2_strsize(name, EFI_VAR_NAME_LEN) + 1;
++}
+ 
+ static efi_status_t
+ efi_thunk_get_variable(efi_char16_t *name, efi_guid_t *vendor,
+@@ -567,11 +601,11 @@ efi_thunk_get_variable(efi_char16_t *nam
+ 	u32 phys_name, phys_vendor, phys_attr;
+ 	u32 phys_data_size, phys_data;
+ 
+-	phys_data_size = virt_to_phys(data_size);
+-	phys_vendor = virt_to_phys(vendor);
+-	phys_name = virt_to_phys(name);
+-	phys_attr = virt_to_phys(attr);
+-	phys_data = virt_to_phys(data);
++	phys_data_size = virt_to_phys_or_null(data_size);
++	phys_vendor = virt_to_phys_or_null(vendor);
++	phys_name = virt_to_phys_or_null_size(name, efi_name_size(name));
++	phys_attr = virt_to_phys_or_null(attr);
++	phys_data = virt_to_phys_or_null_size(data, *data_size);
+ 
+ 	status = efi_thunk(get_variable, phys_name, phys_vendor,
+ 			   phys_attr, phys_data_size, phys_data);
+@@ -586,9 +620,9 @@ efi_thunk_set_variable(efi_char16_t *nam
+ 	u32 phys_name, phys_vendor, phys_data;
+ 	efi_status_t status;
+ 
+-	phys_name = virt_to_phys(name);
+-	phys_vendor = virt_to_phys(vendor);
+-	phys_data = virt_to_phys(data);
++	phys_name = virt_to_phys_or_null_size(name, efi_name_size(name));
++	phys_vendor = virt_to_phys_or_null(vendor);
++	phys_data = virt_to_phys_or_null_size(data, data_size);
+ 
+ 	/* If data_size is > sizeof(u32) we've got problems */
+ 	status = efi_thunk(set_variable, phys_name, phys_vendor,
+@@ -605,9 +639,9 @@ efi_thunk_get_next_variable(unsigned lon
+ 	efi_status_t status;
+ 	u32 phys_name_size, phys_name, phys_vendor;
+ 
+-	phys_name_size = virt_to_phys(name_size);
+-	phys_vendor = virt_to_phys(vendor);
+-	phys_name = virt_to_phys(name);
++	phys_name_size = virt_to_phys_or_null(name_size);
++	phys_vendor = virt_to_phys_or_null(vendor);
++	phys_name = virt_to_phys_or_null_size(name, *name_size);
+ 
+ 	status = efi_thunk(get_next_variable, phys_name_size,
+ 			   phys_name, phys_vendor);
+@@ -621,7 +655,7 @@ efi_thunk_get_next_high_mono_count(u32 *
+ 	efi_status_t status;
+ 	u32 phys_count;
+ 
+-	phys_count = virt_to_phys(count);
++	phys_count = virt_to_phys_or_null(count);
+ 	status = efi_thunk(get_next_high_mono_count, phys_count);
+ 
+ 	return status;
+@@ -633,7 +667,7 @@ efi_thunk_reset_system(int reset_type, e
+ {
+ 	u32 phys_data;
+ 
+-	phys_data = virt_to_phys(data);
++	phys_data = virt_to_phys_or_null_size(data, data_size);
+ 
+ 	efi_thunk(reset_system, reset_type, status, data_size, phys_data);
+ }
+@@ -661,9 +695,9 @@ efi_thunk_query_variable_info(u32 attr,
+ 	if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)
+ 		return EFI_UNSUPPORTED;
+ 
+-	phys_storage = virt_to_phys(storage_space);
+-	phys_remaining = virt_to_phys(remaining_space);
+-	phys_max = virt_to_phys(max_variable_size);
++	phys_storage = virt_to_phys_or_null(storage_space);
++	phys_remaining = virt_to_phys_or_null(remaining_space);
++	phys_max = virt_to_phys_or_null(max_variable_size);
+ 
+ 	status = efi_thunk(query_variable_info, attr, phys_storage,
+ 			   phys_remaining, phys_max);
diff --git a/debian/patches/series b/debian/patches/series
index a15bfa2..d058b07 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -47,6 +47,7 @@ debian/fanotify-taint-on-use-of-fanotify_access_permissions.patch
 
 # Arch bug fixes
 bugfix/arm64/arm64-mm-limit-task_size_64-for-compatibility.patch
+bugfix/x86/x86-efi-prevent-mixed-mode-boot-corruption-with-conf.patch
 
 # Arch features
 features/mips/MIPS-increase-MAX-PHYSMEM-BITS-on-Loongson-3-only.patch

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/kernel/linux.git



More information about the Kernel-svn-changes mailing list