* [dunfell][PATCH] grub2: fix CVE-2020-27749 Stack buffer overflow
@ 2023-07-06 6:20 Hitendra Prajapati
2023-07-06 15:26 ` [OE-core] " Steve Sakoman
0 siblings, 1 reply; 3+ messages in thread
From: Hitendra Prajapati @ 2023-07-06 6:20 UTC (permalink / raw)
To: openembedded-core; +Cc: Hitendra Prajapati
Upstream-Status: Backport [https://launchpad.net/debian/+source/grub2/2.02+dfsg1-20+deb10u4/]
Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
---
.../grub/files/CVE-2020-27749.patch | 609 ++++++++++++++++++
meta/recipes-bsp/grub/grub2.inc | 1 +
2 files changed, 610 insertions(+)
create mode 100644 meta/recipes-bsp/grub/files/CVE-2020-27749.patch
diff --git a/meta/recipes-bsp/grub/files/CVE-2020-27749.patch b/meta/recipes-bsp/grub/files/CVE-2020-27749.patch
new file mode 100644
index 0000000000..30ba11d882
--- /dev/null
+++ b/meta/recipes-bsp/grub/files/CVE-2020-27749.patch
@@ -0,0 +1,609 @@
+From 199580a4a986848f5e27586a741a7e35412cc5c6 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>
+
+Upstream-Status: Backport [https://launchpad.net/debian/+source/grub2/2.02+dfsg1-20+deb10u4/]
+CVE: CVE-2020-27749
+
+Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
+---
+ grub-core/Makefile.core.def | 1 +
+ grub-core/kern/buffer.c | 117 +++++++++++++++++++++
+ grub-core/kern/parser.c | 204 +++++++++++++++++++++++-------------
+ include/grub/buffer.h | 144 +++++++++++++++++++++++++
+ 4 files changed, 395 insertions(+), 71 deletions(-)
+ 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 651ea2a..823cd57 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..9f5f8b8
+--- /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/grub-core/kern/parser.c b/grub-core/kern/parser.c
+index d1cf061..6ab7aa4 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
+@@ -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,17 +117,74 @@ 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 grub_err_t
++terminate_arg (grub_buffer_t buffer, int *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, grub_buffer_t buffer, grub_buffer_t varname,
++ 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.
++ */
++ if (add_var (varname, buffer, state, *newstate) != GRUB_ERR_NONE)
++ return grub_errno;
++
++ if (check_varstate (*newstate))
++ {
++ if (use)
++ return grub_buffer_append_char (varname, 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.
++ */
++ return terminate_arg (buffer, argc);
++ }
++ else if (use)
++ return grub_buffer_append_char (buffer, use);
++
++ return GRUB_ERR_NONE;
+ }
+
+ grub_err_t
+@@ -135,24 +193,36 @@ 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 *vp = varname;
+- char *args;
++ char *rp = rd;
+ 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 (!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,39 +230,14 @@ 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);
++ if (process_char (*rp, buffer, varname, state, argc,
++ &newstate) != GRUB_ERR_NONE)
++ goto fail;
+
+- /* 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))
+- {
+- *(bp++) = '\0';
+- (*argc)++;
+- }
+- }
+- else if (use)
+- *(bp++) = use;
+- }
+ state = newstate;
+ }
+ }
+@@ -200,43 +245,60 @@ 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;
+
+- if (bp != buffer && *(bp - 1))
+- {
+- *(bp++) = '\0';
+- (*argc)++;
+- }
++ /* Ensure that the last argument is terminated. */
++ if (terminate_arg (buffer, argc) != GRUB_ERR_NONE)
++ goto fail;
+
+ /* 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)
+- return grub_errno;
+- grub_memcpy (args, buffer, bp - buffer);
++ {
++ grub_errno = GRUB_ERR_NONE;
++ goto out;
++ }
+
+ *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. */
+- 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;
+ }
+
+- return 0;
++ /* 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);
++ goto out;
+ }
+
+ /* Helper for grub_parser_execute. */
+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.25.1
+
diff --git a/meta/recipes-bsp/grub/grub2.inc b/meta/recipes-bsp/grub/grub2.inc
index d09eecd8ac..cb79f2212b 100644
--- a/meta/recipes-bsp/grub/grub2.inc
+++ b/meta/recipes-bsp/grub/grub2.inc
@@ -106,6 +106,7 @@ SRC_URI = "${GNU_MIRROR}/grub/grub-${PV}.tar.gz \
file://font-Fix-size-overflow-in-grub_font_get_glyph_intern.patch \
file://CVE-2022-2601.patch \
file://CVE-2022-3775.patch \
+ file://CVE-2020-27749.patch \
"
SRC_URI[md5sum] = "5ce674ca6b2612d8939b9e6abed32934"
SRC_URI[sha256sum] = "f10c85ae3e204dbaec39ae22fa3c5e99f0665417e91c2cb49b7e5031658ba6ea"
--
2.25.1
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [OE-core] [dunfell][PATCH] grub2: fix CVE-2020-27749 Stack buffer overflow
2023-07-06 6:20 [dunfell][PATCH] grub2: fix CVE-2020-27749 Stack buffer overflow Hitendra Prajapati
@ 2023-07-06 15:26 ` Steve Sakoman
2023-07-07 4:53 ` Hitendra Prajapati
0 siblings, 1 reply; 3+ messages in thread
From: Steve Sakoman @ 2023-07-06 15:26 UTC (permalink / raw)
To: Hitendra Prajapati; +Cc: openembedded-core
On Wed, Jul 5, 2023 at 8:20 PM Hitendra Prajapati <hprajapati@mvista.com> wrote:
>
> Upstream-Status: Backport [https://launchpad.net/debian/+source/grub2/2.02+dfsg1-20+deb10u4/]
Hi Hitendra,
launchpad.net isn't the upstream for grub2, so please also reference
the actual upstream commits in a v2. It would be helpful to me if you
could send v2 as a patch series so I know the proper order to apply
the patches!
Thanks for helping with CVE fixes.
Steve
>
> Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
> ---
> .../grub/files/CVE-2020-27749.patch | 609 ++++++++++++++++++
> meta/recipes-bsp/grub/grub2.inc | 1 +
> 2 files changed, 610 insertions(+)
> create mode 100644 meta/recipes-bsp/grub/files/CVE-2020-27749.patch
>
> diff --git a/meta/recipes-bsp/grub/files/CVE-2020-27749.patch b/meta/recipes-bsp/grub/files/CVE-2020-27749.patch
> new file mode 100644
> index 0000000000..30ba11d882
> --- /dev/null
> +++ b/meta/recipes-bsp/grub/files/CVE-2020-27749.patch
> @@ -0,0 +1,609 @@
> +From 199580a4a986848f5e27586a741a7e35412cc5c6 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>
> +
> +Upstream-Status: Backport [https://launchpad.net/debian/+source/grub2/2.02+dfsg1-20+deb10u4/]
> +CVE: CVE-2020-27749
> +
> +Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
> +---
> + grub-core/Makefile.core.def | 1 +
> + grub-core/kern/buffer.c | 117 +++++++++++++++++++++
> + grub-core/kern/parser.c | 204 +++++++++++++++++++++++-------------
> + include/grub/buffer.h | 144 +++++++++++++++++++++++++
> + 4 files changed, 395 insertions(+), 71 deletions(-)
> + 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 651ea2a..823cd57 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..9f5f8b8
> +--- /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/grub-core/kern/parser.c b/grub-core/kern/parser.c
> +index d1cf061..6ab7aa4 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
> +@@ -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,17 +117,74 @@ 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 grub_err_t
> ++terminate_arg (grub_buffer_t buffer, int *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, grub_buffer_t buffer, grub_buffer_t varname,
> ++ 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.
> ++ */
> ++ if (add_var (varname, buffer, state, *newstate) != GRUB_ERR_NONE)
> ++ return grub_errno;
> ++
> ++ if (check_varstate (*newstate))
> ++ {
> ++ if (use)
> ++ return grub_buffer_append_char (varname, 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.
> ++ */
> ++ return terminate_arg (buffer, argc);
> ++ }
> ++ else if (use)
> ++ return grub_buffer_append_char (buffer, use);
> ++
> ++ return GRUB_ERR_NONE;
> + }
> +
> + grub_err_t
> +@@ -135,24 +193,36 @@ 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 *vp = varname;
> +- char *args;
> ++ char *rp = rd;
> + 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 (!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,39 +230,14 @@ 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);
> ++ if (process_char (*rp, buffer, varname, state, argc,
> ++ &newstate) != GRUB_ERR_NONE)
> ++ goto fail;
> +
> +- /* 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))
> +- {
> +- *(bp++) = '\0';
> +- (*argc)++;
> +- }
> +- }
> +- else if (use)
> +- *(bp++) = use;
> +- }
> + state = newstate;
> + }
> + }
> +@@ -200,43 +245,60 @@ 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;
> +
> +- if (bp != buffer && *(bp - 1))
> +- {
> +- *(bp++) = '\0';
> +- (*argc)++;
> +- }
> ++ /* Ensure that the last argument is terminated. */
> ++ if (terminate_arg (buffer, argc) != GRUB_ERR_NONE)
> ++ goto fail;
> +
> + /* 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)
> +- return grub_errno;
> +- grub_memcpy (args, buffer, bp - buffer);
> ++ {
> ++ grub_errno = GRUB_ERR_NONE;
> ++ goto out;
> ++ }
> +
> + *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. */
> +- 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;
> + }
> +
> +- return 0;
> ++ /* 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);
> ++ goto out;
> + }
> +
> + /* Helper for grub_parser_execute. */
> +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.25.1
> +
> diff --git a/meta/recipes-bsp/grub/grub2.inc b/meta/recipes-bsp/grub/grub2.inc
> index d09eecd8ac..cb79f2212b 100644
> --- a/meta/recipes-bsp/grub/grub2.inc
> +++ b/meta/recipes-bsp/grub/grub2.inc
> @@ -106,6 +106,7 @@ SRC_URI = "${GNU_MIRROR}/grub/grub-${PV}.tar.gz \
> file://font-Fix-size-overflow-in-grub_font_get_glyph_intern.patch \
> file://CVE-2022-2601.patch \
> file://CVE-2022-3775.patch \
> + file://CVE-2020-27749.patch \
> "
> SRC_URI[md5sum] = "5ce674ca6b2612d8939b9e6abed32934"
> SRC_URI[sha256sum] = "f10c85ae3e204dbaec39ae22fa3c5e99f0665417e91c2cb49b7e5031658ba6ea"
> --
> 2.25.1
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#183913): https://lists.openembedded.org/g/openembedded-core/message/183913
> Mute This Topic: https://lists.openembedded.org/mt/99980756/3620601
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [steve@sakoman.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [dunfell][PATCH] grub2: fix CVE-2020-27749 Stack buffer overflow
2023-07-06 15:26 ` [OE-core] " Steve Sakoman
@ 2023-07-07 4:53 ` Hitendra Prajapati
0 siblings, 0 replies; 3+ messages in thread
From: Hitendra Prajapati @ 2023-07-07 4:53 UTC (permalink / raw)
To: openembedded-core
[-- Attachment #1: Type: text/plain, Size: 124 bytes --]
Hi Steve,
I have added v2 : *https://lists.openembedded.org/g/openembedded-core/message/183996*
Thank you.
Hitendra
[-- Attachment #2: Type: text/html, Size: 159 bytes --]
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2023-07-07 4:53 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-07-06 6:20 [dunfell][PATCH] grub2: fix CVE-2020-27749 Stack buffer overflow Hitendra Prajapati
2023-07-06 15:26 ` [OE-core] " Steve Sakoman
2023-07-07 4:53 ` Hitendra Prajapati
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.