Re: [PATCH 1/3] efi/x86: align GUIDs to their size in the mixed mode runtime wrapper

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On 2/13/20 11:21 AM, Ard Biesheuvel wrote:
The mixed mode runtime wrappers are fragile when it comes to how the
memory referred to by its pointer arguments are laid out in memory,
due to the fact that it translates these addresses to physical addresses
that the runtime services can dereference when running in 1:1 mode.

As a quick (i.e., backportable) fix, copy GUID pointer arguments to
the local stack into a buffer that is naturally aligned to its size,
so that is guaranteed to cover only one physical page.

Note that on x86, we cannot rely on the stack pointer being aligned
the way the compiler expects, so we need to allocate an 8-byte aligned
buffer of sufficient size, and copy the GUID into that buffer at an
offset that is aligned to 16 bytes.

Reported-by: Hans de Goede <hdegoede@xxxxxxxxxx>
Signed-off-by: Ard Biesheuvel <ardb@xxxxxxxxxx>

I can confirm that this fixes the WARN_ON triggering I was seeing,
thanks:

Tested-by: Hans de Goede <hdegoede@xxxxxxxxxx>

Regards,

Hans



---
  arch/x86/platform/efi/efi_64.c | 25 ++++++++++++++++----
  1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c
index fa8506e76bbe..543edfdcd1b9 100644
--- a/arch/x86/platform/efi/efi_64.c
+++ b/arch/x86/platform/efi/efi_64.c
@@ -658,6 +658,8 @@ static efi_status_t
  efi_thunk_get_variable(efi_char16_t *name, efi_guid_t *vendor,
  		       u32 *attr, unsigned long *data_size, void *data)
  {
+	u8 buf[24] __aligned(8);
+	efi_guid_t *vnd = PTR_ALIGN((efi_guid_t *)buf, sizeof(*vnd));
  	efi_status_t status;
  	u32 phys_name, phys_vendor, phys_attr;
  	u32 phys_data_size, phys_data;
@@ -665,8 +667,10 @@ efi_thunk_get_variable(efi_char16_t *name, efi_guid_t *vendor,
spin_lock_irqsave(&efi_runtime_lock, flags); + *vnd = *vendor;
+
  	phys_data_size = virt_to_phys_or_null(data_size);
-	phys_vendor = virt_to_phys_or_null(vendor);
+	phys_vendor = virt_to_phys_or_null(vnd);
  	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);
@@ -683,14 +687,18 @@ static efi_status_t
  efi_thunk_set_variable(efi_char16_t *name, efi_guid_t *vendor,
  		       u32 attr, unsigned long data_size, void *data)
  {
+	u8 buf[24] __aligned(8);
+	efi_guid_t *vnd = PTR_ALIGN((efi_guid_t *)buf, sizeof(*vnd));
  	u32 phys_name, phys_vendor, phys_data;
  	efi_status_t status;
  	unsigned long flags;
spin_lock_irqsave(&efi_runtime_lock, flags); + *vnd = *vendor;
+
  	phys_name = virt_to_phys_or_null_size(name, efi_name_size(name));
-	phys_vendor = virt_to_phys_or_null(vendor);
+	phys_vendor = virt_to_phys_or_null(vnd);
  	phys_data = virt_to_phys_or_null_size(data, data_size);
/* If data_size is > sizeof(u32) we've got problems */
@@ -707,6 +715,8 @@ efi_thunk_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor,
  				   u32 attr, unsigned long data_size,
  				   void *data)
  {
+	u8 buf[24] __aligned(8);
+	efi_guid_t *vnd = PTR_ALIGN((efi_guid_t *)buf, sizeof(*vnd));
  	u32 phys_name, phys_vendor, phys_data;
  	efi_status_t status;
  	unsigned long flags;
@@ -714,8 +724,10 @@ efi_thunk_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor,
  	if (!spin_trylock_irqsave(&efi_runtime_lock, flags))
  		return EFI_NOT_READY;
+ *vnd = *vendor;
+
  	phys_name = virt_to_phys_or_null_size(name, efi_name_size(name));
-	phys_vendor = virt_to_phys_or_null(vendor);
+	phys_vendor = virt_to_phys_or_null(vnd);
  	phys_data = virt_to_phys_or_null_size(data, data_size);
/* If data_size is > sizeof(u32) we've got problems */
@@ -732,14 +744,18 @@ efi_thunk_get_next_variable(unsigned long *name_size,
  			    efi_char16_t *name,
  			    efi_guid_t *vendor)
  {
+	u8 buf[24] __aligned(8);
+	efi_guid_t *vnd = PTR_ALIGN((efi_guid_t *)buf, sizeof(*vnd));
  	efi_status_t status;
  	u32 phys_name_size, phys_name, phys_vendor;
  	unsigned long flags;
spin_lock_irqsave(&efi_runtime_lock, flags); + *vnd = *vendor;
+
  	phys_name_size = virt_to_phys_or_null(name_size);
-	phys_vendor = virt_to_phys_or_null(vendor);
+	phys_vendor = virt_to_phys_or_null(vnd);
  	phys_name = virt_to_phys_or_null_size(name, *name_size);
status = efi_thunk(get_next_variable, phys_name_size,
@@ -747,6 +763,7 @@ efi_thunk_get_next_variable(unsigned long *name_size,
spin_unlock_irqrestore(&efi_runtime_lock, flags); + *vendor = *vnd;
  	return status;
  }




[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux