Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/efistub: Give up if memory attribute protocol returns an error

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>

+16 -12
+14 -10
drivers/firmware/efi/libstub/x86-stub.c
··· 223 223 } 224 224 } 225 225 226 - void efi_adjust_memory_range_protection(unsigned long start, 227 - unsigned long size) 226 + efi_status_t efi_adjust_memory_range_protection(unsigned long start, 227 + unsigned long size) 228 228 { 229 229 efi_status_t status; 230 230 efi_gcd_memory_space_desc_t desc; ··· 236 236 rounded_end = roundup(start + size, EFI_PAGE_SIZE); 237 237 238 238 if (memattr != NULL) { 239 - efi_call_proto(memattr, clear_memory_attributes, rounded_start, 240 - rounded_end - rounded_start, EFI_MEMORY_XP); 241 - return; 239 + status = efi_call_proto(memattr, clear_memory_attributes, 240 + rounded_start, 241 + rounded_end - rounded_start, 242 + EFI_MEMORY_XP); 243 + if (status != EFI_SUCCESS) 244 + efi_warn("Failed to clear EFI_MEMORY_XP attribute\n"); 245 + return status; 242 246 } 243 247 244 248 if (efi_dxe_table == NULL) 245 - return; 249 + return EFI_SUCCESS; 246 250 247 251 /* 248 252 * Don't modify memory region attributes, they are ··· 259 255 status = efi_dxe_call(get_memory_space_descriptor, start, &desc); 260 256 261 257 if (status != EFI_SUCCESS) 262 - return; 258 + break; 263 259 264 260 next = desc.base_address + desc.length; 265 261 ··· 284 280 unprotect_start, 285 281 unprotect_start + unprotect_size, 286 282 status); 283 + break; 287 284 } 288 285 } 286 + return EFI_SUCCESS; 289 287 } 290 288 291 289 static void setup_unaccepted_memory(void) ··· 811 805 812 806 *kernel_entry = addr + entry; 813 807 814 - efi_adjust_memory_range_protection(addr, kernel_total_size); 815 - 816 - return EFI_SUCCESS; 808 + return efi_adjust_memory_range_protection(addr, kernel_total_size); 817 809 } 818 810 819 811 static void __noreturn enter_kernel(unsigned long kernel_addr,
+2 -2
drivers/firmware/efi/libstub/x86-stub.h
··· 5 5 extern void trampoline_32bit_src(void *, bool); 6 6 extern const u16 trampoline_ljmp_imm_offset; 7 7 8 - void efi_adjust_memory_range_protection(unsigned long start, 9 - unsigned long size); 8 + efi_status_t efi_adjust_memory_range_protection(unsigned long start, 9 + unsigned long size); 10 10 11 11 #ifdef CONFIG_X86_64 12 12 efi_status_t efi_setup_5level_paging(void);