Blame SOURCES/0452-commands-boot-Add-API-to-pass-context-to-loader.patch

d18179
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
d18179
From: Chris Coulson <chris.coulson@canonical.com>
d18179
Date: Fri, 29 Apr 2022 21:16:02 +0100
d18179
Subject: [PATCH] commands/boot: Add API to pass context to loader
d18179
d18179
Loaders rely on global variables for saving context which is consumed
d18179
in the boot hook and freed in the unload hook. In the case where a loader
d18179
command is executed twice, calling grub_loader_set a second time executes
d18179
the unload hook, but in some cases this runs when the loader's global
d18179
context has already been updated, resulting in the updated context being
d18179
freed and potential use-after-free bugs when the boot hook is subsequently
d18179
called.
d18179
d18179
This adds a new API (grub_loader_set_ex) which allows a loader to specify
d18179
context that is passed to its boot and unload hooks. This is an alternative
d18179
to requiring that loaders call grub_loader_unset before mutating their
d18179
global context.
d18179
d18179
Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
d18179
(cherry picked from commit 4322a64dde7e8fedb58e50b79408667129d45dd3)
d18179
(cherry picked from commit 937ad0e2159b6b8cb0d2ce3515da3a8b797c7927)
d18179
(cherry picked from commit 873038ae7048f6cae8a3ebb2f97a8d361a080e13)
d18179
(cherry picked from commit 32ca6dd0281503696f003ba259bfcdad708f6314)
d18179
---
d18179
 grub-core/commands/boot.c | 66 +++++++++++++++++++++++++++++++++++++++++------
d18179
 include/grub/loader.h     |  5 ++++
d18179
 2 files changed, 63 insertions(+), 8 deletions(-)
d18179
d18179
diff --git a/grub-core/commands/boot.c b/grub-core/commands/boot.c
d18179
index bbca81e947..53691a62d9 100644
d18179
--- a/grub-core/commands/boot.c
d18179
+++ b/grub-core/commands/boot.c
d18179
@@ -27,10 +27,20 @@
d18179
 
d18179
 GRUB_MOD_LICENSE ("GPLv3+");
d18179
 
d18179
-static grub_err_t (*grub_loader_boot_func) (void);
d18179
-static grub_err_t (*grub_loader_unload_func) (void);
d18179
+static grub_err_t (*grub_loader_boot_func) (void *);
d18179
+static grub_err_t (*grub_loader_unload_func) (void *);
d18179
+static void *grub_loader_context;
d18179
 static int grub_loader_flags;
d18179
 
d18179
+struct grub_simple_loader_hooks
d18179
+{
d18179
+  grub_err_t (*boot) (void);
d18179
+  grub_err_t (*unload) (void);
d18179
+};
d18179
+
d18179
+/* Don't heap allocate this to avoid making grub_loader_set fallible. */
d18179
+static struct grub_simple_loader_hooks simple_loader_hooks;
d18179
+
d18179
 struct grub_preboot
d18179
 {
d18179
   grub_err_t (*preboot_func) (int);
d18179
@@ -44,6 +54,29 @@ static int grub_loader_loaded;
d18179
 static struct grub_preboot *preboots_head = 0,
d18179
   *preboots_tail = 0;
d18179
 
d18179
+static grub_err_t
d18179
+grub_simple_boot_hook (void *context)
d18179
+{
d18179
+  struct grub_simple_loader_hooks *hooks;
d18179
+
d18179
+  hooks = (struct grub_simple_loader_hooks *) context;
d18179
+  return hooks->boot ();
d18179
+}
d18179
+
d18179
+static grub_err_t
d18179
+grub_simple_unload_hook (void *context)
d18179
+{
d18179
+  struct grub_simple_loader_hooks *hooks;
d18179
+  grub_err_t ret;
d18179
+
d18179
+  hooks = (struct grub_simple_loader_hooks *) context;
d18179
+
d18179
+  ret = hooks->unload ();
d18179
+  grub_memset (hooks, 0, sizeof (*hooks));
d18179
+
d18179
+  return ret;
d18179
+}
d18179
+
d18179
 int
d18179
 grub_loader_is_loaded (void)
d18179
 {
d18179
@@ -110,28 +143,45 @@ grub_loader_unregister_preboot_hook (struct grub_preboot *hnd)
d18179
 }
d18179
 
d18179
 void
d18179
-grub_loader_set (grub_err_t (*boot) (void),
d18179
-		 grub_err_t (*unload) (void),
d18179
-		 int flags)
d18179
+grub_loader_set_ex (grub_err_t (*boot) (void *),
d18179
+		    grub_err_t (*unload) (void *),
d18179
+		    void *context,
d18179
+		    int flags)
d18179
 {
d18179
   if (grub_loader_loaded && grub_loader_unload_func)
d18179
-    grub_loader_unload_func ();
d18179
+    grub_loader_unload_func (grub_loader_context);
d18179
 
d18179
   grub_loader_boot_func = boot;
d18179
   grub_loader_unload_func = unload;
d18179
+  grub_loader_context = context;
d18179
   grub_loader_flags = flags;
d18179
 
d18179
   grub_loader_loaded = 1;
d18179
 }
d18179
 
d18179
+void
d18179
+grub_loader_set (grub_err_t (*boot) (void),
d18179
+		 grub_err_t (*unload) (void),
d18179
+		 int flags)
d18179
+{
d18179
+  grub_loader_set_ex (grub_simple_boot_hook,
d18179
+		      grub_simple_unload_hook,
d18179
+		      &simple_loader_hooks,
d18179
+		      flags);
d18179
+
d18179
+  simple_loader_hooks.boot = boot;
d18179
+  simple_loader_hooks.unload = unload;
d18179
+}
d18179
+
d18179
 void
d18179
 grub_loader_unset(void)
d18179
 {
d18179
   if (grub_loader_loaded && grub_loader_unload_func)
d18179
-    grub_loader_unload_func ();
d18179
+    grub_loader_unload_func (grub_loader_context);
d18179
 
d18179
   grub_loader_boot_func = 0;
d18179
   grub_loader_unload_func = 0;
d18179
+  grub_loader_context = 0;
d18179
 
d18179
   grub_loader_loaded = 0;
d18179
 }
d18179
@@ -158,7 +208,7 @@ grub_loader_boot (void)
d18179
 	  return err;
d18179
 	}
d18179
     }
d18179
-  err = (grub_loader_boot_func) ();
d18179
+  err = (grub_loader_boot_func) (grub_loader_context);
d18179
 
d18179
   for (cur = preboots_tail; cur; cur = cur->prev)
d18179
     if (! err)
d18179
diff --git a/include/grub/loader.h b/include/grub/loader.h
d18179
index b208642821..1846fa6c5f 100644
d18179
--- a/include/grub/loader.h
d18179
+++ b/include/grub/loader.h
d18179
@@ -40,6 +40,11 @@ void EXPORT_FUNC (grub_loader_set) (grub_err_t (*boot) (void),
d18179
 				    grub_err_t (*unload) (void),
d18179
 				    int flags);
d18179
 
d18179
+void EXPORT_FUNC (grub_loader_set_ex) (grub_err_t (*boot) (void *),
d18179
+				       grub_err_t (*unload) (void *),
d18179
+				       void *context,
d18179
+				       int flags);
d18179
+
d18179
 /* Unset current loader, if any.  */
d18179
 void EXPORT_FUNC (grub_loader_unset) (void);
d18179