[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