]> git.itanic.dy.fi Git - linux-stable/commitdiff
x86/efistub: Remap kernel text read-only before dropping NX attribute
authorArd Biesheuvel <ardb@kernel.org>
Thu, 25 Jan 2024 13:32:07 +0000 (14:32 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 10 Apr 2024 14:38:23 +0000 (16:38 +0200)
commit 9c55461040a9264b7e44444c53d26480b438eda6 upstream.

Currently, the EFI stub invokes the EFI memory attributes protocol to
strip any NX restrictions from the entire loaded kernel, resulting in
all code and data being mapped read-write-execute.

The point of the EFI memory attributes protocol is to remove the need
for all memory allocations to be mapped with both write and execute
permissions by default, and make it the OS loader's responsibility to
transition data mappings to code mappings where appropriate.

Even though the UEFI specification does not appear to leave room for
denying memory attribute changes based on security policy, let's be
cautious and avoid relying on the ability to create read-write-execute
mappings. This is trivially achievable, given that the amount of kernel
code executing via the firmware's 1:1 mapping is rather small and
limited to the .head.text region. So let's drop the NX restrictions only
on that subregion, but not before remapping it as read-only first.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/x86/boot/compressed/Makefile
arch/x86/boot/compressed/misc.c
arch/x86/include/asm/boot.h
drivers/firmware/efi/libstub/x86-stub.c

index f19c038409aa0d3eb20269c574faa7037221ea04..e9522c6893bee0ea678f146b2067ea20a712bff2 100644 (file)
@@ -84,7 +84,7 @@ LDFLAGS_vmlinux += -T
 hostprogs      := mkpiggy
 HOST_EXTRACFLAGS += -I$(srctree)/tools/include
 
-sed-voffset := -e 's/^\([0-9a-fA-F]*\) [ABCDGRSTVW] \(_text\|__bss_start\|_end\)$$/\#define VO_\2 _AC(0x\1,UL)/p'
+sed-voffset := -e 's/^\([0-9a-fA-F]*\) [ABCDGRSTVW] \(_text\|__start_rodata\|__bss_start\|_end\)$$/\#define VO_\2 _AC(0x\1,UL)/p'
 
 quiet_cmd_voffset = VOFFSET $@
       cmd_voffset = $(NM) $< | sed -n $(sed-voffset) > $@
index 6c5c190a4d867cfda84853997943c6e8ed7cb551..ee0fac468e7f76753fe1d76da836514ecb92f07a 100644 (file)
@@ -330,6 +330,7 @@ static size_t parse_elf(void *output)
        return ehdr.e_entry - LOAD_PHYSICAL_ADDR;
 }
 
+const unsigned long kernel_text_size = VO___start_rodata - VO__text;
 const unsigned long kernel_total_size = VO__end - VO__text;
 
 static u8 boot_heap[BOOT_HEAP_SIZE] __aligned(4);
index a38cc0afc90a0b90a65340bed48c7db243614b35..a3e0be0470a400b9e31a008a019de2453f950a09 100644 (file)
@@ -81,6 +81,7 @@
 
 #ifndef __ASSEMBLY__
 extern unsigned int output_len;
+extern const unsigned long kernel_text_size;
 extern const unsigned long kernel_total_size;
 
 unsigned long decompress_kernel(unsigned char *outbuf, unsigned long virt_addr,
index 0c82cafb9d719c2d42d9f3fc3d5ce64268516d8f..19b51dc34f67047fc1643306e4bdf6b3ec078f20 100644 (file)
@@ -238,6 +238,15 @@ efi_status_t efi_adjust_memory_range_protection(unsigned long start,
        rounded_end = roundup(start + size, EFI_PAGE_SIZE);
 
        if (memattr != NULL) {
+               status = efi_call_proto(memattr, set_memory_attributes,
+                                       rounded_start,
+                                       rounded_end - rounded_start,
+                                       EFI_MEMORY_RO);
+               if (status != EFI_SUCCESS) {
+                       efi_warn("Failed to set EFI_MEMORY_RO attribute\n");
+                       return status;
+               }
+
                status = efi_call_proto(memattr, clear_memory_attributes,
                                        rounded_start,
                                        rounded_end - rounded_start,
@@ -818,7 +827,7 @@ static efi_status_t efi_decompress_kernel(unsigned long *kernel_entry)
 
        *kernel_entry = addr + entry;
 
-       return efi_adjust_memory_range_protection(addr, kernel_total_size);
+       return efi_adjust_memory_range_protection(addr, kernel_text_size);
 }
 
 static void __noreturn enter_kernel(unsigned long kernel_addr,