Skip to content

Commit

Permalink
efi: libstub: ensure allocated memory to be executable
Browse files Browse the repository at this point in the history
There are UEFI versions that restrict execution of memory regions,
preventing the kernel from booting. Parts that needs to be executable
are:

* Area used for trampoline placement.
* All memory regions that the kernel may be relocated before
  and during extraction.

Use DXE services to ensure aforementioned address ranges
to be executable. Only modify attributes that does not
have appropriate attributes.

Signed-off-by: Baskov Evgeniy <baskov@ispras.ru>
Link: https://lore.kernel.org/r/20220303142120.1975-3-baskov@ispras.ru
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
  • Loading branch information
Baskov Evgeniy authored and Ard Biesheuvel committed May 3, 2022
1 parent 3ba75c1 commit 82e0d6d
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 4 deletions.
12 changes: 12 additions & 0 deletions drivers/firmware/efi/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@ config EFI_SOFT_RESERVE

If unsure, say Y.

config EFI_DXE_MEM_ATTRIBUTES
bool "Adjust memory attributes in EFISTUB"
depends on EFI && EFI_STUB && X86
default y
help
UEFI specification does not guarantee all memory to be
accessible for both write and execute as the kernel expects
it to be.
Use DXE services to check and alter memory protection
attributes during boot via EFISTUB to ensure that memory
ranges used by the kernel are writable and executable.

config EFI_PARAMS_FROM_FDT
bool
help
Expand Down
110 changes: 106 additions & 4 deletions drivers/firmware/efi/libstub/x86-stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,110 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
}
}

static void
adjust_memory_range_protection(unsigned long start, unsigned long size)
{
efi_status_t status;
efi_gcd_memory_space_desc_t desc;
unsigned long end, next;
unsigned long rounded_start, rounded_end;
unsigned long unprotect_start, unprotect_size;
int has_system_memory = 0;

if (efi_dxe_table == NULL)
return;

rounded_start = rounddown(start, EFI_PAGE_SIZE);
rounded_end = roundup(start + size, EFI_PAGE_SIZE);

/*
* Don't modify memory region attributes, they are
* already suitable, to lower the possibility to
* encounter firmware bugs.
*/

for (end = start + size; start < end; start = next) {

status = efi_dxe_call(get_memory_space_descriptor, start, &desc);

if (status != EFI_SUCCESS)
return;

next = desc.base_address + desc.length;

/*
* Only system memory is suitable for trampoline/kernel image placement,
* so only this type of memory needs its attributes to be modified.
*/

if (desc.gcd_memory_type != EfiGcdMemoryTypeSystemMemory ||
(desc.attributes & (EFI_MEMORY_RO | EFI_MEMORY_XP)) == 0)
continue;

unprotect_start = max(rounded_start, (unsigned long)desc.base_address);
unprotect_size = min(rounded_end, next) - unprotect_start;

status = efi_dxe_call(set_memory_space_attributes,
unprotect_start, unprotect_size,
EFI_MEMORY_WB);

if (status != EFI_SUCCESS) {
efi_warn("Unable to unprotect memory range [%08lx,%08lx]: %d\n",
unprotect_start,
unprotect_start + unprotect_size,
(int)status);
}
}
}

/*
* Trampoline takes 2 pages and can be loaded in first megabyte of memory
* with its end placed between 128k and 640k where BIOS might start.
* (see arch/x86/boot/compressed/pgtable_64.c)
*
* We cannot find exact trampoline placement since memory map
* can be modified by UEFI, and it can alter the computed address.
*/

#define TRAMPOLINE_PLACEMENT_BASE ((128 - 8)*1024)
#define TRAMPOLINE_PLACEMENT_SIZE (640*1024 - (128 - 8)*1024)

void startup_32(struct boot_params *boot_params);

static void
setup_memory_protection(unsigned long image_base, unsigned long image_size)
{
/*
* Allow execution of possible trampoline used
* for switching between 4- and 5-level page tables
* and relocated kernel image.
*/

adjust_memory_range_protection(TRAMPOLINE_PLACEMENT_BASE,
TRAMPOLINE_PLACEMENT_SIZE);

#ifdef CONFIG_64BIT
if (image_base != (unsigned long)startup_32)
adjust_memory_range_protection(image_base, image_size);
#else
/*
* Clear protection flags on a whole range of possible
* addresses used for KASLR. We don't need to do that
* on x86_64, since KASLR/extraction is performed after
* dedicated identity page tables are built and we only
* need to remove possible protection on relocated image
* itself disregarding further relocations.
*/
adjust_memory_range_protection(LOAD_PHYSICAL_ADDR,
KERNEL_IMAGE_SIZE - LOAD_PHYSICAL_ADDR);
#endif
}

static const efi_char16_t apple[] = L"Apple";

static void setup_quirks(struct boot_params *boot_params)
static void setup_quirks(struct boot_params *boot_params,
unsigned long image_base,
unsigned long image_size)
{
efi_char16_t *fw_vendor = (efi_char16_t *)(unsigned long)
efi_table_attr(efi_system_table, fw_vendor);
Expand All @@ -223,6 +324,9 @@ static void setup_quirks(struct boot_params *boot_params)
if (IS_ENABLED(CONFIG_APPLE_PROPERTIES))
retrieve_apple_device_properties(boot_params);
}

if (IS_ENABLED(CONFIG_EFI_DXE_MEM_ATTRIBUTES))
setup_memory_protection(image_base, image_size);
}

/*
Expand Down Expand Up @@ -342,8 +446,6 @@ static void __noreturn efi_exit(efi_handle_t handle, efi_status_t status)
asm("hlt");
}

void startup_32(struct boot_params *boot_params);

void __noreturn efi_stub_entry(efi_handle_t handle,
efi_system_table_t *sys_table_arg,
struct boot_params *boot_params);
Expand Down Expand Up @@ -798,7 +900,7 @@ unsigned long efi_main(efi_handle_t handle,

setup_efi_pci(boot_params);

setup_quirks(boot_params);
setup_quirks(boot_params, bzimage_addr, buffer_end - buffer_start);

status = exit_boot(boot_params, handle);
if (status != EFI_SUCCESS) {
Expand Down

0 comments on commit 82e0d6d

Please sign in to comment.