From f7bd9986f607a924bf23b813900a8595f2815f0c Mon Sep 17 00:00:00 2001
From: Alexey Makhalov <amakhalov@vmware.com>
Date: Mon, 20 Jul 2020 23:03:05 +0000
Subject: [PATCH 25/27] efi: Fix use-after-free in halt/reboot path

commit 92bfc33db984 ("efi: Free malloc regions on exit")
introduced memory freeing in grub_efi_fini(), which is
used not only by exit path but by halt/reboot one as well.
As result of memory freeing, code and data regions used by
modules, such as halt, reboot, acpi (used by halt) also got
freed. After return to module code, CPU executes, filled
by UEFI firmware (tested with edk2), 0xAFAFAFAF pattern as
a code. Which leads to #UD exception later.

grub> halt
!!!! X64 Exception Type - 06(#UD - Invalid Opcode)  CPU Apic ID - 00000000 !!!!
RIP  - 0000000003F4EC28, CS  - 0000000000000038, RFLAGS - 0000000000200246
RAX  - 0000000000000000, RCX - 00000000061DA188, RDX - 0A74C0854DC35D41
RBX  - 0000000003E10E08, RSP - 0000000007F0F860, RBP - 0000000000000000
RSI  - 00000000064DB768, RDI - 000000000832C5C3
R8   - 0000000000000002, R9  - 0000000000000000, R10 - 00000000061E2E52
R11  - 0000000000000020, R12 - 0000000003EE5C1F, R13 - 00000000061E0FF4
R14  - 0000000003E10D80, R15 - 00000000061E2F60
DS   - 0000000000000030, ES  - 0000000000000030, FS  - 0000000000000030
GS   - 0000000000000030, SS  - 0000000000000030
CR0  - 0000000080010033, CR2 - 0000000000000000, CR3 - 0000000007C01000
CR4  - 0000000000000668, CR8 - 0000000000000000
DR0  - 0000000000000000, DR1 - 0000000000000000, DR2 - 0000000000000000
DR3  - 0000000000000000, DR6 - 00000000FFFF0FF0, DR7 - 0000000000000400
GDTR - 00000000079EEA98 0000000000000047, LDTR - 0000000000000000
IDTR - 0000000007598018 0000000000000FFF,   TR - 0000000000000000
FXSAVE_STATE - 0000000007F0F4C0

Proposal here is to continue to free allocated memory for
exit boot services path but keep it for halt/reboot path
as it won't be much security concern here.
Introduced GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY
loader flag to be used by efi halt/reboot path.

Signed-off-by: Alexey Makhalov <amakhalov@vmware.com>
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/kern/arm/efi/init.c   | 3 +++
 grub-core/kern/arm64/efi/init.c | 3 +++
 grub-core/kern/efi/efi.c        | 3 ++-
 grub-core/kern/efi/init.c       | 1 -
 grub-core/kern/i386/efi/init.c  | 9 +++++++--
 grub-core/kern/ia64/efi/init.c  | 9 +++++++--
 grub-core/kern/riscv/efi/init.c | 3 +++
 grub-core/lib/efi/halt.c        | 3 ++-
 include/grub/loader.h           | 1 +
 9 files changed, 28 insertions(+), 7 deletions(-)

diff --git a/grub-core/kern/arm/efi/init.c b/grub-core/kern/arm/efi/init.c
index 06df60e..40c3b46 100644
--- a/grub-core/kern/arm/efi/init.c
+++ b/grub-core/kern/arm/efi/init.c
@@ -71,4 +71,7 @@ grub_machine_fini (int flags)
   efi_call_1 (b->close_event, tmr_evt);
 
   grub_efi_fini ();
+
+  if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY))
+    grub_efi_memory_fini ();
 }
diff --git a/grub-core/kern/arm64/efi/init.c b/grub-core/kern/arm64/efi/init.c
index 6224999..5010cae 100644
--- a/grub-core/kern/arm64/efi/init.c
+++ b/grub-core/kern/arm64/efi/init.c
@@ -57,4 +57,7 @@ grub_machine_fini (int flags)
     return;
 
   grub_efi_fini ();
+
+  if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY))
+    grub_efi_memory_fini ();
 }
diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
index d6f7bf0..c45c8f4 100644
--- a/grub-core/kern/efi/efi.c
+++ b/grub-core/kern/efi/efi.c
@@ -157,7 +157,8 @@ grub_efi_get_loaded_image (grub_efi_handle_t image_handle)
 void
 grub_reboot (void)
 {
-  grub_machine_fini (GRUB_LOADER_FLAG_NORETURN);
+  grub_machine_fini (GRUB_LOADER_FLAG_NORETURN |
+		     GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY);
   efi_call_4 (grub_efi_system_table->runtime_services->reset_system,
               GRUB_EFI_RESET_COLD, GRUB_EFI_SUCCESS, 0, NULL);
   for (;;) ;
diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c
index d1afa3a..2ffb520 100644
--- a/grub-core/kern/efi/init.c
+++ b/grub-core/kern/efi/init.c
@@ -131,5 +131,4 @@ grub_efi_fini (void)
 {
   grub_efidisk_fini ();
   grub_console_fini ();
-  grub_efi_memory_fini ();
 }
diff --git a/grub-core/kern/i386/efi/init.c b/grub-core/kern/i386/efi/init.c
index da499ab..deb2eac 100644
--- a/grub-core/kern/i386/efi/init.c
+++ b/grub-core/kern/i386/efi/init.c
@@ -39,6 +39,11 @@ grub_machine_init (void)
 void
 grub_machine_fini (int flags)
 {
-  if (flags & GRUB_LOADER_FLAG_NORETURN)
-    grub_efi_fini ();
+  if (!(flags & GRUB_LOADER_FLAG_NORETURN))
+    return;
+
+  grub_efi_fini ();
+
+  if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY))
+    grub_efi_memory_fini ();
 }
diff --git a/grub-core/kern/ia64/efi/init.c b/grub-core/kern/ia64/efi/init.c
index b5ecbd0..f196557 100644
--- a/grub-core/kern/ia64/efi/init.c
+++ b/grub-core/kern/ia64/efi/init.c
@@ -70,6 +70,11 @@ grub_machine_init (void)
 void
 grub_machine_fini (int flags)
 {
-  if (flags & GRUB_LOADER_FLAG_NORETURN)
-    grub_efi_fini ();
+  if (!(flags & GRUB_LOADER_FLAG_NORETURN))
+    return;
+
+  grub_efi_fini ();
+
+  if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY))
+    grub_efi_memory_fini ();
 }
diff --git a/grub-core/kern/riscv/efi/init.c b/grub-core/kern/riscv/efi/init.c
index 7eb1969..38795fe 100644
--- a/grub-core/kern/riscv/efi/init.c
+++ b/grub-core/kern/riscv/efi/init.c
@@ -73,4 +73,7 @@ grub_machine_fini (int flags)
     return;
 
   grub_efi_fini ();
+
+  if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY))
+    grub_efi_memory_fini ();
 }
diff --git a/grub-core/lib/efi/halt.c b/grub-core/lib/efi/halt.c
index 5859f04..29d4136 100644
--- a/grub-core/lib/efi/halt.c
+++ b/grub-core/lib/efi/halt.c
@@ -28,7 +28,8 @@
 void
 grub_halt (void)
 {
-  grub_machine_fini (GRUB_LOADER_FLAG_NORETURN);
+  grub_machine_fini (GRUB_LOADER_FLAG_NORETURN |
+		     GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY);
 #if !defined(__ia64__) && !defined(__arm__) && !defined(__aarch64__) && \
     !defined(__riscv)
   grub_acpi_halt ();
diff --git a/include/grub/loader.h b/include/grub/loader.h
index 7f82a49..b208642 100644
--- a/include/grub/loader.h
+++ b/include/grub/loader.h
@@ -33,6 +33,7 @@ enum
 {
   GRUB_LOADER_FLAG_NORETURN = 1,
   GRUB_LOADER_FLAG_PXE_NOT_UNLOAD = 2,
+  GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY = 4,
 };
 
 void EXPORT_FUNC (grub_loader_set) (grub_err_t (*boot) (void),
-- 
2.23.0

