Skip to content

Commit

Permalink
core/fast-reboot: zero memory after fast reboot
Browse files Browse the repository at this point in the history
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@gmail.com>
Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
  • Loading branch information
npiggin authored and stewartsmith committed Mar 27, 2018
1 parent 336f306 commit c32943b
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 0 deletions.
18 changes: 18 additions & 0 deletions core/fast-reboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,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();

Expand Down Expand Up @@ -379,6 +395,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);
}
64 changes: 64 additions & 0 deletions core/mem_region.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down

0 comments on commit c32943b

Please sign in to comment.