[PATCH 6.1 214/215] x86/efistub: Give up if memory attribute protocol returns an error

Greg Kroah-Hartman
2024-03-04T21:52:28Z

From: Ard Biesheuvel <ardb@kernel.org>

commit a7a6a01f88e87dec4bf2365571dd2dc7403d52d0 upstream.

The recently introduced EFI memory attributes protocol should be used
if it exists to ensure that the memory allocation created for the kernel
permits execution. This is needed for compatibility with tightened
requirements related to Windows logo certification for x86 PCs.

Currently, we simply strip the execute protect (XP) attribute from the
entire range, but this might be rejected under some firmware security
policies, and so in a subsequent patch, this will be changed to only
strip XP from the executable region that runs early, and make it
read-only (RO) as well.

In order to catch any issues early, ensure that the memory attribute
protocol works as intended, and give up if it produces spurious errors.

Note that the DXE services based fallback was always based on best
effort, so don't propagate any errors returned by that API.

Fixes: a1b87d54f4e4 ("x86/efistub: Avoid legacy decompressor when doing EFI boot")
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
---
drivers/firmware/efi/libstub/x86-stub.c | 24 ++++++++++++++----------
drivers/firmware/efi/libstub/x86-stub.h | 4 ++--
2 files changed, 16 insertions(+), 12 deletions(-)

--- a/drivers/firmware/efi/libstub/x86-stub.c
+++ b/drivers/firmware/efi/libstub/x86-stub.c
@@ -212,8 +212,8 @@ static void retrieve_apple_device_proper
}
}

-void efi_adjust_memory_range_protection(unsigned long start,
- unsigned long size)
+efi_status_t efi_adjust_memory_range_protection(unsigned long start,
+ unsigned long size)
{
efi_status_t status;
efi_gcd_memory_space_desc_t desc;
@@ -225,13 +225,17 @@ void efi_adjust_memory_range_protection(
rounded_end = roundup(start + size, EFI_PAGE_SIZE);

if (memattr != NULL) {
- efi_call_proto(memattr, clear_memory_attributes, rounded_start,
- rounded_end - rounded_start, EFI_MEMORY_XP);
- return;
+ status = efi_call_proto(memattr, clear_memory_attributes,
+ rounded_start,
+ rounded_end - rounded_start,
+ EFI_MEMORY_XP);
+ if (status != EFI_SUCCESS)
+ efi_warn("Failed to clear EFI_MEMORY_XP attribute\n");
+ return status;
}

if (efi_dxe_table == NULL)
- return;
+ return EFI_SUCCESS;

/*
* Don't modify memory region attributes, they are
@@ -244,7 +248,7 @@ void efi_adjust_memory_range_protection(
status = efi_dxe_call(get_memory_space_descriptor, start, &desc);

if (status != EFI_SUCCESS)
- return;
+ break;

next = desc.base_address + desc.length;

@@ -269,8 +273,10 @@ void efi_adjust_memory_range_protection(
unprotect_start,
unprotect_start + unprotect_size,
status);
+ break;
}
}
+ return EFI_SUCCESS;
}

static efi_char16_t *efistub_fw_vendor(void)
@@ -800,9 +806,7 @@ static efi_status_t efi_decompress_kerne

*kernel_entry = addr + entry;

- efi_adjust_memory_range_protection(addr, kernel_total_size);
-
- return EFI_SUCCESS;
+ return efi_adjust_memory_range_protection(addr, kernel_total_size);
}

static void __noreturn enter_kernel(unsigned long kernel_addr,
--- a/drivers/firmware/efi/libstub/x86-stub.h
+++ b/drivers/firmware/efi/libstub/x86-stub.h
@@ -5,8 +5,8 @@
extern void trampoline_32bit_src(void *, bool);
extern const u16 trampoline_ljmp_imm_offset;

-void efi_adjust_memory_range_protection(unsigned long start,
- unsigned long size);
+efi_status_t efi_adjust_memory_range_protection(unsigned long start,
+ unsigned long size);

#ifdef CONFIG_X86_64
efi_status_t efi_setup_5level_paging(void);