1From 14ceb3b3ff6db664649138442b6562c114dcf56e Mon Sep 17 00:00:00 2001
2From: Chris Coulson <chris.coulson@canonical.com>
3Date: Tue, 5 Apr 2022 10:58:28 +0100
4Subject: [PATCH] commands/boot: Add API to pass context to loader
5
6Loaders rely on global variables for saving context which is consumed
7in the boot hook and freed in the unload hook. In the case where a loader
8command is executed twice, calling grub_loader_set() a second time executes
9the unload hook, but in some cases this runs when the loader's global
10context has already been updated, resulting in the updated context being
11freed and potential use-after-free bugs when the boot hook is subsequently
12called.
13
14This adds a new API, grub_loader_set_ex(), which allows a loader to specify
15context that is passed to its boot and unload hooks. This is an alternative
16to requiring that loaders call grub_loader_unset() before mutating their
17global context.
18
19Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
20Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
21
22Upstream-Status: Backport
23
24Reference to upstream patch:
25https://git.savannah.gnu.org/cgit/grub.git/commit/?id=14ceb3b3ff6db664649138442b6562c114dcf56e
26
27Signed-off-by: Xiangyu Chen <xiangyu.chen@windriver.com>
28---
29 grub-core/commands/boot.c | 66 ++++++++++++++++++++++++++++++++++-----
30 include/grub/loader.h     |  5 +++
31 2 files changed, 63 insertions(+), 8 deletions(-)
32
33diff --git a/grub-core/commands/boot.c b/grub-core/commands/boot.c
34index bbca81e94..61514788e 100644
35--- a/grub-core/commands/boot.c
36+++ b/grub-core/commands/boot.c
37@@ -27,10 +27,20 @@
38
39 GRUB_MOD_LICENSE ("GPLv3+");
40
41-static grub_err_t (*grub_loader_boot_func) (void);
42-static grub_err_t (*grub_loader_unload_func) (void);
43+static grub_err_t (*grub_loader_boot_func) (void *context);
44+static grub_err_t (*grub_loader_unload_func) (void *context);
45+static void *grub_loader_context;
46 static int grub_loader_flags;
47
48+struct grub_simple_loader_hooks
49+{
50+  grub_err_t (*boot) (void);
51+  grub_err_t (*unload) (void);
52+};
53+
54+/* Don't heap allocate this to avoid making grub_loader_set() fallible. */
55+static struct grub_simple_loader_hooks simple_loader_hooks;
56+
57 struct grub_preboot
58 {
59   grub_err_t (*preboot_func) (int);
60@@ -44,6 +54,29 @@ static int grub_loader_loaded;
61 static struct grub_preboot *preboots_head = 0,
62   *preboots_tail = 0;
63
64+static grub_err_t
65+grub_simple_boot_hook (void *context)
66+{
67+  struct grub_simple_loader_hooks *hooks;
68+
69+  hooks = (struct grub_simple_loader_hooks *) context;
70+  return hooks->boot ();
71+}
72+
73+static grub_err_t
74+grub_simple_unload_hook (void *context)
75+{
76+  struct grub_simple_loader_hooks *hooks;
77+  grub_err_t ret;
78+
79+  hooks = (struct grub_simple_loader_hooks *) context;
80+
81+  ret = hooks->unload ();
82+  grub_memset (hooks, 0, sizeof (*hooks));
83+
84+  return ret;
85+}
86+
87 int
88 grub_loader_is_loaded (void)
89 {
90@@ -110,28 +143,45 @@ grub_loader_unregister_preboot_hook (struct grub_preboot *hnd)
91 }
92
93 void
94-grub_loader_set (grub_err_t (*boot) (void),
95-		 grub_err_t (*unload) (void),
96-		 int flags)
97+grub_loader_set_ex (grub_err_t (*boot) (void *context),
98+		    grub_err_t (*unload) (void *context),
99+		    void *context,
100+		    int flags)
101 {
102   if (grub_loader_loaded && grub_loader_unload_func)
103-    grub_loader_unload_func ();
104+    grub_loader_unload_func (grub_loader_context);
105
106   grub_loader_boot_func = boot;
107   grub_loader_unload_func = unload;
108+  grub_loader_context = context;
109   grub_loader_flags = flags;
110
111   grub_loader_loaded = 1;
112 }
113
114+void
115+grub_loader_set (grub_err_t (*boot) (void),
116+		 grub_err_t (*unload) (void),
117+		 int flags)
118+{
119+  grub_loader_set_ex (grub_simple_boot_hook,
120+		      grub_simple_unload_hook,
121+		      &simple_loader_hooks,
122+		      flags);
123+
124+  simple_loader_hooks.boot = boot;
125+  simple_loader_hooks.unload = unload;
126+}
127+
128 void
129 grub_loader_unset(void)
130 {
131   if (grub_loader_loaded && grub_loader_unload_func)
132-    grub_loader_unload_func ();
133+    grub_loader_unload_func (grub_loader_context);
134
135   grub_loader_boot_func = 0;
136   grub_loader_unload_func = 0;
137+  grub_loader_context = 0;
138
139   grub_loader_loaded = 0;
140 }
141@@ -158,7 +208,7 @@ grub_loader_boot (void)
142 	  return err;
143 	}
144     }
145-  err = (grub_loader_boot_func) ();
146+  err = (grub_loader_boot_func) (grub_loader_context);
147
148   for (cur = preboots_tail; cur; cur = cur->prev)
149     if (! err)
150diff --git a/include/grub/loader.h b/include/grub/loader.h
151index b20864282..97f231054 100644
152--- a/include/grub/loader.h
153+++ b/include/grub/loader.h
154@@ -40,6 +40,11 @@ void EXPORT_FUNC (grub_loader_set) (grub_err_t (*boot) (void),
155 				    grub_err_t (*unload) (void),
156 				    int flags);
157
158+void EXPORT_FUNC (grub_loader_set_ex) (grub_err_t (*boot) (void *context),
159+				       grub_err_t (*unload) (void *context),
160+				       void *context,
161+				       int flags);
162+
163 /* Unset current loader, if any.  */
164 void EXPORT_FUNC (grub_loader_unset) (void);
165
166--
1672.34.1
168
169