Commit 2b5fe07a authored by Ard Biesheuvel's avatar Ard Biesheuvel Committed by Catalin Marinas

arm64: efi: invoke EFI_RNG_PROTOCOL to supply KASLR randomness

Since arm64 does not use a decompressor that supplies an execution
environment where it is feasible to some extent to provide a source of
randomness, the arm64 KASLR kernel depends on the bootloader to supply
some random bits in the /chosen/kaslr-seed DT property upon kernel entry.

On UEFI systems, we can use the EFI_RNG_PROTOCOL, if supplied, to obtain
some random bits. At the same time, use it to randomize the offset of the
kernel Image in physical memory.
Reviewed-by: default avatarMatt Fleming <matt@codeblueprint.co.uk>
Signed-off-by: default avatarArd Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent 48fcb2d0
...@@ -810,6 +810,11 @@ config RANDOMIZE_BASE ...@@ -810,6 +810,11 @@ config RANDOMIZE_BASE
It is the bootloader's job to provide entropy, by passing a It is the bootloader's job to provide entropy, by passing a
random u64 value in /chosen/kaslr-seed at kernel entry. random u64 value in /chosen/kaslr-seed at kernel entry.
When booting via the UEFI stub, it will invoke the firmware's
EFI_RNG_PROTOCOL implementation (if available) to supply entropy
to the kernel proper. In addition, it will randomise the physical
location of the kernel Image as well.
If unsure, say N. If unsure, say N.
config RANDOMIZE_MODULE_REGION_FULL config RANDOMIZE_MODULE_REGION_FULL
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
#include "efistub.h" #include "efistub.h"
bool __nokaslr;
static int efi_secureboot_enabled(efi_system_table_t *sys_table_arg) static int efi_secureboot_enabled(efi_system_table_t *sys_table_arg)
{ {
static efi_guid_t const var_guid = EFI_GLOBAL_VARIABLE_GUID; static efi_guid_t const var_guid = EFI_GLOBAL_VARIABLE_GUID;
...@@ -207,14 +209,6 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, ...@@ -207,14 +209,6 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
pr_efi_err(sys_table, "Failed to find DRAM base\n"); pr_efi_err(sys_table, "Failed to find DRAM base\n");
goto fail; goto fail;
} }
status = handle_kernel_image(sys_table, image_addr, &image_size,
&reserve_addr,
&reserve_size,
dram_base, image);
if (status != EFI_SUCCESS) {
pr_efi_err(sys_table, "Failed to relocate kernel\n");
goto fail;
}
/* /*
* Get the command line from EFI, using the LOADED_IMAGE * Get the command line from EFI, using the LOADED_IMAGE
...@@ -224,7 +218,28 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, ...@@ -224,7 +218,28 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
cmdline_ptr = efi_convert_cmdline(sys_table, image, &cmdline_size); cmdline_ptr = efi_convert_cmdline(sys_table, image, &cmdline_size);
if (!cmdline_ptr) { if (!cmdline_ptr) {
pr_efi_err(sys_table, "getting command line via LOADED_IMAGE_PROTOCOL\n"); pr_efi_err(sys_table, "getting command line via LOADED_IMAGE_PROTOCOL\n");
goto fail_free_image; goto fail;
}
/* check whether 'nokaslr' was passed on the command line */
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
static const u8 default_cmdline[] = CONFIG_CMDLINE;
const u8 *str, *cmdline = cmdline_ptr;
if (IS_ENABLED(CONFIG_CMDLINE_FORCE))
cmdline = default_cmdline;
str = strstr(cmdline, "nokaslr");
if (str == cmdline || (str > cmdline && *(str - 1) == ' '))
__nokaslr = true;
}
status = handle_kernel_image(sys_table, image_addr, &image_size,
&reserve_addr,
&reserve_size,
dram_base, image);
if (status != EFI_SUCCESS) {
pr_efi_err(sys_table, "Failed to relocate kernel\n");
goto fail_free_cmdline;
} }
status = efi_parse_options(cmdline_ptr); status = efi_parse_options(cmdline_ptr);
...@@ -244,7 +259,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, ...@@ -244,7 +259,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_efi_err(sys_table, "Failed to load device tree!\n"); pr_efi_err(sys_table, "Failed to load device tree!\n");
goto fail_free_cmdline; goto fail_free_image;
} }
} }
...@@ -286,12 +301,11 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table, ...@@ -286,12 +301,11 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
efi_free(sys_table, initrd_size, initrd_addr); efi_free(sys_table, initrd_size, initrd_addr);
efi_free(sys_table, fdt_size, fdt_addr); efi_free(sys_table, fdt_size, fdt_addr);
fail_free_cmdline:
efi_free(sys_table, cmdline_size, (unsigned long)cmdline_ptr);
fail_free_image: fail_free_image:
efi_free(sys_table, image_size, *image_addr); efi_free(sys_table, image_size, *image_addr);
efi_free(sys_table, reserve_size, reserve_addr); efi_free(sys_table, reserve_size, reserve_addr);
fail_free_cmdline:
efi_free(sys_table, cmdline_size, (unsigned long)cmdline_ptr);
fail: fail:
return EFI_ERROR; return EFI_ERROR;
} }
......
...@@ -13,6 +13,10 @@ ...@@ -13,6 +13,10 @@
#include <asm/efi.h> #include <asm/efi.h>
#include <asm/sections.h> #include <asm/sections.h>
#include "efistub.h"
extern bool __nokaslr;
efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg, efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg,
unsigned long *image_addr, unsigned long *image_addr,
unsigned long *image_size, unsigned long *image_size,
...@@ -23,26 +27,52 @@ efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg, ...@@ -23,26 +27,52 @@ efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg,
{ {
efi_status_t status; efi_status_t status;
unsigned long kernel_size, kernel_memsize = 0; unsigned long kernel_size, kernel_memsize = 0;
unsigned long nr_pages;
void *old_image_addr = (void *)*image_addr; void *old_image_addr = (void *)*image_addr;
unsigned long preferred_offset; unsigned long preferred_offset;
u64 phys_seed = 0;
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
if (!__nokaslr) {
status = efi_get_random_bytes(sys_table_arg,
sizeof(phys_seed),
(u8 *)&phys_seed);
if (status == EFI_NOT_FOUND) {
pr_efi(sys_table_arg, "EFI_RNG_PROTOCOL unavailable, no randomness supplied\n");
} else if (status != EFI_SUCCESS) {
pr_efi_err(sys_table_arg, "efi_get_random_bytes() failed\n");
return status;
}
} else {
pr_efi(sys_table_arg, "KASLR disabled on kernel command line\n");
}
}
/* /*
* The preferred offset of the kernel Image is TEXT_OFFSET bytes beyond * The preferred offset of the kernel Image is TEXT_OFFSET bytes beyond
* a 2 MB aligned base, which itself may be lower than dram_base, as * a 2 MB aligned base, which itself may be lower than dram_base, as
* long as the resulting offset equals or exceeds it. * long as the resulting offset equals or exceeds it.
*/ */
preferred_offset = round_down(dram_base, SZ_2M) + TEXT_OFFSET; preferred_offset = round_down(dram_base, MIN_KIMG_ALIGN) + TEXT_OFFSET;
if (preferred_offset < dram_base) if (preferred_offset < dram_base)
preferred_offset += SZ_2M; preferred_offset += MIN_KIMG_ALIGN;
/* Relocate the image, if required. */
kernel_size = _edata - _text; kernel_size = _edata - _text;
if (*image_addr != preferred_offset) { kernel_memsize = kernel_size + (_end - _edata);
kernel_memsize = kernel_size + (_end - _edata);
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && phys_seed != 0) {
/*
* If KASLR is enabled, and we have some randomness available,
* locate the kernel at a randomized offset in physical memory.
*/
*reserve_size = kernel_memsize + TEXT_OFFSET;
status = efi_random_alloc(sys_table_arg, *reserve_size,
MIN_KIMG_ALIGN, reserve_addr,
phys_seed);
*image_addr = *reserve_addr + TEXT_OFFSET;
} else {
/* /*
* First, try a straight allocation at the preferred offset. * Else, try a straight allocation at the preferred offset.
* This will work around the issue where, if dram_base == 0x0, * This will work around the issue where, if dram_base == 0x0,
* efi_low_alloc() refuses to allocate at 0x0 (to prevent the * efi_low_alloc() refuses to allocate at 0x0 (to prevent the
* address of the allocation to be mistaken for a FAIL return * address of the allocation to be mistaken for a FAIL return
...@@ -52,27 +82,31 @@ efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg, ...@@ -52,27 +82,31 @@ efi_status_t __init handle_kernel_image(efi_system_table_t *sys_table_arg,
* Mustang), we can still place the kernel at the address * Mustang), we can still place the kernel at the address
* 'dram_base + TEXT_OFFSET'. * 'dram_base + TEXT_OFFSET'.
*/ */
if (*image_addr == preferred_offset)
return EFI_SUCCESS;
*image_addr = *reserve_addr = preferred_offset; *image_addr = *reserve_addr = preferred_offset;
nr_pages = round_up(kernel_memsize, EFI_ALLOC_ALIGN) / *reserve_size = round_up(kernel_memsize, EFI_ALLOC_ALIGN);
EFI_PAGE_SIZE;
status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS, status = efi_call_early(allocate_pages, EFI_ALLOCATE_ADDRESS,
EFI_LOADER_DATA, nr_pages, EFI_LOADER_DATA,
*reserve_size / EFI_PAGE_SIZE,
(efi_physical_addr_t *)reserve_addr); (efi_physical_addr_t *)reserve_addr);
if (status != EFI_SUCCESS) { }
kernel_memsize += TEXT_OFFSET;
status = efi_low_alloc(sys_table_arg, kernel_memsize,
SZ_2M, reserve_addr);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_efi_err(sys_table_arg, "Failed to relocate kernel\n"); *reserve_size = kernel_memsize + TEXT_OFFSET;
return status; status = efi_low_alloc(sys_table_arg, *reserve_size,
} MIN_KIMG_ALIGN, reserve_addr);
*image_addr = *reserve_addr + TEXT_OFFSET;
if (status != EFI_SUCCESS) {
pr_efi_err(sys_table_arg, "Failed to relocate kernel\n");
*reserve_size = 0;
return status;
} }
memcpy((void *)*image_addr, old_image_addr, kernel_size); *image_addr = *reserve_addr + TEXT_OFFSET;
*reserve_size = kernel_memsize;
} }
memcpy((void *)*image_addr, old_image_addr, kernel_size);
return EFI_SUCCESS; return EFI_SUCCESS;
} }
...@@ -147,6 +147,20 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, ...@@ -147,6 +147,20 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
if (status) if (status)
goto fdt_set_fail; goto fdt_set_fail;
if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
efi_status_t efi_status;
efi_status = efi_get_random_bytes(sys_table, sizeof(fdt_val64),
(u8 *)&fdt_val64);
if (efi_status == EFI_SUCCESS) {
status = fdt_setprop(fdt, node, "kaslr-seed",
&fdt_val64, sizeof(fdt_val64));
if (status)
goto fdt_set_fail;
} else if (efi_status != EFI_NOT_FOUND) {
return efi_status;
}
}
return EFI_SUCCESS; return EFI_SUCCESS;
fdt_set_fail: fdt_set_fail:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment