2019-12-06 16:58:08

by Masayoshi Mizuma

[permalink] [raw]
Subject: [PATCH v6 0/4] Adjust the padding size for KASLR

From: Masayoshi Mizuma <[email protected]>

The system sometimes crashes while memory hot-adding on KASLR
enabled system. The crash happens because the regions pointed by
kaslr_regions[].base are overwritten by the hot-added memory.

It happens because of the padding size for kaslr_regions[].base isn't
enough for the system whose physical memory layout has huge space for
memory hotplug. kaslr_regions[].base points "actual installed
memory size + padding" or higher address. So, if the "actual + padding"
is lower address than the maximum memory address, which means the memory
address reachable by memory hot-add, kaslr_regions[].base is destroyed by
the overwritten.

address
^
|------- maximum memory address (Hotplug)
| ^
|------- kaslr_regions[0].base | Hotadd-able region
| ^ |
| | padding |
| V V
|------- actual memory address (Installed on boot)
|

Fix it by getting the maximum memory address from SRAT and store
the value in boot_param, then set the padding size while KASLR
initializing if the default padding size isn't enough.

Changelog:
v6: - Add documents to top of calc_direct_mapping_size() to
explain why the size of direct mapping area is configured
as boot_params.max_addr (0004 patch).
- Add Acked-by from Baoquan (0004 patch).

Masayoshi Mizuma (4):
x86/boot: Wrap up the SRAT traversing code into subtable_parse()
x86/boot: Add max_addr field in struct boot_params
x86/boot: Get the max address from SRAT
x86/mm/KASLR: Adjust the padding size for the direct mapping.

Documentation/x86/zero-page.rst | 4 ++
arch/x86/boot/compressed/acpi.c | 42 +++++++++++++++---
arch/x86/include/uapi/asm/bootparam.h | 2 +-
arch/x86/mm/kaslr.c | 63 ++++++++++++++++++++-------
4 files changed, 88 insertions(+), 23 deletions(-)

--
2.20.1


2019-12-06 16:58:41

by Masayoshi Mizuma

[permalink] [raw]
Subject: [PATCH v6 4/4] x86/mm/KASLR: Adjust the padding size for the direct mapping.

From: Masayoshi Mizuma <[email protected]>

The system sometimes crashes while memory hot-adding on KASLR
enabled system. The crash happens because the regions pointed by
kaslr_regions[].base are overwritten by the hot-added memory.

It happens because of the padding size for kaslr_regions[].base isn't
enough for the system whose physical memory layout has huge space for
memory hotplug. kaslr_regions[].base points "actual installed
memory size + padding" or higher address. So, if the "actual + padding"
is lower address than the maximum memory address, which means the memory
address reachable by memory hot-add, kaslr_regions[].base is destroyed by
the overwritten.

address
^
|------- maximum memory address (Hotplug)
| ^
|------- kaslr_regions[0].base | Hotadd-able region
| ^ |
| | padding |
| V V
|------- actual memory address (Installed on boot)
|

Fix it by getting the maximum memory address from SRAT and store
the value in boot_param, then set the padding size while KASLR
initializing if the default padding size isn't enough.

Acked-by: Baoquan He <[email protected]>
Signed-off-by: Masayoshi Mizuma <[email protected]>
---
arch/x86/mm/kaslr.c | 63 ++++++++++++++++++++++++++++++++++-----------
1 file changed, 48 insertions(+), 15 deletions(-)

diff --git a/arch/x86/mm/kaslr.c b/arch/x86/mm/kaslr.c
index dc6182eecefa..b3f2f468d20f 100644
--- a/arch/x86/mm/kaslr.c
+++ b/arch/x86/mm/kaslr.c
@@ -70,15 +70,58 @@ static inline bool kaslr_memory_enabled(void)
return kaslr_enabled() && !IS_ENABLED(CONFIG_KASAN);
}

+/*
+ * Even though a huge virtual address space is reserved for the direct
+ * mapping of physical memory, e.g in 4-level paging mode, it's 64TB,
+ * rare system can own enough physical memory to use it up, most are
+ * even less than 1TB. So with KASLR enabled, we adapt the size of
+ * direct mapping area to the size of actual physical memory plus the
+ * configured padding CONFIG_RANDOMIZE_MEMORY_PHYSICAL_PADDING.
+ * The left part will be taken out to join memory randomization.
+ *
+ * And in case CONFIG_MEMORY_HOTPLUG is enabled and boot_params.max_addr
+ * has a physical memory address, that means the system memory can be
+ * expanded by memory hot-add to boot_params.max_addr. Set the size
+ * of direct mapping area to boot_params.max_addr so that we can avoid
+ * overwriting the hot-added memory.
+ */
+static inline unsigned long calc_direct_mapping_size(void)
+{
+ unsigned long size_tb, memory_tb;
+
+ memory_tb = DIV_ROUND_UP(max_pfn << PAGE_SHIFT, 1UL << TB_SHIFT) +
+ CONFIG_RANDOMIZE_MEMORY_PHYSICAL_PADDING;
+
+#ifdef CONFIG_MEMORY_HOTPLUG
+ if (boot_params.max_addr) {
+ unsigned long maximum_tb;
+
+ maximum_tb = DIV_ROUND_UP(boot_params.max_addr,
+ 1UL << TB_SHIFT);
+
+ if (maximum_tb > memory_tb)
+ memory_tb = maximum_tb;
+ }
+#endif
+ size_tb = 1 << (MAX_PHYSMEM_BITS - TB_SHIFT);
+
+ /*
+ * Adapt physical memory region size based on available memory
+ */
+ if (memory_tb < size_tb)
+ size_tb = memory_tb;
+
+ return size_tb;
+}
+
/* Initialize base and padding for each memory region randomized with KASLR */
void __init kernel_randomize_memory(void)
{
- size_t i;
- unsigned long vaddr_start, vaddr;
- unsigned long rand, memory_tb;
- struct rnd_state rand_state;
+ unsigned long vaddr_start, vaddr, rand;
unsigned long remain_entropy;
unsigned long vmemmap_size;
+ struct rnd_state rand_state;
+ size_t i;

vaddr_start = pgtable_l5_enabled() ? __PAGE_OFFSET_BASE_L5 : __PAGE_OFFSET_BASE_L4;
vaddr = vaddr_start;
@@ -95,20 +138,10 @@ void __init kernel_randomize_memory(void)
if (!kaslr_memory_enabled())
return;

- kaslr_regions[0].size_tb = 1 << (MAX_PHYSMEM_BITS - TB_SHIFT);
+ kaslr_regions[0].size_tb = calc_direct_mapping_size();
kaslr_regions[1].size_tb = VMALLOC_SIZE_TB;

- /*
- * Update Physical memory mapping to available and
- * add padding if needed (especially for memory hotplug support).
- */
BUG_ON(kaslr_regions[0].base != &page_offset_base);
- memory_tb = DIV_ROUND_UP(max_pfn << PAGE_SHIFT, 1UL << TB_SHIFT) +
- CONFIG_RANDOMIZE_MEMORY_PHYSICAL_PADDING;
-
- /* Adapt phyiscal memory region size based on available memory */
- if (memory_tb < kaslr_regions[0].size_tb)
- kaslr_regions[0].size_tb = memory_tb;

/*
* Calculate the vmemmap region size in TBs, aligned to a TB
--
2.20.1

2019-12-06 16:58:46

by Masayoshi Mizuma

[permalink] [raw]
Subject: [PATCH v6 1/4] x86/boot: Wrap up the SRAT traversing code into subtable_parse()

From: Masayoshi Mizuma <[email protected]>

Wrap up the SRAT traversing code into subtable_parse().

Reviewed-by: Baoquan He <[email protected]>
Signed-off-by: Masayoshi Mizuma <[email protected]>
---
arch/x86/boot/compressed/acpi.c | 21 ++++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/arch/x86/boot/compressed/acpi.c b/arch/x86/boot/compressed/acpi.c
index 25019d42ae93..a0f81438a0fd 100644
--- a/arch/x86/boot/compressed/acpi.c
+++ b/arch/x86/boot/compressed/acpi.c
@@ -362,6 +362,19 @@ static unsigned long get_acpi_srat_table(void)
return 0;
}

+static void subtable_parse(struct acpi_subtable_header *sub_table, int *num)
+{
+ struct acpi_srat_mem_affinity *ma;
+
+ ma = (struct acpi_srat_mem_affinity *)sub_table;
+
+ if (!(ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && ma->length) {
+ immovable_mem[*num].start = ma->base_address;
+ immovable_mem[*num].size = ma->length;
+ (*num)++;
+ }
+}
+
/**
* count_immovable_mem_regions - Parse SRAT and cache the immovable
* memory regions into the immovable_mem array.
@@ -395,14 +408,8 @@ int count_immovable_mem_regions(void)
while (table + sizeof(struct acpi_subtable_header) < table_end) {
sub_table = (struct acpi_subtable_header *)table;
if (sub_table->type == ACPI_SRAT_TYPE_MEMORY_AFFINITY) {
- struct acpi_srat_mem_affinity *ma;

- ma = (struct acpi_srat_mem_affinity *)sub_table;
- if (!(ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && ma->length) {
- immovable_mem[num].start = ma->base_address;
- immovable_mem[num].size = ma->length;
- num++;
- }
+ subtable_parse(sub_table, &num);

if (num >= MAX_NUMNODES*2) {
debug_putstr("Too many immovable memory regions, aborting.\n");
--
2.20.1

2019-12-06 16:59:14

by Masayoshi Mizuma

[permalink] [raw]
Subject: [PATCH v6 3/4] x86/boot: Get the max address from SRAT

From: Masayoshi Mizuma <[email protected]>

Get the max address from SRAT and write it into boot_params->max_addr.

Acked-by: Baoquan He <[email protected]>
Signed-off-by: Masayoshi Mizuma <[email protected]>
---
arch/x86/boot/compressed/acpi.c | 33 +++++++++++++++++++++++++++------
1 file changed, 27 insertions(+), 6 deletions(-)

diff --git a/arch/x86/boot/compressed/acpi.c b/arch/x86/boot/compressed/acpi.c
index a0f81438a0fd..47db706656e0 100644
--- a/arch/x86/boot/compressed/acpi.c
+++ b/arch/x86/boot/compressed/acpi.c
@@ -362,17 +362,32 @@ static unsigned long get_acpi_srat_table(void)
return 0;
}

-static void subtable_parse(struct acpi_subtable_header *sub_table, int *num)
+/**
+ * subtable_parse - Cache the immovable memory regions into the
+ * immovable_mem array and update the index of the array.
+ *
+ * Return the end physical address of hotpluggable region.
+ * The system memory can be extended to the address by hotplug.
+ */
+static unsigned long subtable_parse(struct acpi_subtable_header *sub_table,
+ int *num)
{
struct acpi_srat_mem_affinity *ma;
+ unsigned long addr = 0;

ma = (struct acpi_srat_mem_affinity *)sub_table;

- if (!(ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && ma->length) {
- immovable_mem[*num].start = ma->base_address;
- immovable_mem[*num].size = ma->length;
- (*num)++;
+ if (ma->length) {
+ if (ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE)
+ addr = ma->base_address + ma->length;
+ else {
+ immovable_mem[*num].start = ma->base_address;
+ immovable_mem[*num].size = ma->length;
+ (*num)++;
+ }
}
+
+ return addr;
}

/**
@@ -391,6 +406,7 @@ int count_immovable_mem_regions(void)
struct acpi_subtable_header *sub_table;
struct acpi_table_header *table_header;
char arg[MAX_ACPI_ARG_LENGTH];
+ unsigned long max_addr = 0, addr;
int num = 0;

if (cmdline_find_option("acpi", arg, sizeof(arg)) == 3 &&
@@ -409,7 +425,9 @@ int count_immovable_mem_regions(void)
sub_table = (struct acpi_subtable_header *)table;
if (sub_table->type == ACPI_SRAT_TYPE_MEMORY_AFFINITY) {

- subtable_parse(sub_table, &num);
+ addr = subtable_parse(sub_table, &num);
+ if (addr > max_addr)
+ max_addr = addr;

if (num >= MAX_NUMNODES*2) {
debug_putstr("Too many immovable memory regions, aborting.\n");
@@ -418,6 +436,9 @@ int count_immovable_mem_regions(void)
}
table += sub_table->length;
}
+
+ boot_params->max_addr = max_addr;
+
return num;
}
#endif /* CONFIG_RANDOMIZE_BASE && CONFIG_MEMORY_HOTREMOVE */
--
2.20.1

2019-12-06 16:59:50

by Masayoshi Mizuma

[permalink] [raw]
Subject: [PATCH v6 2/4] x86/boot: Add max_addr field in struct boot_params

From: Masayoshi Mizuma <[email protected]>

Add max_addr field in struct boot_params. max_addr shows the
maximum memory address to be reachable by memory hot-add.
max_addr is set by parsing ACPI SRAT.

Reviewed-by: Baoquan He <[email protected]>
Signed-off-by: Masayoshi Mizuma <[email protected]>
---
Documentation/x86/zero-page.rst | 4 ++++
arch/x86/include/uapi/asm/bootparam.h | 2 +-
2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/Documentation/x86/zero-page.rst b/Documentation/x86/zero-page.rst
index f088f5881666..cc3938d68481 100644
--- a/Documentation/x86/zero-page.rst
+++ b/Documentation/x86/zero-page.rst
@@ -19,6 +19,7 @@ Offset/Size Proto Name Meaning
058/008 ALL tboot_addr Physical address of tboot shared page
060/010 ALL ist_info Intel SpeedStep (IST) BIOS support information
(struct ist_info)
+078/010 ALL max_addr The possible maximum physical memory address [1]_
080/010 ALL hd0_info hd0 disk parameter, OBSOLETE!!
090/010 ALL hd1_info hd1 disk parameter, OBSOLETE!!
0A0/010 ALL sys_desc_table System description table (struct sys_desc_table),
@@ -43,3 +44,6 @@ Offset/Size Proto Name Meaning
(array of struct e820_entry)
D00/1EC ALL eddbuf EDD data (array of struct edd_info)
=========== ===== ======================= =================================================
+
+.. [1] max_addr shows the maximum memory address to be reachable by memory
+ hot-add. max_addr is set by parsing ACPI SRAT.
diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h
index c895df5482c5..6efad338bba9 100644
--- a/arch/x86/include/uapi/asm/bootparam.h
+++ b/arch/x86/include/uapi/asm/bootparam.h
@@ -158,7 +158,7 @@ struct boot_params {
__u64 tboot_addr; /* 0x058 */
struct ist_info ist_info; /* 0x060 */
__u64 acpi_rsdp_addr; /* 0x070 */
- __u8 _pad3[8]; /* 0x078 */
+ __u64 max_addr; /* 0x078 */
__u8 hd0_info[16]; /* obsolete! */ /* 0x080 */
__u8 hd1_info[16]; /* obsolete! */ /* 0x090 */
struct sys_desc_table sys_desc_table; /* obsolete! */ /* 0x0a0 */
--
2.20.1