Commit ace013a5 authored by Ard Biesheuvel's avatar Ard Biesheuvel
Browse files

efi: zboot: Use EFI protocol to remap code/data with the right attributes



Use the recently introduced EFI_MEMORY_ATTRIBUTES_PROTOCOL in the zboot
implementation to set the right attributes for the code and data
sections of the decompressed image, i.e., EFI_MEMORY_RO for code and
EFI_MEMORY_XP for data.

Signed-off-by: default avatarArd Biesheuvel <ardb@kernel.org>
parent 79729f26
Loading
Loading
Loading
Loading
+67 −0
Original line number Diff line number Diff line
@@ -651,3 +651,70 @@ efi_status_t efi_wait_for_key(unsigned long usec, efi_input_key_t *key)

	return status;
}

/**
 * efi_remap_image - Remap a loaded image with the appropriate permissions
 *                   for code and data
 *
 * @image_base:	the base of the image in memory
 * @alloc_size:	the size of the area in memory occupied by the image
 * @code_size:	the size of the leading part of the image containing code
 * 		and read-only data
 *
 * efi_remap_image() uses the EFI memory attribute protocol to remap the code
 * region of the loaded image read-only/executable, and the remainder
 * read-write/non-executable. The code region is assumed to start at the base
 * of the image, and will therefore cover the PE/COFF header as well.
 */
void efi_remap_image(unsigned long image_base, unsigned alloc_size,
		     unsigned long code_size)
{
	efi_guid_t guid = EFI_MEMORY_ATTRIBUTE_PROTOCOL_GUID;
	efi_memory_attribute_protocol_t *memattr;
	efi_status_t status;
	u64 attr;

	/*
	 * If the firmware implements the EFI_MEMORY_ATTRIBUTE_PROTOCOL, let's
	 * invoke it to remap the text/rodata region of the decompressed image
	 * as read-only and the data/bss region as non-executable.
	 */
	status = efi_bs_call(locate_protocol, &guid, NULL, (void **)&memattr);
	if (status != EFI_SUCCESS)
		return;

	// Get the current attributes for the entire region
	status = memattr->get_memory_attributes(memattr, image_base,
						alloc_size, &attr);
	if (status != EFI_SUCCESS) {
		efi_warn("Failed to retrieve memory attributes for image region: 0x%lx\n",
			 status);
		return;
	}

	// Mark the code region as read-only
	status = memattr->set_memory_attributes(memattr, image_base, code_size,
						EFI_MEMORY_RO);
	if (status != EFI_SUCCESS) {
		efi_warn("Failed to remap code region read-only\n");
		return;
	}

	// If the entire region was already mapped as non-exec, clear the
	// attribute from the code region. Otherwise, set it on the data
	// region.
	if (attr & EFI_MEMORY_XP) {
		status = memattr->clear_memory_attributes(memattr, image_base,
							  code_size,
							  EFI_MEMORY_XP);
		if (status != EFI_SUCCESS)
			efi_warn("Failed to remap code region executable\n");
	} else {
		status = memattr->set_memory_attributes(memattr,
							image_base + code_size,
							alloc_size - code_size,
							EFI_MEMORY_XP);
		if (status != EFI_SUCCESS)
			efi_warn("Failed to remap data region non-executable\n");
	}
}
+3 −0
Original line number Diff line number Diff line
@@ -1096,4 +1096,7 @@ struct efi_smbios_type1_record {

const u8 *__efi_get_smbios_string(u8 type, int offset, int recsize);

void efi_remap_image(unsigned long image_base, unsigned alloc_size,
		     unsigned long code_size);

#endif
+2 −0
Original line number Diff line number Diff line
@@ -137,6 +137,8 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab)

	efi_cache_sync_image(image_base, alloc_size, code_size);

	efi_remap_image(image_base, alloc_size, code_size);

	status = efi_stub_common(handle, image, image_base, cmdline_ptr);

free_image: