arm64: KASLR: EFI stub part
cover arm64: KASLR: EFI stub part
This subseries contains the EFI stub specific changes of my KASLR series for arm64. The original v4 series can be found here:
http://thread.gmane.org/gmane.linux.kernel/2135931
This series consists of the patches that result in the kernel to be allocated at a random physical offset on UEFI systems that implement the EFI_RNG_PROTOCOL protocol (if so configured), and pass a random value in /chosen/kaslr-seed in the device tree, to be used by the kernel proper to randomize the layout of the kernel virtual address space.
Changes since v4:
- added R-b's from Matt and Kees (#2
- removed a redundant efi_status_t initializer (#2)
- fixed an issue in update_fdt() (#4) where 'int status' was mistaken for a efi_status_t
Patch #1 implements efi_get_random_bytes() based on the EFI_RNG_PROTOCOL
Patch #2 implements efi_random_alloc()
Patch #3 moves the allocation for the converted command line (UTF-16 to ASCII) away from the base of memory. This is necessary since for parsing the command line before allocating the kernel image.
Patch #4 implements the actual KASLR, by randomizing the kernel physical address, and passing entropy in /chosen/kaslr-seed so that the kernel proper can relocate itself virtually.
Ard Biesheuvel (4):
efi: stub: implement efi_get_random_bytes() based on EFI_RNG_PROTOCOL
efi: stub: add implementation of efi_random_alloc()
efi: stub: use high allocation for converted command line
arm64: efi: invoke EFI_RNG_PROTOCOL to supply KASLR randomness
arch/arm64/Kconfig | 5 + arch/x86/include/asm/efi.h | 2 + drivers/firmware/efi/libstub/Makefile | 2 +- drivers/firmware/efi/libstub/arm-stub.c | 40 ++++-- drivers/firmware/efi/libstub/arm64-stub.c | 78 +++++++---- drivers/firmware/efi/libstub/efi-stub-helper.c | 7 +- drivers/firmware/efi/libstub/efistub.h | 7 + drivers/firmware/efi/libstub/fdt.c | 14 ++ drivers/firmware/efi/libstub/random.c | 135 ++++++++++++++++++++ include/linux/efi.h | 5 +- 10 files changed, 257 insertions(+), 38 deletions(-) create mode 100644 drivers/firmware/efi/libstub/random.c
Signature · 1 line
2.5.0
PATCH 1/4 efi: stub: implement efi_get_random_bytes() based on EFI_RNG_PROTOCOL
This exposes the firmware's implementation of EFI_RNG_PROTOCOL via a new function efi_get_random_bytes().
--- drivers/firmware/efi/libstub/Makefile | 2 +- drivers/firmware/efi/libstub/efistub.h | 3 ++ drivers/firmware/efi/libstub/random.c | 35 ++++++++++++++++++++ include/linux/efi.h | 5 ++- 4 files changed, 43 insertions(+), 2 deletions(-)
drivers/firmware/efi/libstub/Makefile +1 -1
@@ -36,7 +36,7 @@ lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o string.o \ $(patsubst %.c,lib-%.o,$(arm-deps)) lib-$(CONFIG_ARM) += arm32-stub.o -lib-$(CONFIG_ARM64) += arm64-stub.o +lib-$(CONFIG_ARM64) += arm64-stub.o random.o CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) #
drivers/firmware/efi/libstub/efistub.h +3 -0
@@ -43,4 +43,7 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, unsigned long desc_size, efi_memory_desc_t *runtime_map, int *count); +efi_status_t efi_get_random_bytes (efi_system_table_t *sys_table, +unsigned long size,u8 *out); +#endif
drivers/firmware/efi/libstub/random.c new file +35 -0
@@ -0,0 +1,35 @@ +/* +* Copyright (C) 2016 Linaro Ltd; <[email protected]> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +*/ + +#include <linux/efi.h> +#include <asm/efi.h> + +#include "efistub.h" + +struct efi_rng_protocol { +efi_status_t (*get_info)(struct efi_rng_protocol *, +unsigned long *,efi_guid_t *); +efi_status_t (*get_rng)(struct efi_rng_protocol *, +efi_guid_t *,unsigned long ,u8 *out); +}; + +efi_status_t efi_get_random_bytes (efi_system_table_t *sys_table_arg, +unsigned long size,u8 *out) +{ +efi_guid_t rng_proto =EFI_RNG_PROTOCOL_GUID ; +efi_status_t status; +struct efi_rng_protocol *rng; + + status =efi_call_early (locate_protocol, &rng_proto,NULL , + (void **)&rng); +if (status !=EFI_SUCCESS ) +return status; + +return rng->get_rng (rng,NULL , size, out); +}
include/linux/efi.h +4 -1
@@ -299,7 +299,7 @@ typedef struct {void *open_protocol_information;void *protocols_per_handle;void *locate_handle_buffer; -void *locate_protocol; +efi_status_t (*locate_protocol)(efi_guid_t *,void *,void **);void *install_multiple_protocol_interfaces;void *uninstall_multiple_protocol_interfaces;void *calculate_crc32;
@@ -599,6 +599,9 @@ void efi_native_runtime_setup(void);#define EFI_PROPERTIES_TABLE_GUID \ EFI_GUID( 0x880aaca3, 0x4adc, 0x4a04, 0x90, 0x79, 0xb7, 0x47, 0x34, 0x08, 0x25, 0xe5 ) +#define EFI_RNG_PROTOCOL_GUID \ + EFI_GUID( 0x3152bca5, 0xeade, 0x433d, 0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44 ) + typedef struct { efi_guid_t guid; u64 table;
Signature · 1 line
2.5.0
PATCH 2/4 efi: stub: add implementation of efi_random_alloc()
In reply to Ard BiesheuvelThis implements efi_random_alloc(), which allocates a chunk of memory of a certain size at a certain alignment, and uses the random_seed argument it receives to randomize the address of the allocation.
This is implemented by iterating over the UEFI memory map, counting the number of suitable slots (aligned offsets) within each region, and picking a random number between 0 and 'number of slots - 1' to select the slot, This should guarantee that each possible offset is chosen equally likely.
--- drivers/firmware/efi/libstub/efistub.h | 4 + drivers/firmware/efi/libstub/random.c | 100 ++++++++++++++++++++ 2 files changed, 104 insertions(+)
drivers/firmware/efi/libstub/efistub.h +4 -0
@@ -46,4 +46,8 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,efi_status_t efi_get_random_bytes (efi_system_table_t *sys_table,unsigned long size,u8 *out); +efi_status_t efi_random_alloc (efi_system_table_t *sys_table_arg, +unsigned long size,unsigned long align, +unsigned long *addr,unsigned long random_seed); +#endif
drivers/firmware/efi/libstub/random.c +100 -0
103 lines not on this page · raw message
Signature · 1 line
2.5.0
PATCH 3/4 efi: stub: use high allocation for converted command line
In reply to Ard BiesheuvelBefore we can move the command line processing before the allocation of the kernel, which is required for detecting the 'nokaslr' option which controls that allocation, move the converted command line higher up in memory, to prevent it from interfering with the kernel itself.
Since x86 needs the address to fit in 32 bits, use UINT_MAX as the upper bound there. Otherwise, use ULONG_MAX (i.e., no limit)
--- arch/x86/include/asm/efi.h | 2 ++ drivers/firmware/efi/libstub/efi-stub-helper.c | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-)
arch/x86/include/asm/efi.h +2 -0
@@ -25,6 +25,8 @@#define EFI32_LOADER_SIGNATURE "EL32"#define EFI64_LOADER_SIGNATURE "EL64" +#define MAX_CMDLINE_ADDRESS UINT_MAX +#ifdef CONFIG_X86_32
drivers/firmware/efi/libstub/efi-stub-helper.c +6 -1
@@ -649,6 +649,10 @@ static u8 *efi_utf16_to_utf8(u8 *dst, const u16 *src, int n)return dst; } +#ifndef MAX_CMDLINE_ADDRESS +#define MAX_CMDLINE_ADDRESS ULONG_MAX +#endif +/* * Convert the unicode UEFI command line to ASCII to pass to kernel. * Size of memory allocated return in *cmd_line_len.
@@ -684,7 +688,8 @@ char *efi_convert_cmdline(efi_system_table_t *sys_table_arg, options_bytes++;/* NUL termination */ - status =efi_low_alloc (sys_table_arg, options_bytes,0 , &cmdline_addr); + status =efi_high_alloc (sys_table_arg, options_bytes,0 , + &cmdline_addr,MAX_CMDLINE_ADDRESS );if (status !=EFI_SUCCESS )return NULL ;
Signature · 1 line
2.5.0
PATCH 4/4 arm64: efi: invoke EFI_RNG_PROTOCOL to supply KASLR randomness
In reply to Ard BiesheuvelSince 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.
--- arch/arm64/Kconfig | 5 ++ drivers/firmware/efi/libstub/arm-stub.c | 40 ++++++---- drivers/firmware/efi/libstub/arm64-stub.c | 78 ++++++++++++++------ drivers/firmware/efi/libstub/fdt.c | 14 ++++ 4 files changed, 102 insertions(+), 35 deletions(-)
arch/arm64/Kconfig +5 -0
@@ -785,6 +785,11 @@ config RANDOMIZE_BASE It is the bootloader's job to provide entropy, by passing a 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. config RANDOMIZE_MODULE_REGION_FULL
drivers/firmware/efi/libstub/arm-stub.c +27 -13
@@ -18,6 +18,8 @@#include "efistub.h" +bool __nokaslr; +static int efi_secureboot_enabled (efi_system_table_t *sys_table_arg) {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,pr_efi_err (sys_table,"Failed to find DRAM base\n" ); 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
@@ -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);if (!cmdline_ptr) {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);
@@ -244,7 +259,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,if (status !=EFI_SUCCESS ) {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,efi_free (sys_table, initrd_size, initrd_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:efi_free (sys_table, image_size, *image_addr);efi_free (sys_table, reserve_size, reserve_addr); +fail_free_cmdline: +efi_free (sys_table, cmdline_size, (unsigned long )cmdline_ptr); fail:return EFI_ERROR ; }
drivers/firmware/efi/libstub/arm64-stub.c +56 -22
115 lines not on this page · raw message
drivers/firmware/efi/libstub/fdt.c +14 -0
@@ -147,6 +147,20 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,if (status) 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 ; fdt_set_fail:
Signature · 1 line
2.5.0
PATCH 4/4 arm64: efi: invoke EFI_RNG_PROTOCOL to supply KASLR randomness
Quoted 9 lines · Ard Biesheuvel
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.
Quoted 7 lines
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/Kconfig | 5 ++ drivers/firmware/efi/libstub/arm-stub.c | 40 ++++++---- drivers/firmware/efi/libstub/arm64-stub.c | 78 ++++++++++++++------ drivers/firmware/efi/libstub/fdt.c | 14 ++++ 4 files changed, 102 insertions(+), 35 deletions(-)