xref: /OK3568_Linux_fs/buildroot/boot/grub2/0132-kern-parser-Fix-a-stack-buffer-overflow.patch (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593SmuzhiyunFrom 4ea7bae51f97e49c84dc67ea30b466ca8633b9f6 Mon Sep 17 00:00:00 2001
2*4882a593SmuzhiyunFrom: Chris Coulson <chris.coulson@canonical.com>
3*4882a593SmuzhiyunDate: Thu, 7 Jan 2021 19:21:03 +0000
4*4882a593SmuzhiyunSubject: [PATCH] kern/parser: Fix a stack buffer overflow
5*4882a593Smuzhiyun
6*4882a593Smuzhiyungrub_parser_split_cmdline() expands variable names present in the supplied
7*4882a593Smuzhiyuncommand line in to their corresponding variable contents and uses a 1 kiB
8*4882a593Smuzhiyunstack buffer for temporary storage without sufficient bounds checking. If
9*4882a593Smuzhiyunthe function is called with a command line that references a variable with
10*4882a593Smuzhiyuna sufficiently large payload, it is possible to overflow the stack
11*4882a593Smuzhiyunbuffer via tab completion, corrupt the stack frame and potentially
12*4882a593Smuzhiyuncontrol execution.
13*4882a593Smuzhiyun
14*4882a593SmuzhiyunFixes: CVE-2020-27749
15*4882a593Smuzhiyun
16*4882a593SmuzhiyunReported-by: Chris Coulson <chris.coulson@canonical.com>
17*4882a593SmuzhiyunSigned-off-by: Chris Coulson <chris.coulson@canonical.com>
18*4882a593SmuzhiyunSigned-off-by: Darren Kenny <darren.kenny@oracle.com>
19*4882a593SmuzhiyunReviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
20*4882a593SmuzhiyunSigned-off-by: Stefan Sørensen <stefan.sorensen@spectralink.com>
21*4882a593Smuzhiyun---
22*4882a593Smuzhiyun grub-core/kern/parser.c | 110 +++++++++++++++++++++++++++++-------------------
23*4882a593Smuzhiyun 1 file changed, 67 insertions(+), 43 deletions(-)
24*4882a593Smuzhiyun
25*4882a593Smuzhiyundiff --git a/grub-core/kern/parser.c b/grub-core/kern/parser.c
26*4882a593Smuzhiyunindex e010eaa..6ab7aa4 100644
27*4882a593Smuzhiyun--- a/grub-core/kern/parser.c
28*4882a593Smuzhiyun+++ b/grub-core/kern/parser.c
29*4882a593Smuzhiyun@@ -18,6 +18,7 @@
30*4882a593Smuzhiyun  */
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun #include <grub/parser.h>
33*4882a593Smuzhiyun+#include <grub/buffer.h>
34*4882a593Smuzhiyun #include <grub/env.h>
35*4882a593Smuzhiyun #include <grub/misc.h>
36*4882a593Smuzhiyun #include <grub/mm.h>
37*4882a593Smuzhiyun@@ -107,8 +108,8 @@ check_varstate (grub_parser_state_t s)
38*4882a593Smuzhiyun }
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun-static void
42*4882a593Smuzhiyun-add_var (char *varname, char **bp, char **vp,
43*4882a593Smuzhiyun+static grub_err_t
44*4882a593Smuzhiyun+add_var (grub_buffer_t varname, grub_buffer_t buf,
45*4882a593Smuzhiyun 	 grub_parser_state_t state, grub_parser_state_t newstate)
46*4882a593Smuzhiyun {
47*4882a593Smuzhiyun   const char *val;
48*4882a593Smuzhiyun@@ -116,31 +117,41 @@ add_var (char *varname, char **bp, char **vp,
49*4882a593Smuzhiyun   /* Check if a variable was being read in and the end of the name
50*4882a593Smuzhiyun      was reached.  */
51*4882a593Smuzhiyun   if (!(check_varstate (state) && !check_varstate (newstate)))
52*4882a593Smuzhiyun-    return;
53*4882a593Smuzhiyun+    return GRUB_ERR_NONE;
54*4882a593Smuzhiyun+
55*4882a593Smuzhiyun+  if (grub_buffer_append_char (varname, '\0') != GRUB_ERR_NONE)
56*4882a593Smuzhiyun+    return grub_errno;
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun-  *((*vp)++) = '\0';
59*4882a593Smuzhiyun-  val = grub_env_get (varname);
60*4882a593Smuzhiyun-  *vp = varname;
61*4882a593Smuzhiyun+  val = grub_env_get ((const char *) grub_buffer_peek_data (varname));
62*4882a593Smuzhiyun+  grub_buffer_reset (varname);
63*4882a593Smuzhiyun   if (!val)
64*4882a593Smuzhiyun-    return;
65*4882a593Smuzhiyun+    return GRUB_ERR_NONE;
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun   /* Insert the contents of the variable in the buffer.  */
68*4882a593Smuzhiyun-  for (; *val; val++)
69*4882a593Smuzhiyun-    *((*bp)++) = *val;
70*4882a593Smuzhiyun+  return grub_buffer_append_data (buf, val, grub_strlen (val));
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun-static void
74*4882a593Smuzhiyun-terminate_arg (char *buffer, char **bp, int *argc)
75*4882a593Smuzhiyun+static grub_err_t
76*4882a593Smuzhiyun+terminate_arg (grub_buffer_t buffer, int *argc)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun-  if (*bp != buffer && *((*bp) - 1) != '\0')
79*4882a593Smuzhiyun-    {
80*4882a593Smuzhiyun-      *((*bp)++) = '\0';
81*4882a593Smuzhiyun-      (*argc)++;
82*4882a593Smuzhiyun-    }
83*4882a593Smuzhiyun+  grub_size_t unread = grub_buffer_get_unread_bytes (buffer);
84*4882a593Smuzhiyun+
85*4882a593Smuzhiyun+  if (unread == 0)
86*4882a593Smuzhiyun+    return GRUB_ERR_NONE;
87*4882a593Smuzhiyun+
88*4882a593Smuzhiyun+  if (*(const char *) grub_buffer_peek_data_at (buffer, unread - 1) == '\0')
89*4882a593Smuzhiyun+    return GRUB_ERR_NONE;
90*4882a593Smuzhiyun+
91*4882a593Smuzhiyun+  if (grub_buffer_append_char (buffer, '\0') != GRUB_ERR_NONE)
92*4882a593Smuzhiyun+    return grub_errno;
93*4882a593Smuzhiyun+
94*4882a593Smuzhiyun+  (*argc)++;
95*4882a593Smuzhiyun+
96*4882a593Smuzhiyun+  return GRUB_ERR_NONE;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun static grub_err_t
100*4882a593Smuzhiyun-process_char (char c, char *buffer, char **bp, char *varname, char **vp,
101*4882a593Smuzhiyun+process_char (char c, grub_buffer_t buffer, grub_buffer_t varname,
102*4882a593Smuzhiyun 	      grub_parser_state_t state, int *argc,
103*4882a593Smuzhiyun 	      grub_parser_state_t *newstate)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun@@ -153,12 +164,13 @@ process_char (char c, char *buffer, char **bp, char *varname, char **vp,
106*4882a593Smuzhiyun    * not describe the variable anymore, write the variable to
107*4882a593Smuzhiyun    * the buffer.
108*4882a593Smuzhiyun    */
109*4882a593Smuzhiyun-  add_var (varname, bp, vp, state, *newstate);
110*4882a593Smuzhiyun+  if (add_var (varname, buffer, state, *newstate) != GRUB_ERR_NONE)
111*4882a593Smuzhiyun+    return grub_errno;
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun   if (check_varstate (*newstate))
114*4882a593Smuzhiyun     {
115*4882a593Smuzhiyun       if (use)
116*4882a593Smuzhiyun-	*((*vp)++) = use;
117*4882a593Smuzhiyun+        return grub_buffer_append_char (varname, use);
118*4882a593Smuzhiyun     }
119*4882a593Smuzhiyun   else if (*newstate == GRUB_PARSER_STATE_TEXT &&
120*4882a593Smuzhiyun 	   state != GRUB_PARSER_STATE_ESC && grub_isspace (use))
121*4882a593Smuzhiyun@@ -167,10 +179,10 @@ process_char (char c, char *buffer, char **bp, char *varname, char **vp,
122*4882a593Smuzhiyun        * Don't add more than one argument if multiple
123*4882a593Smuzhiyun        * spaces are used.
124*4882a593Smuzhiyun        */
125*4882a593Smuzhiyun-      terminate_arg (buffer, bp, argc);
126*4882a593Smuzhiyun+      return terminate_arg (buffer, argc);
127*4882a593Smuzhiyun     }
128*4882a593Smuzhiyun   else if (use)
129*4882a593Smuzhiyun-    *((*bp)++) = use;
130*4882a593Smuzhiyun+    return grub_buffer_append_char (buffer, use);
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun   return GRUB_ERR_NONE;
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun@@ -181,19 +193,22 @@ grub_parser_split_cmdline (const char *cmdline,
135*4882a593Smuzhiyun 			   int *argc, char ***argv)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun   grub_parser_state_t state = GRUB_PARSER_STATE_TEXT;
138*4882a593Smuzhiyun-  /* XXX: Fixed size buffer, perhaps this buffer should be dynamically
139*4882a593Smuzhiyun-     allocated.  */
140*4882a593Smuzhiyun-  char buffer[1024];
141*4882a593Smuzhiyun-  char *bp = buffer;
142*4882a593Smuzhiyun+  grub_buffer_t buffer, varname;
143*4882a593Smuzhiyun   char *rd = (char *) cmdline;
144*4882a593Smuzhiyun   char *rp = rd;
145*4882a593Smuzhiyun-  char varname[200];
146*4882a593Smuzhiyun-  char *vp = varname;
147*4882a593Smuzhiyun-  char *args;
148*4882a593Smuzhiyun   int i;
149*4882a593Smuzhiyun
150*4882a593Smuzhiyun   *argc = 0;
151*4882a593Smuzhiyun   *argv = NULL;
152*4882a593Smuzhiyun+
153*4882a593Smuzhiyun+  buffer = grub_buffer_new (1024);
154*4882a593Smuzhiyun+  if (buffer == NULL)
155*4882a593Smuzhiyun+    return grub_errno;
156*4882a593Smuzhiyun+
157*4882a593Smuzhiyun+  varname = grub_buffer_new (200);
158*4882a593Smuzhiyun+  if (varname == NULL)
159*4882a593Smuzhiyun+    goto fail;
160*4882a593Smuzhiyun+
161*4882a593Smuzhiyun   do
162*4882a593Smuzhiyun     {
163*4882a593Smuzhiyun       if (rp == NULL || *rp == '\0')
164*4882a593Smuzhiyun@@ -219,7 +234,7 @@ grub_parser_split_cmdline (const char *cmdline,
165*4882a593Smuzhiyun 	{
166*4882a593Smuzhiyun 	  grub_parser_state_t newstate;
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun-	  if (process_char (*rp, buffer, &bp, varname, &vp, state, argc,
169*4882a593Smuzhiyun+	  if (process_char (*rp, buffer, varname, state, argc,
170*4882a593Smuzhiyun 			    &newstate) != GRUB_ERR_NONE)
171*4882a593Smuzhiyun 	    goto fail;
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun@@ -230,10 +245,12 @@ grub_parser_split_cmdline (const char *cmdline,
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun   /* A special case for when the last character was part of a
176*4882a593Smuzhiyun      variable.  */
177*4882a593Smuzhiyun-  add_var (varname, &bp, &vp, state, GRUB_PARSER_STATE_TEXT);
178*4882a593Smuzhiyun+  if (add_var (varname, buffer, state, GRUB_PARSER_STATE_TEXT) != GRUB_ERR_NONE)
179*4882a593Smuzhiyun+    goto fail;
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun   /* Ensure that the last argument is terminated. */
182*4882a593Smuzhiyun-  terminate_arg (buffer, &bp, argc);
183*4882a593Smuzhiyun+  if (terminate_arg (buffer, argc) != GRUB_ERR_NONE)
184*4882a593Smuzhiyun+    goto fail;
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun   /* If there are no args, then we're done. */
187*4882a593Smuzhiyun   if (!*argc)
188*4882a593Smuzhiyun@@ -242,38 +259,45 @@ grub_parser_split_cmdline (const char *cmdline,
189*4882a593Smuzhiyun       goto out;
190*4882a593Smuzhiyun     }
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun-  /* Reserve memory for the return values.  */
193*4882a593Smuzhiyun-  args = grub_malloc (bp - buffer);
194*4882a593Smuzhiyun-  if (!args)
195*4882a593Smuzhiyun-    goto fail;
196*4882a593Smuzhiyun-  grub_memcpy (args, buffer, bp - buffer);
197*4882a593Smuzhiyun-
198*4882a593Smuzhiyun   *argv = grub_calloc (*argc + 1, sizeof (char *));
199*4882a593Smuzhiyun   if (!*argv)
200*4882a593Smuzhiyun     goto fail;
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun   /* The arguments are separated with 0's, setup argv so it points to
203*4882a593Smuzhiyun      the right values.  */
204*4882a593Smuzhiyun-  bp = args;
205*4882a593Smuzhiyun   for (i = 0; i < *argc; i++)
206*4882a593Smuzhiyun     {
207*4882a593Smuzhiyun-      (*argv)[i] = bp;
208*4882a593Smuzhiyun-      while (*bp)
209*4882a593Smuzhiyun-	bp++;
210*4882a593Smuzhiyun-      bp++;
211*4882a593Smuzhiyun+      char *arg;
212*4882a593Smuzhiyun+
213*4882a593Smuzhiyun+      if (i > 0)
214*4882a593Smuzhiyun+	{
215*4882a593Smuzhiyun+	  if (grub_buffer_advance_read_pos (buffer, 1) != GRUB_ERR_NONE)
216*4882a593Smuzhiyun+	    goto fail;
217*4882a593Smuzhiyun+	}
218*4882a593Smuzhiyun+
219*4882a593Smuzhiyun+      arg = (char *) grub_buffer_peek_data (buffer);
220*4882a593Smuzhiyun+      if (arg == NULL ||
221*4882a593Smuzhiyun+	  grub_buffer_advance_read_pos (buffer, grub_strlen (arg)) != GRUB_ERR_NONE)
222*4882a593Smuzhiyun+	goto fail;
223*4882a593Smuzhiyun+
224*4882a593Smuzhiyun+      (*argv)[i] = arg;
225*4882a593Smuzhiyun     }
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun+  /* Keep memory for the return values. */
228*4882a593Smuzhiyun+  grub_buffer_take_data (buffer);
229*4882a593Smuzhiyun+
230*4882a593Smuzhiyun   grub_errno = GRUB_ERR_NONE;
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun  out:
233*4882a593Smuzhiyun   if (rd != cmdline)
234*4882a593Smuzhiyun     grub_free (rd);
235*4882a593Smuzhiyun+  grub_buffer_free (buffer);
236*4882a593Smuzhiyun+  grub_buffer_free (varname);
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun   return grub_errno;
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun  fail:
241*4882a593Smuzhiyun   grub_free (*argv);
242*4882a593Smuzhiyun-  grub_free (args);
243*4882a593Smuzhiyun   goto out;
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun--
247*4882a593Smuzhiyun2.14.2
248*4882a593Smuzhiyun
249