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

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