[Skiboot] [PATCH v2 3/3] core/fast-reboot: zero memory after fast reboot

Nicholas Piggin npiggin at gmail.com
Mon Mar 26 16:06:01 AEDT 2018


This improves the security and predictability of the fast reboot
environment.

There can not be a secure fence between fast reboots, because a
malicious OS can modify the firmware itself. However a well-behaved
OS can have a reasonable expectation that OS memory regions it has
modified will be cleared upon fast reboot.

The memory is zeroed after all other CPUs come up from fast reboot,
just before the new kernel is loaded and booted into. This allows
image preloading to run concurrently, and will allow parallelisation
of the clearing in future.

Signed-off-by: Nicholas Piggin <npiggin at gmail.com>
---
 core/fast-reboot.c | 18 +++++++++++++++
 core/mem_region.c  | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 82 insertions(+)

diff --git a/core/fast-reboot.c b/core/fast-reboot.c
index 9f8c4d9a..1f6293f7 100644
--- a/core/fast-reboot.c
+++ b/core/fast-reboot.c
@@ -352,6 +352,22 @@ void __noreturn fast_reboot_entry(void)
 	cpu_set_sreset_enable(true);
 	cpu_set_ipi_enable(true);
 
+	if (!chip_quirk(QUIRK_MAMBO_CALLOUTS)) {
+		/*
+		 * mem_region_clear_unused avoids these preload regions
+		 * so it can run along side image preloading. Clear these
+		 * regions now to catch anything not overwritten by
+		 * preload.
+		 *
+		 * Mambo may have embedded payload here, so don't clear
+		 * it at all. The OS may have already overwritten it, so
+		 * mambo really should reserve memory regions for this, if
+		 * fast reboot is to work reliably.
+		 */
+		memset(KERNEL_LOAD_BASE, 0, KERNEL_LOAD_SIZE);
+		memset(INITRAMFS_LOAD_BASE, 0, INITRAMFS_LOAD_SIZE);
+	}
+
 	/* Start preloading kernel and ramdisk */
 	start_preload_kernel();
 
@@ -380,6 +396,8 @@ void __noreturn fast_reboot_entry(void)
 
 	ipmi_set_fw_progress_sensor(IPMI_FW_PCI_INIT);
 
+	mem_region_clear_unused();
+
 	/* Load and boot payload */
 	load_and_boot_kernel(true);
 }
diff --git a/core/mem_region.c b/core/mem_region.c
index aa0c850b..2e0b3211 100644
--- a/core/mem_region.c
+++ b/core/mem_region.c
@@ -1171,6 +1171,70 @@ void mem_region_release_unused(void)
 	unlock(&mem_region_lock);
 }
 
+static void mem_clear_range(uint64_t s, uint64_t e)
+{
+	uint64_t res_start, res_end;
+
+	/* Skip exception vectors */
+	if (s < EXCEPTION_VECTORS_END)
+		s = EXCEPTION_VECTORS_END;
+
+	/* Skip kernel preload area */
+	res_start = (uint64_t)KERNEL_LOAD_BASE;
+	res_end = res_start + KERNEL_LOAD_SIZE;
+
+	if (s >= res_start && s < res_end)
+	       s = res_end;
+	if (e > res_start && e <= res_end)
+	       e = res_start;
+	if (e <= s)
+		return;
+	if (s < res_start && e > res_end) {
+		mem_clear_range(s, res_start);
+		mem_clear_range(res_end, e);
+		return;
+	}
+
+	/* Skip initramfs preload area */
+	res_start = (uint64_t)INITRAMFS_LOAD_BASE;
+	res_end = res_start + INITRAMFS_LOAD_SIZE;
+
+	if (s >= res_start && s < res_end)
+	       s = res_end;
+	if (e > res_start && e <= res_end)
+	       e = res_start;
+	if (e <= s)
+		return;
+	if (s < res_start && e > res_end) {
+		mem_clear_range(s, res_start);
+		mem_clear_range(res_end, e);
+		return;
+	}
+
+	prlog(PR_NOTICE, "Clearing region %llx-%llx\n", s, e);
+	memset((void *)s, 0, e - s);
+}
+
+void mem_region_clear_unused(void)
+{
+	struct mem_region *r;
+
+	lock(&mem_region_lock);
+	assert(mem_regions_finalised);
+
+	prlog(PR_NOTICE, "Clearing unused memory:\n");
+	list_for_each(&regions, r, list) {
+		/* If it's not unused, ignore it. */
+		if (!(r->type == REGION_OS))
+			continue;
+
+		assert(r != &skiboot_heap);
+
+		mem_clear_range(r->start, r->start + r->len);
+	}
+	unlock(&mem_region_lock);
+}
+
 static void mem_region_add_dt_reserved_node(struct dt_node *parent,
 		struct mem_region *region)
 {
-- 
2.16.1



More information about the Skiboot mailing list