Skip to content

Commit 8319e9d

Browse files
ardbiesheuvelIngo Molnar
authored andcommitted
efi/x86: Handle by-ref arguments covering multiple pages in mixed mode
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. Since vmalloc'ed pages (including the vmap'ed stack) are not contiguous in the physical address space, this scheme only works if the referenced memory objects do not cross page boundaries. Currently, the mixed mode runtime service wrappers require that all by-ref arguments that live in the vmalloc space have a size that is a power of 2, and are aligned to that same value. While this is a sensible way to construct an object that is guaranteed not to cross a page boundary, it is overly strict when it comes to checking whether a given object violates this requirement, as we can simply take the physical address of the first and the last byte, and verify that they point into the same physical page. When this check fails, we emit a WARN(), but then simply proceed with the call, which could cause data corruption if the next physical page belongs to a mapping that is entirely unrelated. Given that with vmap'ed stacks, this condition is much more likely to trigger, let's relax the condition a bit, but fail the runtime service call if it does trigger. Fixes: f6697df ("x86/efi: Prevent mixed mode boot corruption with CONFIG_VMAP_STACK=y") Signed-off-by: Ard Biesheuvel <[email protected]> Signed-off-by: Ingo Molnar <[email protected]> Cc: [email protected] Cc: Ingo Molnar <[email protected]> Cc: Thomas Gleixner <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent f80c9f6 commit 8319e9d

File tree

1 file changed

+26
-19
lines changed

1 file changed

+26
-19
lines changed

arch/x86/platform/efi/efi_64.c

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -180,24 +180,21 @@ void efi_sync_low_kernel_mappings(void)
180180
static inline phys_addr_t
181181
virt_to_phys_or_null_size(void *va, unsigned long size)
182182
{
183-
bool bad_size;
183+
phys_addr_t pa;
184184

185185
if (!va)
186186
return 0;
187187

188188
if (virt_addr_valid(va))
189189
return virt_to_phys(va);
190190

191-
/*
192-
* A fully aligned variable on the stack is guaranteed not to
193-
* cross a page bounary. Try to catch strings on the stack by
194-
* checking that 'size' is a power of two.
195-
*/
196-
bad_size = size > PAGE_SIZE || !is_power_of_2(size);
191+
pa = slow_virt_to_phys(va);
197192

198-
WARN_ON(!IS_ALIGNED((unsigned long)va, size) || bad_size);
193+
/* check if the object crosses a page boundary */
194+
if (WARN_ON((pa ^ (pa + size - 1)) & PAGE_MASK))
195+
return 0;
199196

200-
return slow_virt_to_phys(va);
197+
return pa;
201198
}
202199

203200
#define virt_to_phys_or_null(addr) \
@@ -615,8 +612,11 @@ efi_thunk_get_variable(efi_char16_t *name, efi_guid_t *vendor,
615612
phys_attr = virt_to_phys_or_null(attr);
616613
phys_data = virt_to_phys_or_null_size(data, *data_size);
617614

618-
status = efi_thunk(get_variable, phys_name, phys_vendor,
619-
phys_attr, phys_data_size, phys_data);
615+
if (!phys_name || (data && !phys_data))
616+
status = EFI_INVALID_PARAMETER;
617+
else
618+
status = efi_thunk(get_variable, phys_name, phys_vendor,
619+
phys_attr, phys_data_size, phys_data);
620620

621621
spin_unlock_irqrestore(&efi_runtime_lock, flags);
622622

@@ -641,9 +641,11 @@ efi_thunk_set_variable(efi_char16_t *name, efi_guid_t *vendor,
641641
phys_vendor = virt_to_phys_or_null(vnd);
642642
phys_data = virt_to_phys_or_null_size(data, data_size);
643643

644-
/* If data_size is > sizeof(u32) we've got problems */
645-
status = efi_thunk(set_variable, phys_name, phys_vendor,
646-
attr, data_size, phys_data);
644+
if (!phys_name || !phys_data)
645+
status = EFI_INVALID_PARAMETER;
646+
else
647+
status = efi_thunk(set_variable, phys_name, phys_vendor,
648+
attr, data_size, phys_data);
647649

648650
spin_unlock_irqrestore(&efi_runtime_lock, flags);
649651

@@ -670,9 +672,11 @@ efi_thunk_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor,
670672
phys_vendor = virt_to_phys_or_null(vnd);
671673
phys_data = virt_to_phys_or_null_size(data, data_size);
672674

673-
/* If data_size is > sizeof(u32) we've got problems */
674-
status = efi_thunk(set_variable, phys_name, phys_vendor,
675-
attr, data_size, phys_data);
675+
if (!phys_name || !phys_data)
676+
status = EFI_INVALID_PARAMETER;
677+
else
678+
status = efi_thunk(set_variable, phys_name, phys_vendor,
679+
attr, data_size, phys_data);
676680

677681
spin_unlock_irqrestore(&efi_runtime_lock, flags);
678682

@@ -698,8 +702,11 @@ efi_thunk_get_next_variable(unsigned long *name_size,
698702
phys_vendor = virt_to_phys_or_null(vnd);
699703
phys_name = virt_to_phys_or_null_size(name, *name_size);
700704

701-
status = efi_thunk(get_next_variable, phys_name_size,
702-
phys_name, phys_vendor);
705+
if (!phys_name)
706+
status = EFI_INVALID_PARAMETER;
707+
else
708+
status = efi_thunk(get_next_variable, phys_name_size,
709+
phys_name, phys_vendor);
703710

704711
spin_unlock_irqrestore(&efi_runtime_lock, flags);
705712

0 commit comments

Comments
 (0)