x86/boot/KASLR: Restrict kernel to be randomized in mirror regions
v9 cover x86/boot/KASLR: Restrict kernel to be randomized in mirror regions
Patch 1/2 is added to add efi_early_memdesc_ptr helper to wrap the open code which gets the start of efi memmap descriptor and also explain why it need be done like that, Ingo suggested it.
And also replace several places of the open code with efi_early_memdesc_ptr helper.
And also use efi_early_memdesc_ptr in process_efi_entries() which handle efi mirror issue during KASLR.
Change:v8->v9:
Matt suggested that an 'early' should be included in the name of the helper to make its name as efi_early_memdesc_ptr. That can help denote the helper should only be used in early bootup stage, after that for_each_efi_memory_*() API is suggested.
v7->v8:
Add efi_memdesc_ptr helper to wrap the open code which gets the start of map descriptor according to Ingo's suggestion.
v6->v7:
Ingo pointed out several incorrect line break issues and unclear description of patch log. Correct them and rewrite patch log.
And also rewrite the EFI warning message that if EFI memmap is above 4G in 32bit system since 32bit system can not handle data above 4G at kernel decompression stage. This is suggested by Ingo too.
v5->v6:
Code style issue fix according to Kees's comment.
This is based on tip/x86/boot, patch 1,2,3/4 in v5 post has been put into tip/x86/boot now.
Baoquan He (2):
efi: Introduce efi_early_memdesc_ptr to get pointer to memmap
descriptor
x86/boot/KASLR: Restrict kernel to be randomized in mirror regions
arch/x86/boot/compressed/eboot.c | 2 +- arch/x86/boot/compressed/kaslr.c | 68 +++++++++++++++++++++++++- drivers/firmware/efi/libstub/efi-stub-helper.c | 4 +- include/linux/efi.h | 21 ++++++++ 4 files changed, 90 insertions(+), 5 deletions(-)
Signature · 1 line
2.5.5
PATCH v9 1/2 efi: Introduce efi_early_memdesc_ptr to get pointer to memmap descriptor
The existing map iteration helper for_each_efi_memory_desc_in_map can only be used after OS initializes EFI to fill data of struct efi_memory_map. Before that we also need iterate map descriptors which are stored in several intermediate structures, like struct efi_boot_memmap for arch independent usage and struct efi_info for x86 arch only.
Introduce efi_early_memdesc_ptr to get pointer to a map descriptor, and replace several places of open code with it.
--- arch/x86/boot/compressed/eboot.c | 2 +- drivers/firmware/efi/libstub/efi-stub-helper.c | 4 ++-- include/linux/efi.h | 21 +++++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-)
arch/x86/boot/compressed/eboot.c +1 -1
@@ -767,7 +767,7 @@ static efi_status_t setup_e820(struct boot_params *params, m |= (u64 )efi->efi_memmap_hi <<32 ;#endif - d = (efi_memory_desc_t *)(m + (i * efi->efi_memdesc_size)); + d =efi_early_memdesc_ptr (m, efi->efi_memdesc_size, i);switch (d->type) {case EFI_RESERVED_TYPE :case EFI_RUNTIME_SERVICES_CODE :
drivers/firmware/efi/libstub/efi-stub-helper.c +2 -2
@@ -205,7 +205,7 @@ efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,unsigned long m = (unsigned long )map;u64 start, end; - desc = (efi_memory_desc_t *)(m + (i * desc_size)); + desc =efi_early_memdesc_ptr (m, desc_size, i);if (desc->type !=EFI_CONVENTIONAL_MEMORY )continue ;
@@ -298,7 +298,7 @@ efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,unsigned long m = (unsigned long )map;u64 start, end; - desc = (efi_memory_desc_t *)(m + (i * desc_size)); + desc =efi_early_memdesc_ptr (m, desc_size, i);if (desc->type !=EFI_CONVENTIONAL_MEMORY )continue ;
include/linux/efi.h +21 -0
@@ -1020,6 +1020,27 @@ extern int efi_memattr_init(void);extern int efi_memattr_apply_permissions (struct mm_struct *mm,efi_memattr_perm_setter fn); +/* +* efi_early_memdesc_ptr - get the n-th efi memmap descriptor +* @map: the start of efi memmap +* @desc_size: the size of space for each efi memmap descriptor +* @n: the index of efi memmap descriptor +* +* EFI boot service provides function GetMemoryMap() to get a copy of the +* current memory map which is an array of memory descriptors, each of +* which describes a contiguous block of memory. And also get the size of +* map, and the size of each descriptor, etc. Note that per section 6.2 of +* UEFI Spec 2.6 Errata A, the returned size of each descriptor might not +* be equal to sizeof(efi_memory_memdesc_t) since efi_memory_memdesc_t may +* be extended in the future in response to hardware innovation. Thus OS +* MUST use the returned size of descriptor to find the start of each +* efi_memory_memdesc_t in the memory map array. This should only be used +* during bootup since for_each_efi_memory_desc_xxx is suggested after OS +* initializes EFI to fill data of struct efi_memory_map. +*/ +#define efi_early_memdesc_ptr (map, desc_size, n) \ + (efi_memory_desc_t *)((void *)(map) + ((n) * (desc_size))) +/* Iterate through an efi_memory_map */ #define for_each_efi_memory_desc_in_map (m, md) \ for ((md) = (m)->map; \
Signature · 1 line
2.5.5
Re: [PATCH v9 1/2] efi: Introduce efi_early_memdesc_ptr to get pointer to memmap descriptor
Quoted 2 lines · Baoquan He
The existing map iteration helper
for_each_efi_memory_desc_in_mapcan only be used after OS initializes EFI to fill data of structefi_memory_map.
Should this say "EFI subsystem"? The firmware doesn't care about the kernel's internal data structures.
Quoted 66 lines
Before that we also need iterate map descriptors which are stored in several intermediate structures, like struct
efi_boot_memmapfor arch independent usage and structefi_infofor x86 arch only.Introduce
efi_early_memdesc_ptrto get pointer to a map descriptor, and replace several places of open code with it.Suggested-by Ingo Molnar <[email protected]>Signed-off-by Baoquan He <[email protected]>--- arch/x86/boot/compressed/eboot.c | 2 +- drivers/firmware/efi/libstub/efi-stub-helper.c | 4 ++-- include/linux/efi.h | 21 +++++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-)arch/x86/boot/compressed/eboot.c +1
-1@@ -767,7 +767,7 @@ static efi_status_t setup_e820(struct boot_params *params, m |= (u64 )efi->efi_memmap_hi <<32 ;#endif - d = (efi_memory_desc_t *)(m + (i * efi->efi_memdesc_size)); + d =efi_early_memdesc_ptr (m, efi->efi_memdesc_size, i);switch (d->type) {case EFI_RESERVED_TYPE :case EFI_RUNTIME_SERVICES_CODE :drivers/firmware/efi/libstub/efi-stub-helper.c +2
-2@@ -205,7 +205,7 @@ efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,unsigned long m = (unsigned long )map;u64 start, end; - desc = (efi_memory_desc_t *)(m + (i * desc_size)); + desc =efi_early_memdesc_ptr (m, desc_size, i);if (desc->type !=EFI_CONVENTIONAL_MEMORY )continue ;@@ -298,7 +298,7 @@ efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,unsigned long m = (unsigned long )map;u64 start, end; - desc = (efi_memory_desc_t *)(m + (i * desc_size)); + desc =efi_early_memdesc_ptr (m, desc_size, i);if (desc->type !=EFI_CONVENTIONAL_MEMORY )continue ;include/linux/efi.h +8
-0@@ -1020,6 +1020,27 @@ extern int efi_memattr_init(void);extern int efi_memattr_apply_permissions (struct mm_struct *mm,efi_memattr_perm_setter fn); +/* +* efi_early_memdesc_ptr - get the n-th efi memmap descriptor +* @map: the start of efi memmap +* @desc_size: the size of space for each efi memmap descriptor +* @n: the index of efi memmap descriptor +* +* EFI boot service provides function GetMemoryMap() to get a copy of the +* current memory map which is an array of memory descriptors, each of
include/linux/efi.h extern int efi_memattr_init(void); · line 1040
Quoted 10 lines
include/linux/efi.h +10
-0+* which describes a contiguous block of memory. And also get the size of +* map, and the size of each descriptor, etc. Note that per section 6.2 of +* UEFI Spec 2.6 Errata A, the returned size of each descriptor might not +* be equal to sizeof(efi_memory_memdesc_t) since efi_memory_memdesc_t may +* be extended in the future in response to hardware innovation. Thus OS +* MUST use the returned size of descriptor to find the start of each +* efi_memory_memdesc_t in the memory map array. This should only be used +* during bootup since for_each_efi_memory_desc_xxx is suggested after OS +* initializes EFI to fill data of struct efi_memory_map. +*/
Again, please use "EFI subsystem" here.
include/linux/efi.h extern int efi_memattr_init(void); · line 1046
Quoted 6 lines
include/linux/efi.h +3
-0+#define efi_early_memdesc_ptr (map, desc_size, n) \ + (efi_memory_desc_t *)((void *)(map) + ((n) * (desc_size))) +/* Iterate through an efi_memory_map */ #define for_each_efi_memory_desc_in_map (m, md) \ for ((md) = (m)->map; \
Otherwise, this looks OK to me.
Re: [PATCH v9 1/2] efi: Introduce efi_early_memdesc_ptr to get pointer to memmap descriptor
Quoted 6 lines · Matt Fleming
Quoted 2 lines · Baoquan He
The existing map iteration helper
for_each_efi_memory_desc_in_mapcan only be used after OS initializes EFI to fill data of structefi_memory_map.Should this say "EFI subsystem"? The firmware doesn't care about the kernel's internal data structures.
Sounds reasonable. Let me update and maybe repost to this thread directly. Thanks!
Quoted 71 lines
Quoted 71 lines
Before that we also need iterate map descriptors which are stored in several intermediate structures, like struct
efi_boot_memmapfor arch independent usage and structefi_infofor x86 arch only.Introduce
efi_early_memdesc_ptrto get pointer to a map descriptor, and replace several places of open code with it.Suggested-by Ingo Molnar <[email protected]>Signed-off-by Baoquan He <[email protected]>--- arch/x86/boot/compressed/eboot.c | 2 +- drivers/firmware/efi/libstub/efi-stub-helper.c | 4 ++-- include/linux/efi.h | 21 +++++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-)arch/x86/boot/compressed/eboot.c +1
-1@@ -767,7 +767,7 @@ static efi_status_t setup_e820(struct boot_params *params, m |= (u64 )efi->efi_memmap_hi <<32 ;#endif - d = (efi_memory_desc_t *)(m + (i * efi->efi_memdesc_size)); + d =efi_early_memdesc_ptr (m, efi->efi_memdesc_size, i);switch (d->type) {case EFI_RESERVED_TYPE :case EFI_RUNTIME_SERVICES_CODE :drivers/firmware/efi/libstub/efi-stub-helper.c +2
-2@@ -205,7 +205,7 @@ efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,unsigned long m = (unsigned long )map;u64 start, end; - desc = (efi_memory_desc_t *)(m + (i * desc_size)); + desc =efi_early_memdesc_ptr (m, desc_size, i);if (desc->type !=EFI_CONVENTIONAL_MEMORY )continue ;@@ -298,7 +298,7 @@ efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,unsigned long m = (unsigned long )map;u64 start, end; - desc = (efi_memory_desc_t *)(m + (i * desc_size)); + desc =efi_early_memdesc_ptr (m, desc_size, i);if (desc->type !=EFI_CONVENTIONAL_MEMORY )continue ;include/linux/efi.h +12
-0@@ -1020,6 +1020,27 @@ extern int efi_memattr_init(void);extern int efi_memattr_apply_permissions (struct mm_struct *mm,efi_memattr_perm_setter fn); +/* +* efi_early_memdesc_ptr - get the n-th efi memmap descriptor +* @map: the start of efi memmap +* @desc_size: the size of space for each efi memmap descriptor +* @n: the index of efi memmap descriptor +* +* EFI boot service provides function GetMemoryMap() to get a copy of the +* current memory map which is an array of memory descriptors, each of +* which describes a contiguous block of memory. And also get the size of +* map, and the size of each descriptor, etc. Note that per section 6.2 of +* UEFI Spec 2.6 Errata A, the returned size of each descriptor might not +* be equal to sizeof(efi_memory_memdesc_t) since efi_memory_memdesc_t may
Quoted 8 lines
include/linux/efi.h extern int efi_memattr_init(void); · line 1040
Quoted 6 lines
include/linux/efi.h +6
-0+* be extended in the future in response to hardware innovation. Thus OS +* MUST use the returned size of descriptor to find the start of each +* efi_memory_memdesc_t in the memory map array. This should only be used +* during bootup since for_each_efi_memory_desc_xxx is suggested after OS +* initializes EFI to fill data of struct efi_memory_map. +*/ Again, please use "EFI subsystem" here.
Sure, will do.
Quoted 9 lines
include/linux/efi.h extern int efi_memattr_init(void); · line 1046
Quoted 7 lines
include/linux/efi.h +3
-0+#define efi_early_memdesc_ptr (map, desc_size, n) \ + (efi_memory_desc_t *)((void *)(map) + ((n) * (desc_size))) +/* Iterate through an efi_memory_map */ #define for_each_efi_memory_desc_in_map (m, md) \ for ((md) = (m)->map; \Otherwise, this looks OK to me.
Thanks for reviewing!
PATCH v10 1/2 efi: Introduce efi_early_memdesc_ptr to get pointer to memmap descriptor
In reply to Baoquan HeThe existing map iteration helper for_each_efi_memory_desc_in_map can only be used after OS initializes EFI subsystem to fill data of struct efi_memory_map. Before that we also need iterate map descriptors which are stored in several intermediate structures, like struct efi_boot_memmap for arch independent usage and struct efi_info for x86 arch only.
Introduce efi_early_memdesc_ptr to get pointer to a map descriptor, and replace several places of open code with it.
---v9->v10:
Use the 'EFI subsystem' instead of EFI since EFI usually means firmware. Here we are talking about EFI subsystem inside kernel.
arch/x86/boot/compressed/eboot.c | 2 +- drivers/firmware/efi/libstub/efi-stub-helper.c | 4 ++-- include/linux/efi.h | 21 +++++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-)
arch/x86/boot/compressed/eboot.c +1 -1
@@ -767,7 +767,7 @@ static efi_status_t setup_e820(struct boot_params *params, m |= (u64 )efi->efi_memmap_hi <<32 ;#endif - d = (efi_memory_desc_t *)(m + (i * efi->efi_memdesc_size)); + d =efi_early_memdesc_ptr (m, efi->efi_memdesc_size, i);switch (d->type) {case EFI_RESERVED_TYPE :case EFI_RUNTIME_SERVICES_CODE :
drivers/firmware/efi/libstub/efi-stub-helper.c +2 -2
@@ -205,7 +205,7 @@ efi_status_t efi_high_alloc(efi_system_table_t *sys_table_arg,unsigned long m = (unsigned long )map;u64 start, end; - desc = (efi_memory_desc_t *)(m + (i * desc_size)); + desc =efi_early_memdesc_ptr (m, desc_size, i);if (desc->type !=EFI_CONVENTIONAL_MEMORY )continue ;
@@ -298,7 +298,7 @@ efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,unsigned long m = (unsigned long )map;u64 start, end; - desc = (efi_memory_desc_t *)(m + (i * desc_size)); + desc =efi_early_memdesc_ptr (m, desc_size, i);if (desc->type !=EFI_CONVENTIONAL_MEMORY )continue ;
include/linux/efi.h +21 -0
@@ -1020,6 +1020,27 @@ extern int efi_memattr_init(void);extern int efi_memattr_apply_permissions (struct mm_struct *mm,efi_memattr_perm_setter fn); +/* +* efi_early_memdesc_ptr - get the n-th efi memmap descriptor +* @map: the start of efi memmap +* @desc_size: the size of space for each efi memmap descriptor +* @n: the index of efi memmap descriptor +* +* EFI boot service provides function GetMemoryMap() to get a copy of the +* current memory map which is an array of memory descriptors, each of +* which describes a contiguous block of memory. And also get the size of +* map, and the size of each descriptor, etc. Note that per section 6.2 of +* UEFI Spec 2.6 Errata A, the returned size of each descriptor might not +* be equal to sizeof(efi_memory_memdesc_t) since efi_memory_memdesc_t may +* be extended in the future in response to hardware innovation. Thus OS +* MUST use the returned size of descriptor to find the start of each +* efi_memory_memdesc_t in the memory map array. This should only be used +* during bootup since for_each_efi_memory_desc_xxx is suggested after OS +* initializes EFI subsystem to fill data of struct efi_memory_map. +*/ +#define efi_early_memdesc_ptr (map, desc_size, n) \ + (efi_memory_desc_t *)((void *)(map) + ((n) * (desc_size))) +/* Iterate through an efi_memory_map */ #define for_each_efi_memory_desc_in_map (m, md) \ for ((md) = (m)->map; \
Signature · 1 line
2.5.5
tip-bot for Baoquan He
merge bot[tip:x86/boot] efi: Introduce efi_early_memdesc_ptr to get pointer to memmap descriptor
efi: Introduce efi_early_memdesc_ptr to get pointer to memmap descriptor
Commit-ID: 02e43c2dcd3b3cf7244f6dda65a07e8dacadaf8d Gitweb: http://git.kernel.org/tip/02e43c2dcd3b3cf7244f6dda65a07e8dacadaf8d Author: Baoquan He <[email protected]> AuthorDate: Wed, 16 Aug 2017 21:46:51 +0800 Committer: Ingo Molnar <[email protected]> CommitDate: Thu, 17 Aug 2017 10:50:57 +0200
efi: Introduce efi_early_memdesc_ptr to get pointer to memmap descriptor
The existing map iteration helper for_each_efi_memory_desc_in_map can only be used after the kernel initializes the EFI subsystem to set up struct efi_memory_map.
Before that we also need iterate map descriptors which are stored in several intermediate structures, like struct efi_boot_memmap for arch independent usage and struct efi_info for x86 arch only.
Introduce efi_early_memdesc_ptr() to get pointer to a map descriptor, and replace several places where that primitive is open coded.
--- arch/x86/boot/compressed/eboot.c | 2 +- drivers/firmware/efi/libstub/efi-stub-helper.c | 4 ++-- include/linux/efi.h | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-)
arch/x86/boot/compressed/eboot.c +1 -1
@@ -767,7 +767,7 @@ static efi_status_t setup_e820(struct boot_params *params, m |= (u64 )efi->efi_memmap_hi <<32 ;#endif - d = (efi_memory_desc_t *)(m + (i * efi->efi_memdesc_size)); + d =efi_early_memdesc_ptr (m, efi->efi_memdesc_size, i);switch (d->type) {case EFI_RESERVED_TYPE :case EFI_RUNTIME_SERVICES_CODE :
drivers/firmware/efi/libstub/efi-stub-helper.c +2 -2
@@ -205,7 +205,7 @@ again:unsigned long m = (unsigned long )map;u64 start, end; - desc = (efi_memory_desc_t *)(m + (i * desc_size)); + desc =efi_early_memdesc_ptr (m, desc_size, i);if (desc->type !=EFI_CONVENTIONAL_MEMORY )continue ;
@@ -298,7 +298,7 @@ efi_status_t efi_low_alloc(efi_system_table_t *sys_table_arg,unsigned long m = (unsigned long )map;u64 start, end; - desc = (efi_memory_desc_t *)(m + (i * desc_size)); + desc =efi_early_memdesc_ptr (m, desc_size, i);if (desc->type !=EFI_CONVENTIONAL_MEMORY )continue ;
include/linux/efi.h +22 -0
@@ -1020,6 +1020,28 @@ extern int efi_memattr_init(void);extern int efi_memattr_apply_permissions (struct mm_struct *mm,efi_memattr_perm_setter fn); +/* +* efi_early_memdesc_ptr - get the n-th EFI memmap descriptor +* @map: the start of efi memmap +* @desc_size: the size of space for each EFI memmap descriptor +* @n: the index of efi memmap descriptor +* +* EFI boot service provides the GetMemoryMap() function to get a copy of the +* current memory map which is an array of memory descriptors, each of +* which describes a contiguous block of memory. It also gets the size of the +* map, and the size of each descriptor, etc. +* +* Note that per section 6.2 of UEFI Spec 2.6 Errata A, the returned size of +* each descriptor might not be equal to sizeof(efi_memory_memdesc_t), +* since efi_memory_memdesc_t may be extended in the future. Thus the OS +* MUST use the returned size of the descriptor to find the start of each +* efi_memory_memdesc_t in the memory map array. This should only be used +* during bootup since for_each_efi_memory_desc_xxx() is available after the +* kernel initializes the EFI subsystem to set up struct efi_memory_map. +*/ +#define efi_early_memdesc_ptr (map, desc_size, n) \ + (efi_memory_desc_t *)((void *)(map) + ((n) * (desc_size))) +/* Iterate through an efi_memory_map */ #define for_each_efi_memory_desc_in_map (m, md) \ for ((md) = (m)->map; \
PATCH v9 2/2 x86/boot/KASLR: Restrict kernel to be randomized in mirror regions
In reply to Baoquan HeCurrently KASLR will parse all e820 entries of RAM type and add all candidate position into slots array. Then we will choose one slot randomly as the new position which kernel will be decompressed into and run at.
On system with EFI enabled, e820 memory regions are coming from EFI memory regions by combining adjacent regions. While these EFI memory regions have more attributes to mark their different use. Mirror attribute is such kind. The physical memory region whose descriptors in EFI memory map has EFI_MEMORY_MORE_RELIABLE attribute (bit: 16) are mirrored. The address range mirroring feature of kernel arranges such mirror region into normal zone and other region into movable zone. And with mirroring feature enabled, the code and date of kernel can only be located in more reliable mirror region. However, the current KASLR code doesn't check EFI memory entries, and could choose new position in non-mirrored region. This will break the functionality of the address range mirroring feature.
So if EFI is detected, iterate EFI memory map and pick the mirror region to process for adding candidate of randomization slot. If EFI is disabled or no mirror region found, still process e820 memory map.
--- arch/x86/boot/compressed/kaslr.c | 68 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-)
arch/x86/boot/compressed/kaslr.c +66 -2
@@ -37,7 +37,9 @@#include <linux/uts.h> #include <linux/utsname.h> #include <linux/ctype.h> +#include <linux/efi.h> #include <generated/utsrelease.h> +#include <asm/efi.h> /* Macros used by the included decompressor code below. */ #define STATIC
@@ -558,6 +560,65 @@ static void process_mem_region(struct mem_vector *entry, } } +#ifdef CONFIG_EFI +/* +* Returns true if mirror region found (and must have been processed +* for slots adding) +*/ +static bool +process_efi_entries (unsigned long minimum,unsigned long image_size) +{ +struct efi_info *e = &boot_params->efi_info; +bool efi_mirror_found = false; +struct mem_vector region; +efi_memory_desc_t *md; +unsigned long pmap; +char *signature; +u32 nr_desc; +int i; + + signature = (char *)&e->efi_loader_signature; +if (strncmp (signature,EFI32_LOADER_SIGNATURE ,4 ) && +strncmp (signature,EFI64_LOADER_SIGNATURE ,4 )) +return false; + +#ifdef CONFIG_X86_32 +/* Can't handle data above 4GB at this time */ +if (e->efi_memmap_hi) { +warn ("EFI memmap is above 4GB, can't be handled now on x86_32. EFI should be disabled.\n" ); +return false; + } + pmap = e->efi_memmap; +#else + pmap = (e->efi_memmap | ((__u64 )e->efi_memmap_hi <<32 )); +#endif + + nr_desc = e->efi_memmap_size / e->efi_memdesc_size; +for (i =0 ; i < nr_desc; i++) { + md =efi_early_memdesc_ptr (pmap, e->efi_memdesc_size, i); +if (md->attribute &EFI_MEMORY_MORE_RELIABLE ) { + region.start = md->phys_addr; + region.size = md->num_pages <<EFI_PAGE_SHIFT ; +process_mem_region (®ion, minimum, image_size); + efi_mirror_found = true; + +if (slot_area_index ==MAX_SLOT_AREA ) { +debug_putstr ("Aborted EFI scan (slot_areas full)!\n" ); +break ; + } + } + } + +return efi_mirror_found; +} +#else +static inline bool +process_efi_entries (unsigned long minimum,unsigned long image_size) +{ +return false; +} +#endif +static void process_e820_entries (unsigned long minimum,unsigned long image_size) {
@@ -586,13 +647,16 @@ static unsigned long find_random_phys_addr(unsigned long minimum, {/* Check if we had too many memmaps. */ if (memmap_too_large) { -debug_putstr ("Aborted e820 scan (more than 4 memmap= args)!\n" ); +debug_putstr ("Aborted memory entries scan (more than 4 memmap= args)!\n" );return 0 ; }/* Make sure minimum is aligned. */ minimum =ALIGN (minimum,CONFIG_PHYSICAL_ALIGN ); +if (process_efi_entries (minimum, image_size)) +return slots_fetch_random (); +process_e820_entries (minimum, image_size);return slots_fetch_random (); }
@@ -652,7 +716,7 @@ void choose_random_location(unsigned long input,*/ min_addr =min (*output,512UL <<20 ); -/* Walk e820 and find a random address. */ +/* Walk available memory entries to find a random address. */ random_addr =find_random_phys_addr (min_addr, output_size);if (!random_addr) {warn ("Physical KASLR disabled: no suitable memory region!" );
Signature · 1 line
2.5.5
tip-bot for Baoquan He
merge bot[tip:x86/boot] x86/boot/KASLR: Prefer mirrored memory regions for the kernel physical address
x86/boot/KASLR: Prefer mirrored memory regions for the kernel physical address
Commit-ID: c05cd79750fbe5415cda896bb99350603cc995ed Gitweb: http://git.kernel.org/tip/c05cd79750fbe5415cda896bb99350603cc995ed Author: Baoquan He <[email protected]> AuthorDate: Mon, 14 Aug 2017 22:54:24 +0800 Committer: Ingo Molnar <[email protected]> CommitDate: Thu, 17 Aug 2017 10:51:35 +0200
x86/boot/KASLR: Prefer mirrored memory regions for the kernel physical address
Currently KASLR will parse all e820 entries of RAM type and add all candidate positions into the slots array. After that we choose one slot randomly as the new position which the kernel will be decompressed into and run at.
On systems with EFI enabled, e820 memory regions are coming from EFI memory regions by combining adjacent regions.
These EFI memory regions have various attributes, and the "mirrored" attribute is one of them. The physical memory region whose descriptors in EFI memory map has EFI_MEMORY_MORE_RELIABLE attribute (bit: 16) are mirrored. The address range mirroring feature of the kernel arranges such mirrored regions into normal zones and other regions into movable zones.
With the mirroring feature enabled, the code and data of the kernel can only be located in the more reliable mirrored regions. However, the current KASLR code doesn't check EFI memory entries, and could choose a new kernel position in non-mirrored regions. This will break the intended functionality of the address range mirroring feature.
To fix this, if EFI is detected, iterate EFI memory map and pick the mirrored region to process for adding candidate of randomization slot. If EFI is disabled or no mirrored region found, still process the e820 memory map.
--- arch/x86/boot/compressed/kaslr.c | 68 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-)
arch/x86/boot/compressed/kaslr.c +66 -2
@@ -37,7 +37,9 @@#include <linux/uts.h> #include <linux/utsname.h> #include <linux/ctype.h> +#include <linux/efi.h> #include <generated/utsrelease.h> +#include <asm/efi.h> /* Macros used by the included decompressor code below. */ #define STATIC
@@ -558,6 +560,65 @@ static void process_mem_region(struct mem_vector *entry, } } +#ifdef CONFIG_EFI +/* +* Returns true if mirror region found (and must have been processed +* for slots adding) +*/ +static bool +process_efi_entries (unsigned long minimum,unsigned long image_size) +{ +struct efi_info *e = &boot_params->efi_info; +bool efi_mirror_found = false; +struct mem_vector region; +efi_memory_desc_t *md; +unsigned long pmap; +char *signature; +u32 nr_desc; +int i; + + signature = (char *)&e->efi_loader_signature; +if (strncmp (signature,EFI32_LOADER_SIGNATURE ,4 ) && +strncmp (signature,EFI64_LOADER_SIGNATURE ,4 )) +return false; + +#ifdef CONFIG_X86_32 +/* Can't handle data above 4GB at this time */ +if (e->efi_memmap_hi) { +warn ("EFI memmap is above 4GB, can't be handled now on x86_32. EFI should be disabled.\n" ); +return false; + } + pmap = e->efi_memmap; +#else + pmap = (e->efi_memmap | ((__u64 )e->efi_memmap_hi <<32 )); +#endif + + nr_desc = e->efi_memmap_size / e->efi_memdesc_size; +for (i =0 ; i < nr_desc; i++) { + md =efi_early_memdesc_ptr (pmap, e->efi_memdesc_size, i); +if (md->attribute &EFI_MEMORY_MORE_RELIABLE ) { + region.start = md->phys_addr; + region.size = md->num_pages <<EFI_PAGE_SHIFT ; +process_mem_region (®ion, minimum, image_size); + efi_mirror_found = true; + +if (slot_area_index ==MAX_SLOT_AREA ) { +debug_putstr ("Aborted EFI scan (slot_areas full)!\n" ); +break ; + } + } + } + +return efi_mirror_found; +} +#else +static inline bool +process_efi_entries (unsigned long minimum,unsigned long image_size) +{ +return false; +} +#endif +static void process_e820_entries (unsigned long minimum,unsigned long image_size) {
@@ -586,13 +647,16 @@ static unsigned long find_random_phys_addr(unsigned long minimum, {/* Check if we had too many memmaps. */ if (memmap_too_large) { -debug_putstr ("Aborted e820 scan (more than 4 memmap= args)!\n" ); +debug_putstr ("Aborted memory entries scan (more than 4 memmap= args)!\n" );return 0 ; }/* Make sure minimum is aligned. */ minimum =ALIGN (minimum,CONFIG_PHYSICAL_ALIGN ); +if (process_efi_entries (minimum, image_size)) +return slots_fetch_random (); +process_e820_entries (minimum, image_size);return slots_fetch_random (); }
@@ -652,7 +716,7 @@ void choose_random_location(unsigned long input,*/ min_addr =min (*output,512UL <<20 ); -/* Walk e820 and find a random address. */ +/* Walk available memory entries to find a random address. */ random_addr =find_random_phys_addr (min_addr, output_size);if (!random_addr) {warn ("Physical KASLR disabled: no suitable memory region!" );
Quoted 5 lines · Baoquan He
Currently KASLR will parse all e820 entries of RAM type and add all candidate position into slots array. Then we will choose one slot randomly as the new position which kernel will be decompressed into and run at.
Quoted 12 lines
On system with EFI enabled, e820 memory regions are coming from EFI memory regions by combining adjacent regions. While these EFI memory regions have more attributes to mark their different use. Mirror attribute is such kind. The physical memory region whose descriptors in EFI memory map has
EFI_MEMORY_MORE_RELIABLEattribute (bit: 16) are mirrored. The address range mirroring feature of kernel arranges such mirror region into normal zone and other region into movable zone. And with mirroring feature enabled, the code and date of kernel can only be located in more reliable mirror region. However, the current KASLR code doesn't check EFI memory entries, and could choose new position in non-mirrored region. This will break the functionality of the address range mirroring feature.
Thanks a lot for helping improving the patch log, Ingo! Will pay more attention to the description in words and paragraph partition of log.
Quoted 120 lines
So if EFI is detected, iterate EFI memory map and pick the mirror region to process for adding candidate of randomization slot. If EFI is disabled or no mirror region found, still process e820 memory map.
Signed-off-by Baoquan He <[email protected]>--- arch/x86/boot/compressed/kaslr.c | 68 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-)arch/x86/boot/compressed/kaslr.c +66
-2@@ -37,7 +37,9 @@#include <linux/uts.h> #include <linux/utsname.h> #include <linux/ctype.h> +#include <linux/efi.h> #include <generated/utsrelease.h> +#include <asm/efi.h> /* Macros used by the included decompressor code below. */ #define STATIC @@ -558,6 +560,65 @@ static void process_mem_region(struct mem_vector *entry, } } +#ifdef CONFIG_EFI +/* +* Returns true if mirror region found (and must have been processed +* for slots adding) +*/ +static bool +process_efi_entries (unsigned long minimum,unsigned long image_size) +{ +struct efi_info *e = &boot_params->efi_info; +bool efi_mirror_found = false; +struct mem_vector region; +efi_memory_desc_t *md; +unsigned long pmap; +char *signature; +u32 nr_desc; +int i; + + signature = (char *)&e->efi_loader_signature; +if (strncmp (signature,EFI32_LOADER_SIGNATURE ,4 ) && +strncmp (signature,EFI64_LOADER_SIGNATURE ,4 )) +return false; + +#ifdef CONFIG_X86_32 +/* Can't handle data above 4GB at this time */ +if (e->efi_memmap_hi) { +warn ("EFI memmap is above 4GB, can't be handled now on x86_32. EFI should be disabled.\n" ); +return false; + } + pmap = e->efi_memmap; +#else + pmap = (e->efi_memmap | ((__u64 )e->efi_memmap_hi <<32 )); +#endif + + nr_desc = e->efi_memmap_size / e->efi_memdesc_size; +for (i =0 ; i < nr_desc; i++) { + md =efi_early_memdesc_ptr (pmap, e->efi_memdesc_size, i); +if (md->attribute &EFI_MEMORY_MORE_RELIABLE ) { + region.start = md->phys_addr; + region.size = md->num_pages <<EFI_PAGE_SHIFT ; +process_mem_region (®ion, minimum, image_size); + efi_mirror_found = true; + +if (slot_area_index ==MAX_SLOT_AREA ) { +debug_putstr ("Aborted EFI scan (slot_areas full)!\n" ); +break ; + } + } + } + +return efi_mirror_found; +} +#else +static inline bool +process_efi_entries (unsigned long minimum,unsigned long image_size) +{ +return false; +} +#endif +static void process_e820_entries (unsigned long minimum,unsigned long image_size) {@@ -586,13 +647,16 @@ static unsigned long find_random_phys_addr(unsigned long minimum, {/* Check if we had too many memmaps. */ if (memmap_too_large) { -debug_putstr ("Aborted e820 scan (more than 4 memmap= args)!\n" ); +debug_putstr ("Aborted memory entries scan (more than 4 memmap= args)!\n" );return 0 ; }/* Make sure minimum is aligned. */ minimum =ALIGN (minimum,CONFIG_PHYSICAL_ALIGN ); +if (process_efi_entries (minimum, image_size)) +return slots_fetch_random (); +process_e820_entries (minimum, image_size);return slots_fetch_random (); }@@ -652,7 +716,7 @@ void choose_random_location(unsigned long input,*/ min_addr =min (*output,512UL <<20 ); -/* Walk e820 and find a random address. */ +/* Walk available memory entries to find a random address. */ random_addr =find_random_phys_addr (min_addr, output_size);if (!random_addr) {warn ("Physical KASLR disabled: no suitable memory region!" );Signature · 1 line
2.5.5
Quoted 19 lines · Baoquan He
Quoted 5 lines · Baoquan He
Currently KASLR will parse all e820 entries of RAM type and add all candidate position into slots array. Then we will choose one slot randomly as the new position which kernel will be decompressed into and run at.
Quoted 12 lines
On system with EFI enabled, e820 memory regions are coming from EFI memory regions by combining adjacent regions. While these EFI memory regions have more attributes to mark their different use. Mirror attribute is such kind. The physical memory region whose descriptors in EFI memory map has
EFI_MEMORY_MORE_RELIABLEattribute (bit: 16) are mirrored. The address range mirroring feature of kernel arranges such mirror region into normal zone and other region into movable zone. And with mirroring feature enabled, the code and date of kernel can only be located in more reliable mirror region. However, the current KASLR code doesn't check EFI memory entries, and could choose new position in non-mirrored region. This will break the functionality of the address range mirroring feature.
Quoted 9 lines
Thanks a lot for helping improving the patch log, Ingo! Will pay more attention to the description in words and paragraph partition of log.
Quoted 6 lines
So if EFI is detected, iterate EFI memory map and pick the mirror region to process for adding candidate of randomization slot. If EFI is disabled or no mirror region found, still process e820 memory map.
Signed-off-by Baoquan He <[email protected]>
Could the x86 people on cc either take these directly, or indicate whether they are ok with this going in via the EFI tree?
Thanks.
Quoted 114 lines
Quoted 114 lines
--- arch/x86/boot/compressed/kaslr.c | 68 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-)arch/x86/boot/compressed/kaslr.c +66
-3@@ -37,7 +37,9 @@#include <linux/uts.h> #include <linux/utsname.h> #include <linux/ctype.h> +#include <linux/efi.h> #include <generated/utsrelease.h> +#include <asm/efi.h> /* Macros used by the included decompressor code below. */ #define STATIC @@ -558,6 +560,65 @@ static void process_mem_region(struct mem_vector *entry, } } +#ifdef CONFIG_EFI +/* +* Returns true if mirror region found (and must have been processed +* for slots adding) +*/ +static bool +process_efi_entries (unsigned long minimum,unsigned long image_size) +{ +struct efi_info *e = &boot_params->efi_info; +bool efi_mirror_found = false; +struct mem_vector region; +efi_memory_desc_t *md; +unsigned long pmap; +char *signature; +u32 nr_desc; +int i; + + signature = (char *)&e->efi_loader_signature; +if (strncmp (signature,EFI32_LOADER_SIGNATURE ,4 ) && +strncmp (signature,EFI64_LOADER_SIGNATURE ,4 )) +return false; + +#ifdef CONFIG_X86_32 +/* Can't handle data above 4GB at this time */ +if (e->efi_memmap_hi) { +warn ("EFI memmap is above 4GB, can't be handled now on x86_32. EFI should be disabled.\n" ); +return false; + } + pmap = e->efi_memmap; +#else + pmap = (e->efi_memmap | ((__u64 )e->efi_memmap_hi <<32 )); +#endif + + nr_desc = e->efi_memmap_size / e->efi_memdesc_size; +for (i =0 ; i < nr_desc; i++) { + md =efi_early_memdesc_ptr (pmap, e->efi_memdesc_size, i); +if (md->attribute &EFI_MEMORY_MORE_RELIABLE ) { + region.start = md->phys_addr; + region.size = md->num_pages <<EFI_PAGE_SHIFT ; +process_mem_region (®ion, minimum, image_size); + efi_mirror_found = true; + +if (slot_area_index ==MAX_SLOT_AREA ) { +debug_putstr ("Aborted EFI scan (slot_areas full)!\n" ); +break ; + } + } + } + +return efi_mirror_found; +} +#else +static inline bool +process_efi_entries (unsigned long minimum,unsigned long image_size) +{ +return false; +} +#endif +static void process_e820_entries (unsigned long minimum,unsigned long image_size) {@@ -586,13 +647,16 @@ static unsigned long find_random_phys_addr(unsigned long minimum, {/* Check if we had too many memmaps. */ if (memmap_too_large) { -debug_putstr ("Aborted e820 scan (more than 4 memmap= args)!\n" ); +debug_putstr ("Aborted memory entries scan (more than 4 memmap= args)!\n" );return 0 ; }/* Make sure minimum is aligned. */ minimum =ALIGN (minimum,CONFIG_PHYSICAL_ALIGN ); +if (process_efi_entries (minimum, image_size)) +return slots_fetch_random (); +process_e820_entries (minimum, image_size);return slots_fetch_random (); }@@ -652,7 +716,7 @@ void choose_random_location(unsigned long input,*/ min_addr =min (*output,512UL <<20 ); -/* Walk e820 and find a random address. */ +/* Walk available memory entries to find a random address. */ random_addr =find_random_phys_addr (min_addr, output_size);if (!random_addr) {warn ("Physical KASLR disabled: no suitable memory region!" ); --2.5.5
Quoted 10 lines · Ard Biesheuvel
Quoted 8 lines · Baoquan He
Thanks a lot for helping improving the patch log, Ingo! Will pay more attention to the description in words and paragraph partition of log.
Quoted 5 lines
So if EFI is detected, iterate EFI memory map and pick the mirror region to process for adding candidate of randomization slot. If EFI is disabled or no mirror region found, still process e820 memory map.
Quoted 5 lines
Quoted 1 line
Quoted 1 line
Signed-off-by Baoquan He <[email protected]>Could the x86 people on cc either take these directly, or indicate whether they are ok with this going in via the EFI tree?
Thanks for looking into this, Ard. I saw Ingo has put these into tip/x86/boot.
Quoted 115 lines
Quoted 115 lines
Quoted 115 lines
--- arch/x86/boot/compressed/kaslr.c | 68 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-)arch/x86/boot/compressed/kaslr.c +66
-3@@ -37,7 +37,9 @@#include <linux/uts.h> #include <linux/utsname.h> #include <linux/ctype.h> +#include <linux/efi.h> #include <generated/utsrelease.h> +#include <asm/efi.h> /* Macros used by the included decompressor code below. */ #define STATIC @@ -558,6 +560,65 @@ static void process_mem_region(struct mem_vector *entry, } } +#ifdef CONFIG_EFI +/* +* Returns true if mirror region found (and must have been processed +* for slots adding) +*/ +static bool +process_efi_entries (unsigned long minimum,unsigned long image_size) +{ +struct efi_info *e = &boot_params->efi_info; +bool efi_mirror_found = false; +struct mem_vector region; +efi_memory_desc_t *md; +unsigned long pmap; +char *signature; +u32 nr_desc; +int i; + + signature = (char *)&e->efi_loader_signature; +if (strncmp (signature,EFI32_LOADER_SIGNATURE ,4 ) && +strncmp (signature,EFI64_LOADER_SIGNATURE ,4 )) +return false; + +#ifdef CONFIG_X86_32 +/* Can't handle data above 4GB at this time */ +if (e->efi_memmap_hi) { +warn ("EFI memmap is above 4GB, can't be handled now on x86_32. EFI should be disabled.\n" ); +return false; + } + pmap = e->efi_memmap; +#else + pmap = (e->efi_memmap | ((__u64 )e->efi_memmap_hi <<32 )); +#endif + + nr_desc = e->efi_memmap_size / e->efi_memdesc_size; +for (i =0 ; i < nr_desc; i++) { + md =efi_early_memdesc_ptr (pmap, e->efi_memdesc_size, i); +if (md->attribute &EFI_MEMORY_MORE_RELIABLE ) { + region.start = md->phys_addr; + region.size = md->num_pages <<EFI_PAGE_SHIFT ; +process_mem_region (®ion, minimum, image_size); + efi_mirror_found = true; + +if (slot_area_index ==MAX_SLOT_AREA ) { +debug_putstr ("Aborted EFI scan (slot_areas full)!\n" ); +break ; + } + } + } + +return efi_mirror_found; +} +#else +static inline bool +process_efi_entries (unsigned long minimum,unsigned long image_size) +{ +return false; +} +#endif +static void process_e820_entries (unsigned long minimum,unsigned long image_size) {@@ -586,13 +647,16 @@ static unsigned long find_random_phys_addr(unsigned long minimum, {/* Check if we had too many memmaps. */ if (memmap_too_large) { -debug_putstr ("Aborted e820 scan (more than 4 memmap= args)!\n" ); +debug_putstr ("Aborted memory entries scan (more than 4 memmap= args)!\n" );return 0 ; }/* Make sure minimum is aligned. */ minimum =ALIGN (minimum,CONFIG_PHYSICAL_ALIGN ); +if (process_efi_entries (minimum, image_size)) +return slots_fetch_random (); +process_e820_entries (minimum, image_size);return slots_fetch_random (); }@@ -652,7 +716,7 @@ void choose_random_location(unsigned long input,*/ min_addr =min (*output,512UL <<20 ); -/* Walk e820 and find a random address. */ +/* Walk available memory entries to find a random address. */ random_addr =find_random_phys_addr (min_addr, output_size);if (!random_addr) {warn ("Physical KASLR disabled: no suitable memory region!" ); --2.5.5