This commit is contained in:
yangl777 2021-03-11 21:28:54 +08:00
parent e21afe78df
commit 581b26921d
13 changed files with 1261 additions and 1 deletions

View File

@ -0,0 +1,50 @@
From d06161b035dde4769199ad65aa0a587a5920012b Mon Sep 17 00:00:00 2001
From: Darren Kenny <darren.kenny@oracle.com>
Date: Fri, 22 Jan 2021 12:32:41 +0000
Subject: kern/parser: Fix resource leak if argc == 0
After processing the command-line yet arriving at the point where we are
setting argv, we are allocating memory, even if argc == 0, which makes
no sense since we never put anything into the allocated argv.
The solution is to simply return that we've successfully processed the
arguments but that argc == 0, and also ensure that argv is NULL when
we're not allocating anything in it.
There are only 2 callers of this function, and both are handling a zero
value in argc assuming nothing is allocated in argv.
Fixes: CID 96680
Signed-off-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/kern/parser.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/grub-core/kern/parser.c b/grub-core/kern/parser.c
index 619db31..d1cf061 100644
--- a/grub-core/kern/parser.c
+++ b/grub-core/kern/parser.c
@@ -146,6 +146,7 @@ grub_parser_split_cmdline (const char *cmdline,
int i;
*argc = 0;
+ *argv = NULL;
do
{
if (!rd || !*rd)
@@ -207,6 +208,10 @@ grub_parser_split_cmdline (const char *cmdline,
(*argc)++;
}
+ /* If there are no args, then we're done. */
+ if (!*argc)
+ return 0;
+
/* Reserve memory for the return values. */
args = grub_malloc (bp - buffer);
if (!args)
--
2.23.0

View File

@ -0,0 +1,77 @@
From c6c426e5ab6ea715153b72584de6bd8c82f698ec Mon Sep 17 00:00:00 2001
From: Chris Coulson <chris.coulson@canonical.com>
Date: Wed, 18 Nov 2020 00:59:24 +0000
Subject: kern/parser: Fix a memory leak
The getline() function supplied to grub_parser_split_cmdline() returns
a newly allocated buffer and can be called multiple times, but the
returned buffer is never freed.
Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/kern/parser.c | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/grub-core/kern/parser.c b/grub-core/kern/parser.c
index d1cf061..0163a62 100644
--- a/grub-core/kern/parser.c
+++ b/grub-core/kern/parser.c
@@ -141,6 +141,7 @@ grub_parser_split_cmdline (const char *cmdline,
char *bp = buffer;
char *rd = (char *) cmdline;
char varname[200];
+ char *rp = rd;
char *vp = varname;
char *args;
int i;
@@ -149,10 +150,18 @@ grub_parser_split_cmdline (const char *cmdline,
*argv = NULL;
do
{
- if (!rd || !*rd)
- {
+ if (rp == NULL || *rp == '\0')
+ {
+ if (rd != cmdline)
+ {
+ grub_free (rd);
+ rd = rp = NULL;
+ }
if (getline)
- getline (&rd, 1, getline_data);
+ {
+ getline (&rd, 1, getline_data);
+ rp = rd;
+ }
else
break;
}
@@ -160,12 +169,12 @@ grub_parser_split_cmdline (const char *cmdline,
if (!rd)
break;
- for (; *rd; rd++)
+ for (; *rp != '\0'; rp++)
{
grub_parser_state_t newstate;
char use;
- newstate = grub_parser_cmdline_state (state, *rd, &use);
+ newstate = grub_parser_cmdline_state (state, *rp, &use);
/* If a variable was being processed and this character does
not describe the variable anymore, write the variable to
@@ -198,6 +207,9 @@ grub_parser_split_cmdline (const char *cmdline,
}
while (state != GRUB_PARSER_STATE_TEXT && !check_varstate (state));
+ if (rd != cmdline)
+ grub_free (rd);
+
/* A special case for when the last character was part of a
variable. */
add_var (varname, &bp, &vp, state, GRUB_PARSER_STATE_TEXT);
--
2.23.0

View File

@ -0,0 +1,119 @@
From b1c9e9e889e4273fb15712051c887e6078511448 Mon Sep 17 00:00:00 2001
From: Chris Coulson <chris.coulson@canonical.com>
Date: Tue, 5 Jan 2021 22:17:28 +0000
Subject: kern/parser: Introduce process_char() helper
grub_parser_split_cmdline() iterates over each command line character.
In order to add error checking and to simplify the subsequent error
handling, split the character processing in to a separate function.
Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/kern/parser.c | 74 +++++++++++++++++++++++++----------------
1 file changed, 46 insertions(+), 28 deletions(-)
diff --git a/grub-core/kern/parser.c b/grub-core/kern/parser.c
index 0163a62..7e1ba99 100644
--- a/grub-core/kern/parser.c
+++ b/grub-core/kern/parser.c
@@ -1,7 +1,7 @@
/* parser.c - the part of the parser that can return partial tokens */
/*
* GRUB -- GRand Unified Bootloader
- * Copyright (C) 2005,2007,2009 Free Software Foundation, Inc.
+ * Copyright (C) 2005,2007,2009,2021 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -129,6 +129,46 @@ add_var (char *varname, char **bp, char **vp,
*((*bp)++) = *val;
}
+static grub_err_t
+process_char (char c, char *buffer, char **bp, char *varname, char **vp,
+ grub_parser_state_t state, int *argc,
+ grub_parser_state_t *newstate)
+{
+ char use;
+
+ *newstate = grub_parser_cmdline_state (state, c, &use);
+
+ /*
+ * If a variable was being processed and this character does
+ * not describe the variable anymore, write the variable to
+ * the buffer.
+ */
+ add_var (varname, bp, vp, state, *newstate);
+
+ if (check_varstate (*newstate))
+ {
+ if (use)
+ *((*vp)++) = use;
+ }
+ else if (*newstate == GRUB_PARSER_STATE_TEXT &&
+ state != GRUB_PARSER_STATE_ESC && grub_isspace (use))
+ {
+ /*
+ * Don't add more than one argument if multiple
+ * spaces are used.
+ */
+ if (*bp != buffer && *((*bp) - 1) != '\0')
+ {
+ *((*bp)++) = '\0';
+ (*argc)++;
+ }
+ }
+ else if (use)
+ *((*bp)++) = use;
+
+ return GRUB_ERR_NONE;
+}
+
grub_err_t
grub_parser_split_cmdline (const char *cmdline,
grub_reader_getline_t getline, void *getline_data,
@@ -172,35 +212,13 @@ grub_parser_split_cmdline (const char *cmdline,
for (; *rp != '\0'; rp++)
{
grub_parser_state_t newstate;
- char use;
-
- newstate = grub_parser_cmdline_state (state, *rp, &use);
- /* If a variable was being processed and this character does
- not describe the variable anymore, write the variable to
- the buffer. */
- add_var (varname, &bp, &vp, state, newstate);
-
- if (check_varstate (newstate))
- {
- if (use)
- *(vp++) = use;
- }
- else
+ if (process_char (*rp, buffer, &bp, varname, &vp, state, argc,
+ &newstate) != GRUB_ERR_NONE)
{
- if (newstate == GRUB_PARSER_STATE_TEXT
- && state != GRUB_PARSER_STATE_ESC && grub_isspace (use))
- {
- /* Don't add more than one argument if multiple
- spaces are used. */
- if (bp != buffer && *(bp - 1))
- {
- *(bp++) = '\0';
- (*argc)++;
- }
- }
- else if (use)
- *(bp++) = use;
+ if (rd != cmdline)
+ grub_free (rd);
+ return grub_errno;
}
state = newstate;
}
--
2.23.0

View File

@ -0,0 +1,65 @@
From 3d157bbd06506b170fde5ec23980c4bf9f7660e2 Mon Sep 17 00:00:00 2001
From: Chris Coulson <chris.coulson@canonical.com>
Date: Thu, 7 Jan 2021 19:53:55 +0000
Subject: kern/parser: Introduce terminate_arg() helper
process_char() and grub_parser_split_cmdline() use similar code for
terminating the most recent argument. Add a helper function for this.
Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/kern/parser.c | 23 +++++++++++++----------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/grub-core/kern/parser.c b/grub-core/kern/parser.c
index 7e1ba99..45d0871 100644
--- a/grub-core/kern/parser.c
+++ b/grub-core/kern/parser.c
@@ -129,6 +129,16 @@ add_var (char *varname, char **bp, char **vp,
*((*bp)++) = *val;
}
+static void
+terminate_arg (char *buffer, char **bp, int *argc)
+{
+ if (*bp != buffer && *((*bp) - 1) != '\0')
+ {
+ *((*bp)++) = '\0';
+ (*argc)++;
+ }
+}
+
static grub_err_t
process_char (char c, char *buffer, char **bp, char *varname, char **vp,
grub_parser_state_t state, int *argc,
@@ -157,11 +167,7 @@ process_char (char c, char *buffer, char **bp, char *varname, char **vp,
* Don't add more than one argument if multiple
* spaces are used.
*/
- if (*bp != buffer && *((*bp) - 1) != '\0')
- {
- *((*bp)++) = '\0';
- (*argc)++;
- }
+ terminate_arg (buffer, bp, argc);
}
else if (use)
*((*bp)++) = use;
@@ -232,11 +238,8 @@ grub_parser_split_cmdline (const char *cmdline,
variable. */
add_var (varname, &bp, &vp, state, GRUB_PARSER_STATE_TEXT);
- if (bp != buffer && *(bp - 1))
- {
- *(bp++) = '\0';
- (*argc)++;
- }
+ /* Ensure that the last argument is terminated. */
+ terminate_arg (buffer, &bp, argc);
/* If there are no args, then we're done. */
if (!*argc)
--
2.23.0

View File

@ -0,0 +1,90 @@
From 8bc817014ce3d7a498db44eae33c8b90e2430926 Mon Sep 17 00:00:00 2001
From: Chris Coulson <chris.coulson@canonical.com>
Date: Wed, 6 Jan 2021 13:54:26 +0000
Subject: kern/parser: Refactor grub_parser_split_cmdline() cleanup
Introduce a common function epilogue used for cleaning up on all
return paths, which will simplify additional error handling to be
introduced in a subsequent commit.
Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/kern/parser.c | 35 ++++++++++++++++++++---------------
1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/grub-core/kern/parser.c b/grub-core/kern/parser.c
index 572c67089..e010eaa1f 100644
--- a/grub-core/kern/parser.c
+++ b/grub-core/kern/parser.c
@@ -221,19 +221,13 @@ grub_parser_split_cmdline (const char *cmdline,
if (process_char (*rp, buffer, &bp, varname, &vp, state, argc,
&newstate) != GRUB_ERR_NONE)
- {
- if (rd != cmdline)
- grub_free (rd);
- return grub_errno;
- }
+ goto fail;
+
state = newstate;
}
}
while (state != GRUB_PARSER_STATE_TEXT && !check_varstate (state));
- if (rd != cmdline)
- grub_free (rd);
-
/* A special case for when the last character was part of a
variable. */
add_var (varname, &bp, &vp, state, GRUB_PARSER_STATE_TEXT);
@@ -243,20 +237,20 @@ grub_parser_split_cmdline (const char *cmdline,
/* If there are no args, then we're done. */
if (!*argc)
- return 0;
+ {
+ grub_errno = GRUB_ERR_NONE;
+ goto out;
+ }
/* Reserve memory for the return values. */
args = grub_malloc (bp - buffer);
if (!args)
- return grub_errno;
+ goto fail;
grub_memcpy (args, buffer, bp - buffer);
*argv = grub_calloc (*argc + 1, sizeof (char *));
if (!*argv)
- {
- grub_free (args);
- return grub_errno;
- }
+ goto fail;
/* The arguments are separated with 0's, setup argv so it points to
the right values. */
@@ -269,7 +263,18 @@ grub_parser_split_cmdline (const char *cmdline,
bp++;
}
- return 0;
+ grub_errno = GRUB_ERR_NONE;
+
+ out:
+ if (rd != cmdline)
+ grub_free (rd);
+
+ return grub_errno;
+
+ fail:
+ grub_free (*argv);
+ grub_free (args);
+ goto out;
}
/* Helper for grub_parser_execute. */
--
cgit v1.2.1

View File

@ -0,0 +1,307 @@
From 030fb6c4fa354cdbd6a8d6903dfed5d36eaf3cb2 Mon Sep 17 00:00:00 2001
From: Chris Coulson <chris.coulson@canonical.com>
Date: Thu, 7 Jan 2021 15:15:43 +0000
Subject: kern/buffer: Add variable sized heap buffer
Add a new variable sized heap buffer type (grub_buffer_t) with simple
operations for appending data, accessing the data and maintaining
a read cursor.
Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/Makefile.core.def | 1 +
grub-core/kern/buffer.c | 117 +++++++++++++++++++++++++++++++++++
include/grub/buffer.h | 144 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 262 insertions(+)
create mode 100644 grub-core/kern/buffer.c
create mode 100644 include/grub/buffer.h
diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index b283c50..c7563bb 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -123,6 +123,7 @@ kernel = {
riscv32_efi_startup = kern/riscv/efi/startup.S;
riscv64_efi_startup = kern/riscv/efi/startup.S;
+ common = kern/buffer.c;
common = kern/command.c;
common = kern/corecmd.c;
common = kern/device.c;
diff --git a/grub-core/kern/buffer.c b/grub-core/kern/buffer.c
new file mode 100644
index 0000000..b50a187
--- /dev/null
+++ b/grub-core/kern/buffer.c
@@ -0,0 +1,117 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2021 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/buffer.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/safemath.h>
+#include <grub/types.h>
+
+grub_buffer_t
+grub_buffer_new (grub_size_t sz)
+{
+ struct grub_buffer *ret;
+
+ ret = (struct grub_buffer *) grub_malloc (sizeof (*ret));
+ if (ret == NULL)
+ return NULL;
+
+ ret->data = (grub_uint8_t *) grub_malloc (sz);
+ if (ret->data == NULL)
+ {
+ grub_free (ret);
+ return NULL;
+ }
+
+ ret->sz = sz;
+ ret->pos = 0;
+ ret->used = 0;
+
+ return ret;
+}
+
+void
+grub_buffer_free (grub_buffer_t buf)
+{
+ grub_free (buf->data);
+ grub_free (buf);
+}
+
+grub_err_t
+grub_buffer_ensure_space (grub_buffer_t buf, grub_size_t req)
+{
+ grub_uint8_t *d;
+ grub_size_t newsz = 1;
+
+ /* Is the current buffer size adequate? */
+ if (buf->sz >= req)
+ return GRUB_ERR_NONE;
+
+ /* Find the smallest power-of-2 size that satisfies the request. */
+ while (newsz < req)
+ {
+ if (newsz == 0)
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
+ N_("requested buffer size is too large"));
+ newsz <<= 1;
+ }
+
+ d = (grub_uint8_t *) grub_realloc (buf->data, newsz);
+ if (d == NULL)
+ return grub_errno;
+
+ buf->data = d;
+ buf->sz = newsz;
+
+ return GRUB_ERR_NONE;
+}
+
+void *
+grub_buffer_take_data (grub_buffer_t buf)
+{
+ void *data = buf->data;
+
+ buf->data = NULL;
+ buf->sz = buf->pos = buf->used = 0;
+
+ return data;
+}
+
+void
+grub_buffer_reset (grub_buffer_t buf)
+{
+ buf->pos = buf->used = 0;
+}
+
+grub_err_t
+grub_buffer_advance_read_pos (grub_buffer_t buf, grub_size_t n)
+{
+ grub_size_t newpos;
+
+ if (grub_add (buf->pos, n, &newpos))
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
+
+ if (newpos > buf->used)
+ return grub_error (GRUB_ERR_OUT_OF_RANGE,
+ N_("new read is position beyond the end of the written data"));
+
+ buf->pos = newpos;
+
+ return GRUB_ERR_NONE;
+}
diff --git a/include/grub/buffer.h b/include/grub/buffer.h
new file mode 100644
index 0000000..f4b10cf
--- /dev/null
+++ b/include/grub/buffer.h
@@ -0,0 +1,144 @@
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2021 Free Software Foundation, Inc.
+ *
+ * GRUB is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GRUB is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_BUFFER_H
+#define GRUB_BUFFER_H 1
+
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/safemath.h>
+#include <grub/types.h>
+
+struct grub_buffer
+{
+ grub_uint8_t *data;
+ grub_size_t sz;
+ grub_size_t pos;
+ grub_size_t used;
+};
+
+/*
+ * grub_buffer_t represents a simple variable sized byte buffer with
+ * read and write cursors. It currently only implements
+ * functionality required by the only user in GRUB (append byte[s],
+ * peeking data at a specified position and updating the read cursor.
+ * Some things that this doesn't do yet are:
+ * - Reading a portion of the buffer by copying data from the current
+ * read position in to a caller supplied destination buffer and then
+ * automatically updating the read cursor.
+ * - Dropping the read part at the start of the buffer when an append
+ * requires more space.
+ */
+typedef struct grub_buffer *grub_buffer_t;
+
+/* Allocate a new buffer with the specified initial size. */
+extern grub_buffer_t grub_buffer_new (grub_size_t sz);
+
+/* Free the buffer and its resources. */
+extern void grub_buffer_free (grub_buffer_t buf);
+
+/* Return the number of unread bytes in this buffer. */
+static inline grub_size_t
+grub_buffer_get_unread_bytes (grub_buffer_t buf)
+{
+ return buf->used - buf->pos;
+}
+
+/*
+ * Ensure that the buffer size is at least the requested
+ * number of bytes.
+ */
+extern grub_err_t grub_buffer_ensure_space (grub_buffer_t buf, grub_size_t req);
+
+/*
+ * Append the specified number of bytes from the supplied
+ * data to the buffer.
+ */
+static inline grub_err_t
+grub_buffer_append_data (grub_buffer_t buf, const void *data, grub_size_t len)
+{
+ grub_size_t req;
+
+ if (grub_add (buf->used, len, &req))
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
+
+ if (grub_buffer_ensure_space (buf, req) != GRUB_ERR_NONE)
+ return grub_errno;
+
+ grub_memcpy (&buf->data[buf->used], data, len);
+ buf->used = req;
+
+ return GRUB_ERR_NONE;
+}
+
+/* Append the supplied character to the buffer. */
+static inline grub_err_t
+grub_buffer_append_char (grub_buffer_t buf, char c)
+{
+ return grub_buffer_append_data (buf, &c, 1);
+}
+
+/*
+ * Forget and return the underlying data buffer. The caller
+ * becomes the owner of this buffer, and must free it when it
+ * is no longer required.
+ */
+extern void *grub_buffer_take_data (grub_buffer_t buf);
+
+/* Reset this buffer. Note that this does not deallocate any resources. */
+void grub_buffer_reset (grub_buffer_t buf);
+
+/*
+ * Return a pointer to the underlying data buffer at the specified
+ * offset from the current read position. Note that this pointer may
+ * become invalid if the buffer is mutated further.
+ */
+static inline void *
+grub_buffer_peek_data_at (grub_buffer_t buf, grub_size_t off)
+{
+ if (grub_add (buf->pos, off, &off))
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected."));
+ return NULL;
+ }
+
+ if (off >= buf->used)
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("peek out of range"));
+ return NULL;
+ }
+
+ return &buf->data[off];
+}
+
+/*
+ * Return a pointer to the underlying data buffer at the current
+ * read position. Note that this pointer may become invalid if the
+ * buffer is mutated further.
+ */
+static inline void *
+grub_buffer_peek_data (grub_buffer_t buf)
+{
+ return grub_buffer_peek_data_at (buf, 0);
+}
+
+/* Advance the read position by the specified number of bytes. */
+extern grub_err_t grub_buffer_advance_read_pos (grub_buffer_t buf, grub_size_t n);
+
+#endif /* GRUB_BUFFER_H */
--
2.23.0

View File

@ -0,0 +1,247 @@
From 4ea7bae51f97e49c84dc67ea30b466ca8633b9f6 Mon Sep 17 00:00:00 2001
From: Chris Coulson <chris.coulson@canonical.com>
Date: Thu, 7 Jan 2021 19:21:03 +0000
Subject: kern/parser: Fix a stack buffer overflow
grub_parser_split_cmdline() expands variable names present in the supplied
command line in to their corresponding variable contents and uses a 1 kiB
stack buffer for temporary storage without sufficient bounds checking. If
the function is called with a command line that references a variable with
a sufficiently large payload, it is possible to overflow the stack
buffer via tab completion, corrupt the stack frame and potentially
control execution.
Fixes: CVE-2020-27749
Reported-by: Chris Coulson <chris.coulson@canonical.com>
Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
Signed-off-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
grub-core/kern/parser.c | 110 ++++++++++++++++++++++++----------------
1 file changed, 67 insertions(+), 43 deletions(-)
diff --git a/grub-core/kern/parser.c b/grub-core/kern/parser.c
index d50538f..89276d5 100644
--- a/grub-core/kern/parser.c
+++ b/grub-core/kern/parser.c
@@ -18,6 +18,7 @@
*/
#include <grub/parser.h>
+#include <grub/buffer.h>
#include <grub/env.h>
#include <grub/misc.h>
#include <grub/mm.h>
@@ -107,8 +108,8 @@ check_varstate (grub_parser_state_t s)
}
-static void
-add_var (char *varname, char **bp, char **vp,
+static grub_err_t
+add_var (grub_buffer_t varname, grub_buffer_t buf,
grub_parser_state_t state, grub_parser_state_t newstate)
{
const char *val;
@@ -116,31 +117,41 @@ add_var (char *varname, char **bp, char **vp,
/* Check if a variable was being read in and the end of the name
was reached. */
if (!(check_varstate (state) && !check_varstate (newstate)))
- return;
+ return GRUB_ERR_NONE;
+
+ if (grub_buffer_append_char (varname, '\0') != GRUB_ERR_NONE)
+ return grub_errno;
- *((*vp)++) = '\0';
- val = grub_env_get (varname);
- *vp = varname;
+ val = grub_env_get ((const char *) grub_buffer_peek_data (varname));
+ grub_buffer_reset (varname);
if (!val)
- return;
+ return GRUB_ERR_NONE;
/* Insert the contents of the variable in the buffer. */
- for (; *val; val++)
- *((*bp)++) = *val;
+ return grub_buffer_append_data (buf, val, grub_strlen (val));
}
-static void
-terminate_arg (char *buffer, char **bp, int *argc)
+static grub_err_t
+terminate_arg (grub_buffer_t buffer, int *argc)
{
- if (*bp != buffer && *((*bp) - 1) != '\0')
- {
- *((*bp)++) = '\0';
- (*argc)++;
- }
+ grub_size_t unread = grub_buffer_get_unread_bytes (buffer);
+
+ if (unread == 0)
+ return GRUB_ERR_NONE;
+
+ if (*(const char *) grub_buffer_peek_data_at (buffer, unread - 1) == '\0')
+ return GRUB_ERR_NONE;
+
+ if (grub_buffer_append_char (buffer, '\0') != GRUB_ERR_NONE)
+ return grub_errno;
+
+ (*argc)++;
+
+ return GRUB_ERR_NONE;
}
static grub_err_t
-process_char (char c, char *buffer, char **bp, char *varname, char **vp,
+process_char (char c, grub_buffer_t buffer, grub_buffer_t varname,
grub_parser_state_t state, int *argc,
grub_parser_state_t *newstate)
{
@@ -153,12 +164,13 @@ process_char (char c, char *buffer, char **bp, char *varname, char **vp,
* not describe the variable anymore, write the variable to
* the buffer.
*/
- add_var (varname, bp, vp, state, *newstate);
+ if (add_var (varname, buffer, state, *newstate) != GRUB_ERR_NONE)
+ return grub_errno;
if (check_varstate (*newstate))
{
if (use)
- *((*vp)++) = use;
+ return grub_buffer_append_char (varname, use);
}
else if (*newstate == GRUB_PARSER_STATE_TEXT &&
state != GRUB_PARSER_STATE_ESC && grub_isspace (use))
@@ -167,10 +179,10 @@ process_char (char c, char *buffer, char **bp, char *varname, char **vp,
* Don't add more than one argument if multiple
* spaces are used.
*/
- terminate_arg (buffer, bp, argc);
+ return terminate_arg (buffer, argc);
}
else if (use)
- *((*bp)++) = use;
+ return grub_buffer_append_char (buffer, use);
return GRUB_ERR_NONE;
}
@@ -181,19 +193,22 @@ grub_parser_split_cmdline (const char *cmdline,
int *argc, char ***argv)
{
grub_parser_state_t state = GRUB_PARSER_STATE_TEXT;
- /* XXX: Fixed size buffer, perhaps this buffer should be dynamically
- allocated. */
- char buffer[1024];
- char *bp = buffer;
+ grub_buffer_t buffer, varname;
char *rd = (char *) cmdline;
- char varname[200];
char *rp = rd;
- char *vp = varname;
- char *args;
int i;
*argc = 0;
*argv = NULL;
+
+ buffer = grub_buffer_new (1024);
+ if (buffer == NULL)
+ return grub_errno;
+
+ varname = grub_buffer_new (200);
+ if (varname == NULL)
+ goto fail;
+
do
{
if (rp == NULL || *rp == '\0')
@@ -219,7 +234,7 @@ grub_parser_split_cmdline (const char *cmdline,
{
grub_parser_state_t newstate;
- if (process_char (*rp, buffer, &bp, varname, &vp, state, argc,
+ if (process_char (*rp, buffer, varname, state, argc,
&newstate) != GRUB_ERR_NONE)
goto fail;
@@ -230,10 +245,12 @@ grub_parser_split_cmdline (const char *cmdline,
/* A special case for when the last character was part of a
variable. */
- add_var (varname, &bp, &vp, state, GRUB_PARSER_STATE_TEXT);
+ if (add_var (varname, buffer, state, GRUB_PARSER_STATE_TEXT) != GRUB_ERR_NONE)
+ goto fail;
/* Ensure that the last argument is terminated. */
- terminate_arg (buffer, &bp, argc);
+ if (terminate_arg (buffer, argc) != GRUB_ERR_NONE)
+ goto fail;
/* If there are no args, then we're done. */
if (!*argc)
@@ -242,38 +259,45 @@ grub_parser_split_cmdline (const char *cmdline,
goto out;
}
- /* Reserve memory for the return values. */
- args = grub_malloc (bp - buffer);
- if (!args)
- goto fail;
- grub_memcpy (args, buffer, bp - buffer);
-
*argv = grub_calloc (*argc + 1, sizeof (char *));
if (!*argv)
goto fail;
/* The arguments are separated with 0's, setup argv so it points to
the right values. */
- bp = args;
for (i = 0; i < *argc; i++)
{
- (*argv)[i] = bp;
- while (*bp)
- bp++;
- bp++;
+ char *arg;
+
+ if (i > 0)
+ {
+ if (grub_buffer_advance_read_pos (buffer, 1) != GRUB_ERR_NONE)
+ goto fail;
+ }
+
+ arg = (char *) grub_buffer_peek_data (buffer);
+ if (arg == NULL ||
+ grub_buffer_advance_read_pos (buffer, grub_strlen (arg)) != GRUB_ERR_NONE)
+ goto fail;
+
+ (*argv)[i] = arg;
}
+ /* Keep memory for the return values. */
+ grub_buffer_take_data (buffer);
+
grub_errno = GRUB_ERR_NONE;
out:
if (rd != cmdline)
grub_free (rd);
+ grub_buffer_free (buffer);
+ grub_buffer_free (varname);
return grub_errno;
fail:
grub_free (*argv);
- grub_free (args);
goto out;
}
--
2.23.0

View File

@ -0,0 +1,83 @@
From 7630ec5397fe418276b360f9011934b8c034936c
From: Daniel Kiper <daniel.kiper@oracle.com>
Date: 2021-03-02 15:54:15 +0100
Subject: [PATCH] dl: Only allow unloading modules that are not dependencies
When a module is attempted to be removed its reference counter is always
decremented. This means that repeated rmmod invocations will cause the
module to be unloaded even if another module depends on it.
This may lead to a use-after-free scenario allowing an attacker to execute
arbitrary code and by-pass the UEFI Secure Boot protection.
While being there, add the extern keyword to some function declarations in
that header file.
Fixes: CVE-2020-25632
Reference:http://git.savannah.gnu.org/cgit/grub.git/commit/?id=7630ec5397fe418276b360f9011934b8c034936c
Reported-by: Chris Coulson <chris.coulson@canonical.com>
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Diffstat
-rw-r--r-- grub-core/commands/minicmd.c 7
-rw-r--r-- grub-core/kern/dl.c 9
-rw-r--r-- include/grub/dl.h 8
3 files changed, 19 insertions, 5 deletions
diff --git a/grub-core/commands/minicmd.c b/grub-core/commands/minicmd.c
index 6bbce3128..fa498931e 100644
--- a/grub-core/commands/minicmd.c
+++ b/grub-core/commands/minicmd.c
@@ -140,8 +140,11 @@ grub_mini_cmd_rmmod (struct grub_command *cmd __attribute__ ((unused)),
if (grub_dl_is_persistent (mod))
return grub_error (GRUB_ERR_BAD_ARGUMENT, "cannot unload persistent module");
- if (grub_dl_unref (mod) <= 0)
- grub_dl_unload (mod);
+ if (grub_dl_ref_count (mod) > 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "cannot unload referenced module");
+
+ grub_dl_unref (mod);
+ grub_dl_unload (mod);
return 0;
}
diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
index 48eb5e7b6..48f8a7907 100644
--- a/grub-core/kern/dl.c
+++ b/grub-core/kern/dl.c
@@ -549,6 +549,15 @@ grub_dl_unref (grub_dl_t mod)
return --mod->ref_count;
}
+int
+grub_dl_ref_count (grub_dl_t mod)
+{
+ if (mod == NULL)
+ return 0;
+
+ return mod->ref_count;
+}
+
static void
grub_dl_flush_cache (grub_dl_t mod)
{
diff --git a/include/grub/dl.h b/include/grub/dl.h
index f03c03561..b3753c9ca 100644
--- a/include/grub/dl.h
+++ b/include/grub/dl.h
@@ -203,9 +203,11 @@ grub_dl_t EXPORT_FUNC(grub_dl_load) (const char *name);
grub_dl_t grub_dl_load_core (void *addr, grub_size_t size);
grub_dl_t EXPORT_FUNC(grub_dl_load_core_noinit) (void *addr, grub_size_t size);
int EXPORT_FUNC(grub_dl_unload) (grub_dl_t mod);
-void grub_dl_unload_unneeded (void);
-int EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod);
-int EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod);
+extern void grub_dl_unload_unneeded (void);
+extern int EXPORT_FUNC(grub_dl_ref) (grub_dl_t mod);
+extern int EXPORT_FUNC(grub_dl_unref) (grub_dl_t mod);
+extern int EXPORT_FUNC(grub_dl_ref_count) (grub_dl_t mod);
+
extern grub_dl_t EXPORT_VAR(grub_dl_head);
#ifndef GRUB_UTIL

View File

@ -0,0 +1,110 @@
From 128c16a682034263eb519c89bc0934eeb6fa8cfa
From: Daniel Kiper <daniel.kiper@oracle.com>
Date: 2021-03-02 15:54:15 +0100
Subject: [PATCH] usb: Avoid possible out-of-bound accesses caused by malicious devices
The maximum number of configurations and interfaces are fixed but there is
no out-of-bound checking to prevent a malicious USB device to report large
values for these and cause accesses outside the arrays' memory.
Fixes: CVE-2020-25647
Reference:http://git.savannah.gnu.org/cgit/grub.git/commit/?id=128c16a682034263eb519c89bc0934eeb6fa8cfa
Reported-by: Joseph Tartaro <joseph.tartaro@ioactive.com>
Reported-by: Ilja Van Sprundel <ivansprundel@ioactive.com>
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Diffstat
-rw-r--r-- grub-core/bus/usb/usb.c 15
-rw-r--r-- include/grub/usb.h 10
2 files changed, 19 insertions, 6 deletions
diff --git a/grub-core/bus/usb/usb.c b/grub-core/bus/usb/usb.c
index 8da5e4c74..7cb3cc230 100644
--- a/grub-core/bus/usb/usb.c
+++ b/grub-core/bus/usb/usb.c
@@ -75,6 +75,9 @@ grub_usb_controller_iterate (grub_usb_controller_iterate_hook_t hook,
grub_usb_err_t
grub_usb_clear_halt (grub_usb_device_t dev, int endpoint)
{
+ if (endpoint >= GRUB_USB_MAX_TOGGLE)
+ return GRUB_USB_ERR_BADDEVICE;
+
dev->toggle[endpoint] = 0;
return grub_usb_control_msg (dev, (GRUB_USB_REQTYPE_OUT
| GRUB_USB_REQTYPE_STANDARD
@@ -134,10 +137,10 @@ grub_usb_device_initialize (grub_usb_device_t dev)
return err;
descdev = &dev->descdev;
- for (i = 0; i < 8; i++)
+ for (i = 0; i < GRUB_USB_MAX_CONF; i++)
dev->config[i].descconf = NULL;
- if (descdev->configcnt == 0)
+ if (descdev->configcnt == 0 || descdev->configcnt > GRUB_USB_MAX_CONF)
{
err = GRUB_USB_ERR_BADDEVICE;
goto fail;
@@ -172,6 +175,12 @@ grub_usb_device_initialize (grub_usb_device_t dev)
/* Skip the configuration descriptor. */
pos = dev->config[i].descconf->length;
+ if (dev->config[i].descconf->numif > GRUB_USB_MAX_IF)
+ {
+ err = GRUB_USB_ERR_BADDEVICE;
+ goto fail;
+ }
+
/* Read all interfaces. */
for (currif = 0; currif < dev->config[i].descconf->numif; currif++)
{
@@ -217,7 +226,7 @@ grub_usb_device_initialize (grub_usb_device_t dev)
fail:
- for (i = 0; i < 8; i++)
+ for (i = 0; i < GRUB_USB_MAX_CONF; i++)
grub_free (dev->config[i].descconf);
return err;
diff --git a/include/grub/usb.h b/include/grub/usb.h
index 512ae1dd0..6475c552f 100644
--- a/include/grub/usb.h
+++ b/include/grub/usb.h
@@ -23,6 +23,10 @@
#include <grub/usbdesc.h>
#include <grub/usbtrans.h>
+#define GRUB_USB_MAX_CONF 8
+#define GRUB_USB_MAX_IF 32
+#define GRUB_USB_MAX_TOGGLE 256
+
typedef struct grub_usb_device *grub_usb_device_t;
typedef struct grub_usb_controller *grub_usb_controller_t;
typedef struct grub_usb_controller_dev *grub_usb_controller_dev_t;
@@ -167,7 +171,7 @@ struct grub_usb_configuration
struct grub_usb_desc_config *descconf;
/* Interfaces associated to this configuration. */
- struct grub_usb_interface interf[32];
+ struct grub_usb_interface interf[GRUB_USB_MAX_IF];
};
struct grub_usb_hub_port
@@ -191,7 +195,7 @@ struct grub_usb_device
struct grub_usb_controller controller;
/* Device configurations (after opening the device). */
- struct grub_usb_configuration config[8];
+ struct grub_usb_configuration config[GRUB_USB_MAX_CONF];
/* Device address. */
int addr;
@@ -203,7 +207,7 @@ struct grub_usb_device
int initialized;
/* Data toggle values (used for bulk transfers only). */
- int toggle[256];
+ int toggle[GRUB_USB_MAX_TOGGLE];
/* Used by libusb wrapper. Schedulded for removal. */
void *data;

View File

@ -0,0 +1,51 @@
From 2a330dba93ff11bc00eda76e9419bc52b0c7ead6
From: Daniel Kiper <daniel.kiper@oracle.com>
Date: 2021-03-02 15:54:15 +0100
Subject: [PATCH] lib/arg: Block repeated short options that require an argument
Fuzzing found the following crash:
search -hhhhhhhhhhhhhf
We didn't allocate enough option space for 13 hints because the
allocation code counts the number of discrete arguments (i.e. argc).
However, the shortopt parsing code will happily keep processing
a combination of short options without checking if those short
options require an argument. This means you can easily end writing
past the allocated option space.
This fixes a OOB write which can cause heap corruption.
Fixes: CVE-2021-20225
Reference:http://git.savannah.gnu.org/cgit/grub.git/commit/?id=2a330dba93ff11bc00eda76e9419bc52b0c7ead6
Reported-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Diffstat
-rw-r--r-- grub-core/lib/arg.c 13
1 files changed, 13 insertions, 0 deletions
diff --git a/grub-core/lib/arg.c b/grub-core/lib/arg.c
index 8439a0062..ade82d5dc 100644
--- a/grub-core/lib/arg.c
+++ b/grub-core/lib/arg.c
@@ -299,6 +299,19 @@ grub_arg_parse (grub_extcmd_t cmd, int argc, char **argv,
it can have an argument value. */
if (*curshort)
{
+ /*
+ * Only permit further short opts if this one doesn't
+ * require a value.
+ */
+ if (opt->type != ARG_TYPE_NONE &&
+ !(opt->flags & GRUB_ARG_OPTION_OPTIONAL))
+ {
+ grub_error (GRUB_ERR_BAD_ARGUMENT,
+ N_("missing mandatory option for `%s'"),
+ opt->longarg);
+ goto fail;
+ }
+
if (parse_option (cmd, opt, 0, usr) || grub_errno)
goto fail;
}

View File

@ -0,0 +1,44 @@
From 2f533a89a8dfcacbf2c9dbc77d910f111f24bf33
From: Daniel Kiper <daniel.kiper@oracle.com>
Date: 2021-03-02 15:54:15 +0100
Subject: [PATCH] commands/menuentry: Fix quoting in setparams_prefix()
Commit 9acdcbf32542 (use single quotes in menuentry setparams command)
says that expressing a quoted single quote will require 3 characters. It
actually requires (and always did require!) 4 characters:
str: a'b => a'\''b
len: 3 => 6 (2 for the letters + 4 for the quote)
This leads to not allocating enough memory and thus out of bounds writes
that have been observed to cause heap corruption.
Allocate 4 bytes for each single quote.
Commit 22e7dbb2bb81 (Fix quoting in legacy parser.) does the same
quoting, but it adds 3 as extra overhead on top of the single byte that
the quote already needs. So it's correct.
Fixes: 9acdcbf32542 (use single quotes in menuentry setparams command)
Fixes: CVE-2021-20233
Reference:http://git.savannah.gnu.org/cgit/grub.git/commit/?id=2f533a89a8dfcacbf2c9dbc77d910f111f24bf33
Reported-by: Daniel Axtens <dja@axtens.net>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Diffstat
-rw-r--r-- grub-core/commands/menuentry.c 2
1 files changed, 1 insertions, 1 deletions
diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c
index 9164df744..720e6d8ea 100644
--- a/grub-core/commands/menuentry.c
+++ b/grub-core/commands/menuentry.c
@@ -230,7 +230,7 @@ setparams_prefix (int argc, char **args)
len += 3; /* 3 = 1 space + 2 quotes */
p = args[i];
while (*p)
- len += (*p++ == '\'' ? 3 : 1);
+ len += (*p++ == '\'' ? 4 : 1);
}
result = grub_malloc (len + 2);

View File

@ -248,3 +248,14 @@ Patch0247: 0247-linux-Fix-integer-overflows-in-initrd-size-handling.patch
Patch0248: 0248-linuxefi-fail-kernel-validation-without-shim-protoco.patch Patch0248: 0248-linuxefi-fail-kernel-validation-without-shim-protoco.patch
Patch0249: 0249-remove-08_fallback_counting.in-apply-grubby.patch Patch0249: 0249-remove-08_fallback_counting.in-apply-grubby.patch
Patch0250: 0250-tftp-roll-over-block-counter-to-prevent-timeouts-wit.patch Patch0250: 0250-tftp-roll-over-block-counter-to-prevent-timeouts-wit.patch
Patch0251: backport-CVE-2020-25632.patch
Patch0252: backport-CVE-2020-25647.patch
Patch0253: backport-0001-CVE-2020-27749.patch
Patch0254: backport-0002-CVE-2020-27749.patch
Patch0255: backport-0003-CVE-2020-27749.patch
Patch0256: backport-0004-CVE-2020-27749.patch
Patch0257: backport-0005-CVE-2020-27749.patch
Patch0258: backport-0006-CVE-2020-27749.patch
Patch0259: backport-0007-CVE-2020-27749.patch
Patch0260: backport-CVE-2021-20225.patch
Patch0261: backport-CVE-2021-20233.patch

View File

@ -7,7 +7,7 @@
Name: grub2 Name: grub2
Epoch: 1 Epoch: 1
Version: 2.04 Version: 2.04
Release: 9 Release: 10
Summary: Bootloader with support for Linux, Multiboot and more Summary: Bootloader with support for Linux, Multiboot and more
License: GPLv3+ License: GPLv3+
URL: http://www.gnu.org/software/grub/ URL: http://www.gnu.org/software/grub/
@ -448,6 +448,12 @@ rm -r /boot/grub2.tmp/ || :
%{_datadir}/man/man* %{_datadir}/man/man*
%changelog %changelog
* Fri Mar 12 2021 yanglu <yanglu60@huawei.com> - 2.04-10
- Type:cves
- Id:CVE-2020-25632 CVE-2020-25647 CVE-2020-27749 CVE-2021-20225 CVE-2021-20233
- SUG:NA
- DESC:fix CVE-2020-25632 CVE-2020-25647 CVE-2020-27749 CVE-2021-20225 CVE-2021-20233
* Sat Feb 27 2021 fengtao <fengtao40@huawei.com> - 2.04-9 * Sat Feb 27 2021 fengtao <fengtao40@huawei.com> - 2.04-9
- Type:bugfix - Type:bugfix
- ID:NA - ID:NA