All of lore.kernel.org
 help / color / mirror / Atom feed
* [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round
@ 2022-06-07 17:00 Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 01/30] loader/efi/chainloader: Simplify the loader state Daniel Kiper
                   ` (29 more replies)
  0 siblings, 30 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alec.r.brown, alexander.burmashev, andrew.cooper3,
	chris.coulson, curiousj, darren.kenny, dja, eric.snowberg,
	ilya.okomin, jag.raman, jan.setjeeilers, jaredz, john.haxby,
	julian.klode, kanth.ghatraju, konrad.wilk, lidong.chen, mbenatto,
	mchang, meissner, mlewando, narayav1, phcoder, pjanda, pjones,
	rharwood, robert.truxal, ross.philipson, sudhakar, tamas.lengyel,
	tcullum

Hi all,

This patch set contains a bundle of fixes for various security flaws discovered
in the GRUB2 during last year. The most severe ones, i.e. potentially exploitable,
have CVEs assigned and are listed at the end of this email. Additionally, the list
of CVEs contains a CVE assigned for the shim vulnerability. It has been added
for completeness.

Details of exactly what needs updating will be provided by the respective
distros and vendors when updates become available. Here [1] we are listing at
least some links to the messaging known at the time of this posting.

Full mitigation against all CVEs will require updated shim with latest SBAT
(Secure Boot Advanced Targeting) [2] data provided by distros and vendors.
This time UEFI revocation list (dbx) will not be used and revocation of broken
artifacts will be done with SBAT only. For information on how to apply the
latest SBAT revocations, please see mokutil(1). Vendor shims may explicitly
permit known older boot artifacts to boot.

Updated GRUB2, shim and other boot artifacts from all the affected vendors will
be made available when the embargo lifts or some time thereafter.

I am posting all the GRUB2 upstream patches which fix all security bugs found
and reported up until now. Major Linux distros carry or will carry soon one
form or another of these patches. Now all the GRUB2 upstream patches are in
the GRUB2 git repository [3] too.

I would like to thank, in alphabetical order, the following people who were working
really hard on the GRUB, shim and other things related to these issues:
  - Alec Brown (Oracle),
  - Alexander Burmashev (Oracle),
  - Andrew Cooper (Citrix),
  - Chris Coulson (Canonical),
  - D. Jared Dominguez (Red Hat),
  - Daniel Axtens,
  - Darren Kenny (Oracle),
  - Eric Snowberg (Oracle),
  - Ilya Okomin (Oracle),
  - Jagannathan Raman (Oracle),
  - Jan Setje-Eilers (Oracle),
  - Jeremiah Cox,
  - John Haxby (Oracle),
  - Julian Andres Klode (Canonical),
  - Lidong Chen (Oracle),
  - Marco A Benatto (Red Hat),
  - Marcus Meissner (SUSE),
  - Marta Lewandowska (Red Hat),
  - Michael Chang (SUSE),
  - Peter Jones (Red Hat),
  - Petr Janda (Red Hat),
  - Robbie Harwood (Red Hat),
  - Robert Truxal (Microsoft),
  - Ross Philipson (Oracle),
  - Steve McIntyre (Debian),
  - Sudhakar Kuppusamy (IBM),
  - Tamas K Lengyel (Intel),
  - Todd Cullum (Red Hat),
  - Vikram Narayanan (University of California Irvine).

We would not be able to succeed without all your hard work.

It was very big pleasure to work with you all.

Thank you!

Daniel

[1] Red Hat: https://access.redhat.com/security/security-updates/#/
    SUSE:    https://www.suse.com/support/kb/doc/?id=000020668

[2] https://github.com/rhboot/shim/blob/main/SBAT.md

[3] https://git.savannah.gnu.org/gitweb/?p=grub.git
    https://git.savannah.gnu.org/git/grub.git

*******************************************************************************

CVE-2021-3695 grub2: Crafted PNG grayscale images may lead to out-of-bounds write in heap
7.5/CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H

A crafted 16-bit grayscale PNG image may lead to a out-of-bounds write in the
heap area. An attacker may take advantage of that to cause heap data corruption
or eventually arbitrary code execution and circumvent secure boot protections.
This issue has a high complexity to be exploited as an attacker needs to
perform some triage over the heap layout to achieve significant results, also
the values written into the memory are repeated three times in a row making
difficult to produce valid payloads.

Reported-by: Daniel Axtens

*******************************************************************************

CVE-2021-3696 grub2: Crafted PNG image may lead to out-of-bound write during huffman table handling
5/CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:C/C:L/I:L/A:L

A heap out-of-bounds write may happen during the handling of Huffman tables in
the PNG reader. This may lead to data corruption in the heap space.
Confidentiality, Integrity and Availability impact may be considered Low as it's
very complex to an attacker control the encoding and positioning of corrupted
Huffman entries to achieve results such as arbitrary code execution and/or
secure boot circumvention.

Reported-by: Daniel Axtens

*******************************************************************************

CVE-2021-3697 grub2: Crafted JPEG image can lead to buffer underflow write in the heap
7.5/CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H

A crafted JPEG image may lead the JPEG reader to underflow its data pointer,
allowing user controlled data to be written in heap. To be successfully
performed the attacker needs to do some triage over the heap layout and craft
an image with a malicious format and payload. This vulnerability can lead to
data corruption and eventual code execution or secure boot circumvention.

Reported-by: Daniel Axtens

*******************************************************************************

CVE-2022-28733 grub2: Integer underflow in grub_net_recv_ip4_packets
8.1/CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H

A malicious crafted IP packet can lead to an integer underflow in
grub_net_recv_ip4_packets() function on rsm->total_len value. Under certain
circumstances the total_len value may end up wrapping around to a small integer
number which will be used in memory allocation. If the attack succeeds in such
way, subsequent operations can write past the end of the buffer.

Reported-by: Daniel Axtens

*******************************************************************************

CVE-2022-28734 grub2: Out-of-bounds write when handling split HTTP headers
7/CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:H

When handling split HTTP headers, GRUB2 HTTP code accidentally moves its
internal data buffer point by one position. This can lead to a out-of-bound
write further when parsing the HTTP request, writing a NULL byte past the
buffer. It's conceivable that an attacker controlled set of packets can lead
to corruption of the GRUB2's internal memory metadata.

Reported-by: Daniel Axtens

*******************************************************************************

CVE-2022-28735 grub2: shim_lock verifier allows non-kernel files to be loaded
6.7/CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H

The GRUB2's shim_lock verifier allows non-kernel files to be loaded on shim-powered
secure boot systems. Allowing such files to be loaded may lead to unverified
code and modules to be loaded in GRUB2 breaking the secure boot trust-chain.

Reported-by: Julian Andres Klode

*******************************************************************************

CVE-2022-28736 grub2: use-after-free in grub_cmd_chainloader()
6.4/CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H

There's a use-after-free vulnerability in grub_cmd_chainloader() function. The
chainloader command is used to boot up operating systems that doesn't support
multiboot and do not have direct support from GRUB2. When executing chainloader
more than once a use-after-free vulnerability is triggered. If an attacker can
control the GRUB2's memory allocation pattern sensitive data may be exposed and
arbitrary code execution can be achieved.

Reported-by: Chris Coulson

*******************************************************************************

CVE-2022-28737: shim: Buffer overflow when loading crafted EFI images
6.5/CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:H

There's a possible overflow in handle_image() when shim tries to load and execute
crafted EFI executables. The handle_image() function takes into account the SizeOfRawData
field from each section to be loaded. An attacker can leverage this to perform
out-of-bound writes into memory. Arbitrary code execution is not discarded in
such scenario.

Reported-by: Chris Coulson

*******************************************************************************

 grub-core/commands/boot.c          |  66 +++++++++++++++++++++++++++++++++++++++++++++++-------
 grub-core/fs/btrfs.c               | 105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 grub-core/fs/f2fs.c                |  58 ++++++++++++++++++++++++++++++++++++-----------
 grub-core/kern/efi/sb.c            |  39 +++++++++++++++++++++++++++++---
 grub-core/kern/file.c              |   2 ++
 grub-core/loader/efi/chainloader.c |  46 ++++++++++++++++++++------------------
 grub-core/net/dns.c                |  25 ++++++++++++++++-----
 grub-core/net/http.c               |  17 +++++++++-----
 grub-core/net/ip.c                 |  10 ++++++++-
 grub-core/net/net.c                |  11 +++++++--
 grub-core/net/netbuff.c            |  13 +++++++++++
 grub-core/net/tftp.c               |   3 ++-
 grub-core/normal/charset.c         |   2 ++
 grub-core/video/readers/jpeg.c     | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
 grub-core/video/readers/png.c      | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------
 include/grub/loader.h              |   5 +++++
 include/grub/net.h                 |   1 +
 include/grub/verify.h              |   1 +
 18 files changed, 501 insertions(+), 167 deletions(-)

Chris Coulson (3):
      loader/efi/chainloader: Simplify the loader state
      commands/boot: Add API to pass context to loader
      loader/efi/chainloader: Use grub_loader_set_ex()

Daniel Axtens (20):
      kern/file: Do not leak device_name on error in grub_file_open()
      video/readers/png: Abort sooner if a read operation fails
      video/readers/png: Refuse to handle multiple image headers
      video/readers/png: Drop greyscale support to fix heap out-of-bounds write
      video/readers/png: Avoid heap OOB R/W inserting huff table items
      video/readers/png: Sanity check some huffman codes
      video/readers/jpeg: Abort sooner if a read operation fails
      video/readers/jpeg: Do not reallocate a given huff table
      video/readers/jpeg: Refuse to handle multiple start of streams
      video/readers/jpeg: Block int underflow -> wild pointer write
      normal/charset: Fix array out-of-bounds formatting unicode for display
      net/ip: Do IP fragment maths safely
      net/netbuff: Block overly large netbuff allocs
      net/dns: Fix double-free addresses on corrupt DNS response
      net/dns: Don't read past the end of the string we're checking against
      net/tftp: Prevent a UAF and double-free from a failed seek
      net/tftp: Avoid a trivial UAF
      net/http: Do not tear down socket if it's already been torn down
      net/http: Fix OOB write for split http headers
      net/http: Error out on headers with LF without CR

Darren Kenny (3):
      fs/btrfs: Fix several fuzz issues with invalid dir item sizing
      fs/btrfs: Fix more ASAN and SEGV issues found with fuzzing
      fs/btrfs: Fix more fuzz issues related to chunks

Julian Andres Klode (1):
      kern/efi/sb: Reject non-kernel files in the shim_lock verifier

Sudhakar Kuppusamy (3):
      fs/f2fs: Do not read past the end of nat journal entries
      fs/f2fs: Do not read past the end of nat bitmap
      fs/f2fs: Do not copy file names that are too long


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 01/30] loader/efi/chainloader: Simplify the loader state
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 02/30] commands/boot: Add API to pass context to loader Daniel Kiper
                   ` (28 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Chris Coulson <chris.coulson@canonical.com>

The chainloader command retains the source buffer and device path passed
to LoadImage(), requiring the unload hook passed to grub_loader_set() to
free them. It isn't required to retain this state though - they aren't
required by StartImage() or anything else in the boot hook, so clean them
up before grub_cmd_chainloader() finishes.

Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/loader/efi/chainloader.c | 38 +++++++++++++++++++++-----------------
 1 file changed, 21 insertions(+), 17 deletions(-)

diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
index 2bd80f4db..d1602c89b 100644
--- a/grub-core/loader/efi/chainloader.c
+++ b/grub-core/loader/efi/chainloader.c
@@ -44,25 +44,20 @@ GRUB_MOD_LICENSE ("GPLv3+");
 
 static grub_dl_t my_mod;
 
-static grub_efi_physical_address_t address;
-static grub_efi_uintn_t pages;
-static grub_efi_device_path_t *file_path;
 static grub_efi_handle_t image_handle;
-static grub_efi_char16_t *cmdline;
 
 static grub_err_t
 grub_chainloader_unload (void)
 {
+  grub_efi_loaded_image_t *loaded_image;
   grub_efi_boot_services_t *b;
 
+  loaded_image = grub_efi_get_loaded_image (image_handle);
+  if (loaded_image != NULL)
+    grub_free (loaded_image->load_options);
+
   b = grub_efi_system_table->boot_services;
   efi_call_1 (b->unload_image, image_handle);
-  efi_call_2 (b->free_pages, address, pages);
-
-  grub_free (file_path);
-  grub_free (cmdline);
-  cmdline = 0;
-  file_path = 0;
 
   grub_dl_unref (my_mod);
   return GRUB_ERR_NONE;
@@ -140,7 +135,7 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename)
   char *dir_start;
   char *dir_end;
   grub_size_t size;
-  grub_efi_device_path_t *d;
+  grub_efi_device_path_t *d, *file_path;
 
   dir_start = grub_strchr (filename, ')');
   if (! dir_start)
@@ -222,11 +217,14 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
   grub_efi_status_t status;
   grub_efi_boot_services_t *b;
   grub_device_t dev = 0;
-  grub_efi_device_path_t *dp = 0;
+  grub_efi_device_path_t *dp = NULL, *file_path = NULL;
   grub_efi_loaded_image_t *loaded_image;
   char *filename;
   void *boot_image = 0;
   grub_efi_handle_t dev_handle = 0;
+  grub_efi_physical_address_t address = 0;
+  grub_efi_uintn_t pages = 0;
+  grub_efi_char16_t *cmdline = NULL;
 
   if (argc == 0)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
@@ -234,11 +232,6 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
 
   grub_dl_ref (my_mod);
 
-  /* Initialize some global variables.  */
-  address = 0;
-  image_handle = 0;
-  file_path = 0;
-
   b = grub_efi_system_table->boot_services;
 
   file = grub_file_open (filename, GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE);
@@ -408,6 +401,10 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
   grub_file_close (file);
   grub_device_close (dev);
 
+  /* We're finished with the source image buffer and file path now. */
+  efi_call_2 (b->free_pages, address, pages);
+  grub_free (file_path);
+
   grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
   return 0;
 
@@ -419,11 +416,18 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
   if (file)
     grub_file_close (file);
 
+  grub_free (cmdline);
   grub_free (file_path);
 
   if (address)
     efi_call_2 (b->free_pages, address, pages);
 
+  if (image_handle != NULL)
+    {
+      efi_call_1 (b->unload_image, image_handle);
+      image_handle = NULL;
+    }
+
   grub_dl_unref (my_mod);
 
   return grub_errno;
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 02/30] commands/boot: Add API to pass context to loader
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 01/30] loader/efi/chainloader: Simplify the loader state Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 03/30] loader/efi/chainloader: Use grub_loader_set_ex() Daniel Kiper
                   ` (27 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Chris Coulson <chris.coulson@canonical.com>

Loaders rely on global variables for saving context which is consumed
in the boot hook and freed in the unload hook. In the case where a loader
command is executed twice, calling grub_loader_set() a second time executes
the unload hook, but in some cases this runs when the loader's global
context has already been updated, resulting in the updated context being
freed and potential use-after-free bugs when the boot hook is subsequently
called.

This adds a new API, grub_loader_set_ex(), which allows a loader to specify
context that is passed to its boot and unload hooks. This is an alternative
to requiring that loaders call grub_loader_unset() before mutating their
global context.

Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/commands/boot.c | 66 +++++++++++++++++++++++++++++++++++++++++------
 include/grub/loader.h     |  5 ++++
 2 files changed, 63 insertions(+), 8 deletions(-)

diff --git a/grub-core/commands/boot.c b/grub-core/commands/boot.c
index bbca81e94..61514788e 100644
--- a/grub-core/commands/boot.c
+++ b/grub-core/commands/boot.c
@@ -27,10 +27,20 @@
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
-static grub_err_t (*grub_loader_boot_func) (void);
-static grub_err_t (*grub_loader_unload_func) (void);
+static grub_err_t (*grub_loader_boot_func) (void *context);
+static grub_err_t (*grub_loader_unload_func) (void *context);
+static void *grub_loader_context;
 static int grub_loader_flags;
 
+struct grub_simple_loader_hooks
+{
+  grub_err_t (*boot) (void);
+  grub_err_t (*unload) (void);
+};
+
+/* Don't heap allocate this to avoid making grub_loader_set() fallible. */
+static struct grub_simple_loader_hooks simple_loader_hooks;
+
 struct grub_preboot
 {
   grub_err_t (*preboot_func) (int);
@@ -44,6 +54,29 @@ static int grub_loader_loaded;
 static struct grub_preboot *preboots_head = 0,
   *preboots_tail = 0;
 
+static grub_err_t
+grub_simple_boot_hook (void *context)
+{
+  struct grub_simple_loader_hooks *hooks;
+
+  hooks = (struct grub_simple_loader_hooks *) context;
+  return hooks->boot ();
+}
+
+static grub_err_t
+grub_simple_unload_hook (void *context)
+{
+  struct grub_simple_loader_hooks *hooks;
+  grub_err_t ret;
+
+  hooks = (struct grub_simple_loader_hooks *) context;
+
+  ret = hooks->unload ();
+  grub_memset (hooks, 0, sizeof (*hooks));
+
+  return ret;
+}
+
 int
 grub_loader_is_loaded (void)
 {
@@ -110,28 +143,45 @@ grub_loader_unregister_preboot_hook (struct grub_preboot *hnd)
 }
 
 void
-grub_loader_set (grub_err_t (*boot) (void),
-		 grub_err_t (*unload) (void),
-		 int flags)
+grub_loader_set_ex (grub_err_t (*boot) (void *context),
+		    grub_err_t (*unload) (void *context),
+		    void *context,
+		    int flags)
 {
   if (grub_loader_loaded && grub_loader_unload_func)
-    grub_loader_unload_func ();
+    grub_loader_unload_func (grub_loader_context);
 
   grub_loader_boot_func = boot;
   grub_loader_unload_func = unload;
+  grub_loader_context = context;
   grub_loader_flags = flags;
 
   grub_loader_loaded = 1;
 }
 
 void
+grub_loader_set (grub_err_t (*boot) (void),
+		 grub_err_t (*unload) (void),
+		 int flags)
+{
+  grub_loader_set_ex (grub_simple_boot_hook,
+		      grub_simple_unload_hook,
+		      &simple_loader_hooks,
+		      flags);
+
+  simple_loader_hooks.boot = boot;
+  simple_loader_hooks.unload = unload;
+}
+
+void
 grub_loader_unset(void)
 {
   if (grub_loader_loaded && grub_loader_unload_func)
-    grub_loader_unload_func ();
+    grub_loader_unload_func (grub_loader_context);
 
   grub_loader_boot_func = 0;
   grub_loader_unload_func = 0;
+  grub_loader_context = 0;
 
   grub_loader_loaded = 0;
 }
@@ -158,7 +208,7 @@ grub_loader_boot (void)
 	  return err;
 	}
     }
-  err = (grub_loader_boot_func) ();
+  err = (grub_loader_boot_func) (grub_loader_context);
 
   for (cur = preboots_tail; cur; cur = cur->prev)
     if (! err)
diff --git a/include/grub/loader.h b/include/grub/loader.h
index b20864282..97f231054 100644
--- a/include/grub/loader.h
+++ b/include/grub/loader.h
@@ -40,6 +40,11 @@ void EXPORT_FUNC (grub_loader_set) (grub_err_t (*boot) (void),
 				    grub_err_t (*unload) (void),
 				    int flags);
 
+void EXPORT_FUNC (grub_loader_set_ex) (grub_err_t (*boot) (void *context),
+				       grub_err_t (*unload) (void *context),
+				       void *context,
+				       int flags);
+
 /* Unset current loader, if any.  */
 void EXPORT_FUNC (grub_loader_unset) (void);
 
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 03/30] loader/efi/chainloader: Use grub_loader_set_ex()
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 01/30] loader/efi/chainloader: Simplify the loader state Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 02/30] commands/boot: Add API to pass context to loader Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 04/30] kern/efi/sb: Reject non-kernel files in the shim_lock verifier Daniel Kiper
                   ` (26 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Chris Coulson <chris.coulson@canonical.com>

This ports the EFI chainloader to use grub_loader_set_ex() in order to fix
a use-after-free bug that occurs when grub_cmd_chainloader() is executed
more than once before a boot attempt is performed.

Fixes: CVE-2022-28736

Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/loader/efi/chainloader.c | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
index d1602c89b..7557eb269 100644
--- a/grub-core/loader/efi/chainloader.c
+++ b/grub-core/loader/efi/chainloader.c
@@ -44,11 +44,10 @@ GRUB_MOD_LICENSE ("GPLv3+");
 
 static grub_dl_t my_mod;
 
-static grub_efi_handle_t image_handle;
-
 static grub_err_t
-grub_chainloader_unload (void)
+grub_chainloader_unload (void *context)
 {
+  grub_efi_handle_t image_handle = (grub_efi_handle_t) context;
   grub_efi_loaded_image_t *loaded_image;
   grub_efi_boot_services_t *b;
 
@@ -64,8 +63,9 @@ grub_chainloader_unload (void)
 }
 
 static grub_err_t
-grub_chainloader_boot (void)
+grub_chainloader_boot (void *context)
 {
+  grub_efi_handle_t image_handle = (grub_efi_handle_t) context;
   grub_efi_boot_services_t *b;
   grub_efi_status_t status;
   grub_efi_uintn_t exit_data_size;
@@ -225,6 +225,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
   grub_efi_physical_address_t address = 0;
   grub_efi_uintn_t pages = 0;
   grub_efi_char16_t *cmdline = NULL;
+  grub_efi_handle_t image_handle = NULL;
 
   if (argc == 0)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
@@ -405,7 +406,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
   efi_call_2 (b->free_pages, address, pages);
   grub_free (file_path);
 
-  grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
+  grub_loader_set_ex (grub_chainloader_boot, grub_chainloader_unload, image_handle, 0);
   return 0;
 
  fail:
@@ -423,10 +424,7 @@ grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
     efi_call_2 (b->free_pages, address, pages);
 
   if (image_handle != NULL)
-    {
-      efi_call_1 (b->unload_image, image_handle);
-      image_handle = NULL;
-    }
+    efi_call_1 (b->unload_image, image_handle);
 
   grub_dl_unref (my_mod);
 
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 04/30] kern/efi/sb: Reject non-kernel files in the shim_lock verifier
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (2 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 03/30] loader/efi/chainloader: Use grub_loader_set_ex() Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 05/30] kern/file: Do not leak device_name on error in grub_file_open() Daniel Kiper
                   ` (25 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Julian Andres Klode <julian.klode@canonical.com>

We must not allow other verifiers to pass things like the GRUB modules.
Instead of maintaining a blocklist, maintain an allowlist of things
that we do not care about.

This allowlist really should be made reusable, and shared by the
lockdown verifier, but this is the minimal patch addressing
security concerns where the TPM verifier was able to mark modules
as verified (or the OpenPGP verifier for that matter), when it
should not do so on shim-powered secure boot systems.

Fixes: CVE-2022-28735

Signed-off-by: Julian Andres Klode <julian.klode@canonical.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/kern/efi/sb.c | 39 ++++++++++++++++++++++++++++++++++++---
 include/grub/verify.h   |  1 +
 2 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/grub-core/kern/efi/sb.c b/grub-core/kern/efi/sb.c
index c52ec6226..89c4bb3fd 100644
--- a/grub-core/kern/efi/sb.c
+++ b/grub-core/kern/efi/sb.c
@@ -119,10 +119,11 @@ shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)),
 			 void **context __attribute__ ((unused)),
 			 enum grub_verify_flags *flags)
 {
-  *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
+  *flags = GRUB_VERIFY_FLAGS_NONE;
 
   switch (type & GRUB_FILE_TYPE_MASK)
     {
+    /* Files we check. */
     case GRUB_FILE_TYPE_LINUX_KERNEL:
     case GRUB_FILE_TYPE_MULTIBOOT_KERNEL:
     case GRUB_FILE_TYPE_BSD_KERNEL:
@@ -130,11 +131,43 @@ shim_lock_verifier_init (grub_file_t io __attribute__ ((unused)),
     case GRUB_FILE_TYPE_PLAN9_KERNEL:
     case GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE:
       *flags = GRUB_VERIFY_FLAGS_SINGLE_CHUNK;
+      return GRUB_ERR_NONE;
 
-      /* Fall through. */
+    /* Files that do not affect secureboot state. */
+    case GRUB_FILE_TYPE_NONE:
+    case GRUB_FILE_TYPE_LOOPBACK:
+    case GRUB_FILE_TYPE_LINUX_INITRD:
+    case GRUB_FILE_TYPE_OPENBSD_RAMDISK:
+    case GRUB_FILE_TYPE_XNU_RAMDISK:
+    case GRUB_FILE_TYPE_SIGNATURE:
+    case GRUB_FILE_TYPE_PUBLIC_KEY:
+    case GRUB_FILE_TYPE_PUBLIC_KEY_TRUST:
+    case GRUB_FILE_TYPE_PRINT_BLOCKLIST:
+    case GRUB_FILE_TYPE_TESTLOAD:
+    case GRUB_FILE_TYPE_GET_SIZE:
+    case GRUB_FILE_TYPE_FONT:
+    case GRUB_FILE_TYPE_ZFS_ENCRYPTION_KEY:
+    case GRUB_FILE_TYPE_CAT:
+    case GRUB_FILE_TYPE_HEXCAT:
+    case GRUB_FILE_TYPE_CMP:
+    case GRUB_FILE_TYPE_HASHLIST:
+    case GRUB_FILE_TYPE_TO_HASH:
+    case GRUB_FILE_TYPE_KEYBOARD_LAYOUT:
+    case GRUB_FILE_TYPE_PIXMAP:
+    case GRUB_FILE_TYPE_GRUB_MODULE_LIST:
+    case GRUB_FILE_TYPE_CONFIG:
+    case GRUB_FILE_TYPE_THEME:
+    case GRUB_FILE_TYPE_GETTEXT_CATALOG:
+    case GRUB_FILE_TYPE_FS_SEARCH:
+    case GRUB_FILE_TYPE_LOADENV:
+    case GRUB_FILE_TYPE_SAVEENV:
+    case GRUB_FILE_TYPE_VERIFY_SIGNATURE:
+      *flags = GRUB_VERIFY_FLAGS_SKIP_VERIFICATION;
+      return GRUB_ERR_NONE;
 
+    /* Other files. */
     default:
-      return GRUB_ERR_NONE;
+      return grub_error (GRUB_ERR_ACCESS_DENIED, N_("prohibited by secure boot policy"));
     }
 }
 
diff --git a/include/grub/verify.h b/include/grub/verify.h
index cd129c398..672ae1692 100644
--- a/include/grub/verify.h
+++ b/include/grub/verify.h
@@ -24,6 +24,7 @@
 
 enum grub_verify_flags
   {
+    GRUB_VERIFY_FLAGS_NONE		= 0,
     GRUB_VERIFY_FLAGS_SKIP_VERIFICATION	= 1,
     GRUB_VERIFY_FLAGS_SINGLE_CHUNK	= 2,
     /* Defer verification to another authority. */
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 05/30] kern/file: Do not leak device_name on error in grub_file_open()
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (3 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 04/30] kern/efi/sb: Reject non-kernel files in the shim_lock verifier Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 06/30] video/readers/png: Abort sooner if a read operation fails Daniel Kiper
                   ` (24 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

If we have an error in grub_file_open() before we free device_name, we
will leak it.

Free device_name in the error path and null out the pointer in the good
path once we free it there.

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/kern/file.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/grub-core/kern/file.c b/grub-core/kern/file.c
index df24c1fd4..8d48fd50d 100644
--- a/grub-core/kern/file.c
+++ b/grub-core/kern/file.c
@@ -79,6 +79,7 @@ grub_file_open (const char *name, enum grub_file_type type)
 
   device = grub_device_open (device_name);
   grub_free (device_name);
+  device_name = NULL;
   if (! device)
     goto fail;
 
@@ -131,6 +132,7 @@ grub_file_open (const char *name, enum grub_file_type type)
   return file;
 
  fail:
+  grub_free (device_name);
   if (device)
     grub_device_close (device);
 
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 06/30] video/readers/png: Abort sooner if a read operation fails
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (4 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 05/30] kern/file: Do not leak device_name on error in grub_file_open() Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 07/30] video/readers/png: Refuse to handle multiple image headers Daniel Kiper
                   ` (23 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

Fuzzing revealed some inputs that were taking a long time, potentially
forever, because they did not bail quickly upon encountering an I/O error.

Try to catch I/O errors sooner and bail out.

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/video/readers/png.c | 55 ++++++++++++++++++++++++++++++++++++-------
 1 file changed, 47 insertions(+), 8 deletions(-)

diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c
index 54dfedf43..d715c4629 100644
--- a/grub-core/video/readers/png.c
+++ b/grub-core/video/readers/png.c
@@ -142,6 +142,7 @@ static grub_uint8_t
 grub_png_get_byte (struct grub_png_data *data)
 {
   grub_uint8_t r;
+  grub_ssize_t bytes_read = 0;
 
   if ((data->inside_idat) && (data->idat_remain == 0))
     {
@@ -175,7 +176,14 @@ grub_png_get_byte (struct grub_png_data *data)
     }
 
   r = 0;
-  grub_file_read (data->file, &r, 1);
+  bytes_read = grub_file_read (data->file, &r, 1);
+
+  if (bytes_read != 1)
+    {
+      grub_error (GRUB_ERR_BAD_FILE_TYPE,
+		  "png: unexpected end of data");
+      return 0;
+    }
 
   if (data->inside_idat)
     data->idat_remain--;
@@ -231,15 +239,16 @@ grub_png_decode_image_palette (struct grub_png_data *data,
   if (len == 0)
     return GRUB_ERR_NONE;
 
-  for (i = 0; 3 * i < len && i < 256; i++)
+  grub_errno = GRUB_ERR_NONE;
+  for (i = 0; 3 * i < len && i < 256 && grub_errno == GRUB_ERR_NONE; i++)
     for (j = 0; j < 3; j++)
       data->palette[i][j] = grub_png_get_byte (data);
-  for (i *= 3; i < len; i++)
+  for (i *= 3; i < len && grub_errno == GRUB_ERR_NONE; i++)
     grub_png_get_byte (data);
 
   grub_png_get_dword (data);
 
-  return GRUB_ERR_NONE;
+  return grub_errno;
 }
 
 static grub_err_t
@@ -256,9 +265,13 @@ grub_png_decode_image_header (struct grub_png_data *data)
     return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: invalid image size");
 
   color_bits = grub_png_get_byte (data);
+  if (grub_errno != GRUB_ERR_NONE)
+    return grub_errno;
   data->is_16bit = (color_bits == 16);
 
   color_type = grub_png_get_byte (data);
+  if (grub_errno != GRUB_ERR_NONE)
+    return grub_errno;
 
   /* According to PNG spec, no other types are valid.  */
   if ((color_type & ~(PNG_COLOR_MASK_ALPHA | PNG_COLOR_MASK_COLOR))
@@ -340,14 +353,20 @@ grub_png_decode_image_header (struct grub_png_data *data)
   if (grub_png_get_byte (data) != PNG_COMPRESSION_BASE)
     return grub_error (GRUB_ERR_BAD_FILE_TYPE,
 		       "png: compression method not supported");
+  if (grub_errno != GRUB_ERR_NONE)
+    return grub_errno;
 
   if (grub_png_get_byte (data) != PNG_FILTER_TYPE_BASE)
     return grub_error (GRUB_ERR_BAD_FILE_TYPE,
 		       "png: filter method not supported");
+  if (grub_errno != GRUB_ERR_NONE)
+    return grub_errno;
 
   if (grub_png_get_byte (data) != PNG_INTERLACE_NONE)
     return grub_error (GRUB_ERR_BAD_FILE_TYPE,
 		       "png: interlace method not supported");
+  if (grub_errno != GRUB_ERR_NONE)
+    return grub_errno;
 
   /* Skip crc checksum.  */
   grub_png_get_dword (data);
@@ -449,7 +468,7 @@ grub_png_get_huff_code (struct grub_png_data *data, struct huff_table *ht)
   int code, i;
 
   code = 0;
-  for (i = 0; i < ht->max_length; i++)
+  for (i = 0; i < ht->max_length && grub_errno == GRUB_ERR_NONE; i++)
     {
       code = (code << 1) + grub_png_get_bits (data, 1);
       if (code < ht->maxval[i])
@@ -504,8 +523,14 @@ grub_png_init_dynamic_block (struct grub_png_data *data)
   grub_uint8_t lens[DEFLATE_HCLEN_MAX];
 
   nl = DEFLATE_HLIT_BASE + grub_png_get_bits (data, 5);
+  if (grub_errno != GRUB_ERR_NONE)
+    return grub_errno;
   nd = DEFLATE_HDIST_BASE + grub_png_get_bits (data, 5);
+  if (grub_errno != GRUB_ERR_NONE)
+    return grub_errno;
   nb = DEFLATE_HCLEN_BASE + grub_png_get_bits (data, 4);
+  if (grub_errno != GRUB_ERR_NONE)
+    return grub_errno;
 
   if ((nl > DEFLATE_HLIT_MAX) || (nd > DEFLATE_HDIST_MAX) ||
       (nb > DEFLATE_HCLEN_MAX))
@@ -533,7 +558,7 @@ grub_png_init_dynamic_block (struct grub_png_data *data)
 			    data->dist_offset);
 
   prev = 0;
-  for (i = 0; i < nl + nd; i++)
+  for (i = 0; i < nl + nd && grub_errno == GRUB_ERR_NONE; i++)
     {
       int n, code;
       struct huff_table *ht;
@@ -721,17 +746,21 @@ grub_png_read_dynamic_block (struct grub_png_data *data)
 	  len = cplens[n];
 	  if (cplext[n])
 	    len += grub_png_get_bits (data, cplext[n]);
+	  if (grub_errno != GRUB_ERR_NONE)
+	    return grub_errno;
 
 	  n = grub_png_get_huff_code (data, &data->dist_table);
 	  dist = cpdist[n];
 	  if (cpdext[n])
 	    dist += grub_png_get_bits (data, cpdext[n]);
+	  if (grub_errno != GRUB_ERR_NONE)
+	    return grub_errno;
 
 	  pos = data->wp - dist;
 	  if (pos < 0)
 	    pos += WSIZE;
 
-	  while (len > 0)
+	  while (len > 0 && grub_errno == GRUB_ERR_NONE)
 	    {
 	      data->slide[data->wp] = data->slide[pos];
 	      grub_png_output_byte (data, data->slide[data->wp]);
@@ -759,7 +788,11 @@ grub_png_decode_image_data (struct grub_png_data *data)
   int final;
 
   cmf = grub_png_get_byte (data);
+  if (grub_errno != GRUB_ERR_NONE)
+    return grub_errno;
   flg = grub_png_get_byte (data);
+  if (grub_errno != GRUB_ERR_NONE)
+    return grub_errno;
 
   if ((cmf & 0xF) != Z_DEFLATED)
     return grub_error (GRUB_ERR_BAD_FILE_TYPE,
@@ -774,7 +807,11 @@ grub_png_decode_image_data (struct grub_png_data *data)
       int block_type;
 
       final = grub_png_get_bits (data, 1);
+      if (grub_errno != GRUB_ERR_NONE)
+	return grub_errno;
       block_type = grub_png_get_bits (data, 2);
+      if (grub_errno != GRUB_ERR_NONE)
+	return grub_errno;
 
       switch (block_type)
 	{
@@ -790,7 +827,7 @@ grub_png_decode_image_data (struct grub_png_data *data)
 	    grub_png_get_byte (data);
 	    grub_png_get_byte (data);
 
-	    for (i = 0; i < len; i++)
+	    for (i = 0; i < len && grub_errno == GRUB_ERR_NONE; i++)
 	      grub_png_output_byte (data, grub_png_get_byte (data));
 
 	    break;
@@ -1045,6 +1082,8 @@ grub_png_decode_png (struct grub_png_data *data)
 
       len = grub_png_get_dword (data);
       type = grub_png_get_dword (data);
+      if (grub_errno != GRUB_ERR_NONE)
+	break;
       data->next_offset = data->file->offset + len + 4;
 
       switch (type)
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 07/30] video/readers/png: Refuse to handle multiple image headers
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (5 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 06/30] video/readers/png: Abort sooner if a read operation fails Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 08/30] video/readers/png: Drop greyscale support to fix heap out-of-bounds write Daniel Kiper
                   ` (22 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

This causes the bitmap to be leaked. Do not permit multiple image headers.

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/video/readers/png.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c
index d715c4629..35ae553c8 100644
--- a/grub-core/video/readers/png.c
+++ b/grub-core/video/readers/png.c
@@ -258,6 +258,9 @@ grub_png_decode_image_header (struct grub_png_data *data)
   int color_bits;
   enum grub_video_blit_format blt;
 
+  if (data->image_width || data->image_height)
+    return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: two image headers found");
+
   data->image_width = grub_png_get_dword (data);
   data->image_height = grub_png_get_dword (data);
 
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 08/30] video/readers/png: Drop greyscale support to fix heap out-of-bounds write
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (6 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 07/30] video/readers/png: Refuse to handle multiple image headers Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 09/30] video/readers/png: Avoid heap OOB R/W inserting huff table items Daniel Kiper
                   ` (21 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

A 16-bit greyscale PNG without alpha is processed in the following loop:

      for (i = 0; i < (data->image_width * data->image_height);
	   i++, d1 += 4, d2 += 2)
	{
	  d1[R3] = d2[1];
	  d1[G3] = d2[1];
	  d1[B3] = d2[1];
	}

The increment of d1 is wrong. d1 is incremented by 4 bytes per iteration,
but there are only 3 bytes allocated for storage. This means that image
data will overwrite somewhat-attacker-controlled parts of memory - 3 bytes
out of every 4 following the end of the image.

This has existed since greyscale support was added in 2013 in commit
3ccf16dff98f (grub-core/video/readers/png.c: Support grayscale).

Saving starfield.png as a 16-bit greyscale image without alpha in the gimp
and attempting to load it causes grub-emu to crash - I don't think this code
has ever worked.

Delete all PNG greyscale support.

Fixes: CVE-2021-3695

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/video/readers/png.c | 85 +++----------------------------------------
 1 file changed, 6 insertions(+), 79 deletions(-)

diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c
index 35ae553c8..a3161e25b 100644
--- a/grub-core/video/readers/png.c
+++ b/grub-core/video/readers/png.c
@@ -100,7 +100,7 @@ struct grub_png_data
 
   unsigned image_width, image_height;
   int bpp, is_16bit;
-  int raw_bytes, is_gray, is_alpha, is_palette;
+  int raw_bytes, is_alpha, is_palette;
   int row_bytes, color_bits;
   grub_uint8_t *image_data;
 
@@ -296,13 +296,13 @@ grub_png_decode_image_header (struct grub_png_data *data)
     data->bpp = 3;
   else
     {
-      data->is_gray = 1;
-      data->bpp = 1;
+      return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+			 "png: color type not supported");
     }
 
   if ((color_bits != 8) && (color_bits != 16)
       && (color_bits != 4
-	  || !(data->is_gray || data->is_palette)))
+	  || !data->is_palette))
     return grub_error (GRUB_ERR_BAD_FILE_TYPE,
                        "png: bit depth must be 8 or 16");
 
@@ -331,7 +331,7 @@ grub_png_decode_image_header (struct grub_png_data *data)
     }
 
 #ifndef GRUB_CPU_WORDS_BIGENDIAN
-  if (data->is_16bit || data->is_gray || data->is_palette)
+  if (data->is_16bit || data->is_palette)
 #endif
     {
       data->image_data = grub_calloc (data->image_height, data->row_bytes);
@@ -899,27 +899,8 @@ grub_png_convert_image (struct grub_png_data *data)
       int shift;
       int mask = (1 << data->color_bits) - 1;
       unsigned j;
-      if (data->is_gray)
-	{
-	  /* Generic formula is
-	     (0xff * i) / ((1U << data->color_bits) - 1)
-	     but for allowed bit depth of 1, 2 and for it's
-	     equivalent to
-	     (0xff / ((1U << data->color_bits) - 1)) * i
-	     Precompute the multipliers to avoid division.
-	  */
 
-	  const grub_uint8_t multipliers[5] = { 0xff, 0xff, 0x55, 0x24, 0x11 };
-	  for (i = 0; i < (1U << data->color_bits); i++)
-	    {
-	      grub_uint8_t col = multipliers[data->color_bits] * i;
-	      palette[i][0] = col;
-	      palette[i][1] = col;
-	      palette[i][2] = col;
-	    }
-	}
-      else
-	grub_memcpy (palette, data->palette, 3 << data->color_bits);
+      grub_memcpy (palette, data->palette, 3 << data->color_bits);
       d1c = d1;
       d2c = d2;
       for (j = 0; j < data->image_height; j++, d1c += data->image_width * 3,
@@ -957,60 +938,6 @@ grub_png_convert_image (struct grub_png_data *data)
       return;
     }
 
-  if (data->is_gray)
-    {
-      switch (data->bpp)
-	{
-	case 4:
-	  /* 16-bit gray with alpha.  */
-	  for (i = 0; i < (data->image_width * data->image_height);
-	       i++, d1 += 4, d2 += 4)
-	    {
-	      d1[R4] = d2[3];
-	      d1[G4] = d2[3];
-	      d1[B4] = d2[3];
-	      d1[A4] = d2[1];
-	    }
-	  break;
-	case 2:
-	  if (data->is_16bit)
-	    /* 16-bit gray without alpha.  */
-	    {
-	      for (i = 0; i < (data->image_width * data->image_height);
-		   i++, d1 += 4, d2 += 2)
-		{
-		  d1[R3] = d2[1];
-		  d1[G3] = d2[1];
-		  d1[B3] = d2[1];
-		}
-	    }
-	  else
-	    /* 8-bit gray with alpha.  */
-	    {
-	      for (i = 0; i < (data->image_width * data->image_height);
-		   i++, d1 += 4, d2 += 2)
-		{
-		  d1[R4] = d2[1];
-		  d1[G4] = d2[1];
-		  d1[B4] = d2[1];
-		  d1[A4] = d2[0];
-		}
-	    }
-	  break;
-	  /* 8-bit gray without alpha.  */
-	case 1:
-	  for (i = 0; i < (data->image_width * data->image_height);
-	       i++, d1 += 3, d2++)
-	    {
-	      d1[R3] = d2[0];
-	      d1[G3] = d2[0];
-	      d1[B3] = d2[0];
-	    }
-	  break;
-	}
-      return;
-    }
-
     {
   /* Only copy the upper 8 bit.  */
 #ifndef GRUB_CPU_WORDS_BIGENDIAN
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 09/30] video/readers/png: Avoid heap OOB R/W inserting huff table items
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (7 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 08/30] video/readers/png: Drop greyscale support to fix heap out-of-bounds write Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 10/30] video/readers/png: Sanity check some huffman codes Daniel Kiper
                   ` (20 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

In fuzzing we observed crashes where a code would attempt to be inserted
into a huffman table before the start, leading to a set of heap OOB reads
and writes as table entries with negative indices were shifted around and
the new code written in.

Catch the case where we would underflow the array and bail.

Fixes: CVE-2021-3696

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/video/readers/png.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c
index a3161e25b..d7ed5aa6c 100644
--- a/grub-core/video/readers/png.c
+++ b/grub-core/video/readers/png.c
@@ -438,6 +438,13 @@ grub_png_insert_huff_item (struct huff_table *ht, int code, int len)
   for (i = len; i < ht->max_length; i++)
     n += ht->maxval[i];
 
+  if (n > ht->num_values)
+    {
+      grub_error (GRUB_ERR_BAD_FILE_TYPE,
+		  "png: out of range inserting huffman table item");
+      return;
+    }
+
   for (i = 0; i < n; i++)
     ht->values[ht->num_values - i] = ht->values[ht->num_values - i - 1];
 
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 10/30] video/readers/png: Sanity check some huffman codes
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (8 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 09/30] video/readers/png: Avoid heap OOB R/W inserting huff table items Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 11/30] video/readers/jpeg: Abort sooner if a read operation fails Daniel Kiper
                   ` (19 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

ASAN picked up two OOB global reads: we weren't checking if some code
values fit within the cplens or cpdext arrays. Check and throw an error
if not.

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/video/readers/png.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c
index d7ed5aa6c..7f2ba7849 100644
--- a/grub-core/video/readers/png.c
+++ b/grub-core/video/readers/png.c
@@ -753,6 +753,9 @@ grub_png_read_dynamic_block (struct grub_png_data *data)
 	  int len, dist, pos;
 
 	  n -= 257;
+	  if (((unsigned int) n) >= ARRAY_SIZE (cplens))
+	    return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+			       "png: invalid huff code");
 	  len = cplens[n];
 	  if (cplext[n])
 	    len += grub_png_get_bits (data, cplext[n]);
@@ -760,6 +763,9 @@ grub_png_read_dynamic_block (struct grub_png_data *data)
 	    return grub_errno;
 
 	  n = grub_png_get_huff_code (data, &data->dist_table);
+	  if (((unsigned int) n) >= ARRAY_SIZE (cpdist))
+	    return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+			       "png: invalid huff code");
 	  dist = cpdist[n];
 	  if (cpdext[n])
 	    dist += grub_png_get_bits (data, cpdext[n]);
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 11/30] video/readers/jpeg: Abort sooner if a read operation fails
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (9 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 10/30] video/readers/png: Sanity check some huffman codes Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 12/30] video/readers/jpeg: Do not reallocate a given huff table Daniel Kiper
                   ` (18 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

Fuzzing revealed some inputs that were taking a long time, potentially
forever, because they did not bail quickly upon encountering an I/O error.

Try to catch I/O errors sooner and bail out.

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/video/readers/jpeg.c | 86 ++++++++++++++++++++++++++++++++++--------
 1 file changed, 70 insertions(+), 16 deletions(-)

diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c
index c47ffd651..806c56c78 100644
--- a/grub-core/video/readers/jpeg.c
+++ b/grub-core/video/readers/jpeg.c
@@ -109,9 +109,17 @@ static grub_uint8_t
 grub_jpeg_get_byte (struct grub_jpeg_data *data)
 {
   grub_uint8_t r;
+  grub_ssize_t bytes_read;
 
   r = 0;
-  grub_file_read (data->file, &r, 1);
+  bytes_read = grub_file_read (data->file, &r, 1);
+
+  if (bytes_read != 1)
+    {
+      grub_error (GRUB_ERR_BAD_FILE_TYPE,
+		  "jpeg: unexpected end of data");
+      return 0;
+    }
 
   return r;
 }
@@ -120,9 +128,17 @@ static grub_uint16_t
 grub_jpeg_get_word (struct grub_jpeg_data *data)
 {
   grub_uint16_t r;
+  grub_ssize_t bytes_read;
 
   r = 0;
-  grub_file_read (data->file, &r, sizeof (grub_uint16_t));
+  bytes_read = grub_file_read (data->file, &r, sizeof (grub_uint16_t));
+
+  if (bytes_read != sizeof (grub_uint16_t))
+    {
+      grub_error (GRUB_ERR_BAD_FILE_TYPE,
+		  "jpeg: unexpected end of data");
+      return 0;
+    }
 
   return grub_be_to_cpu16 (r);
 }
@@ -135,6 +151,11 @@ grub_jpeg_get_bit (struct grub_jpeg_data *data)
   if (data->bit_mask == 0)
     {
       data->bit_save = grub_jpeg_get_byte (data);
+      if (grub_errno != GRUB_ERR_NONE) {
+	grub_error (GRUB_ERR_BAD_FILE_TYPE,
+		    "jpeg: file read error");
+	return 0;
+      }
       if (data->bit_save == JPEG_ESC_CHAR)
 	{
 	  if (grub_jpeg_get_byte (data) != 0)
@@ -143,6 +164,11 @@ grub_jpeg_get_bit (struct grub_jpeg_data *data)
 			  "jpeg: invalid 0xFF in data stream");
 	      return 0;
 	    }
+	  if (grub_errno != GRUB_ERR_NONE)
+	    {
+	      grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: file read error");
+	      return 0;
+	    }
 	}
       data->bit_mask = 0x80;
     }
@@ -161,7 +187,7 @@ grub_jpeg_get_number (struct grub_jpeg_data *data, int num)
     return 0;
 
   msb = value = grub_jpeg_get_bit (data);
-  for (i = 1; i < num; i++)
+  for (i = 1; i < num && grub_errno == GRUB_ERR_NONE; i++)
     value = (value << 1) + (grub_jpeg_get_bit (data) != 0);
   if (!msb)
     value += 1 - (1 << num);
@@ -208,6 +234,8 @@ grub_jpeg_decode_huff_table (struct grub_jpeg_data *data)
   while (data->file->offset + sizeof (count) + 1 <= next_marker)
     {
       id = grub_jpeg_get_byte (data);
+      if (grub_errno != GRUB_ERR_NONE)
+	return grub_errno;
       ac = (id >> 4) & 1;
       id &= 0xF;
       if (id > 1)
@@ -258,6 +286,8 @@ grub_jpeg_decode_quan_table (struct grub_jpeg_data *data)
 
   next_marker = data->file->offset;
   next_marker += grub_jpeg_get_word (data);
+  if (grub_errno != GRUB_ERR_NONE)
+    return grub_errno;
 
   if (next_marker > data->file->size)
     {
@@ -269,6 +299,8 @@ grub_jpeg_decode_quan_table (struct grub_jpeg_data *data)
 	 <= next_marker)
     {
       id = grub_jpeg_get_byte (data);
+      if (grub_errno != GRUB_ERR_NONE)
+        return grub_errno;
       if (id >= 0x10)		/* Upper 4-bit is precision.  */
 	return grub_error (GRUB_ERR_BAD_FILE_TYPE,
 			   "jpeg: only 8-bit precision is supported");
@@ -300,6 +332,9 @@ grub_jpeg_decode_sof (struct grub_jpeg_data *data)
   next_marker = data->file->offset;
   next_marker += grub_jpeg_get_word (data);
 
+  if (grub_errno != GRUB_ERR_NONE)
+    return grub_errno;
+
   if (grub_jpeg_get_byte (data) != 8)
     return grub_error (GRUB_ERR_BAD_FILE_TYPE,
 		       "jpeg: only 8-bit precision is supported");
@@ -325,6 +360,8 @@ grub_jpeg_decode_sof (struct grub_jpeg_data *data)
 	return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid index");
 
       ss = grub_jpeg_get_byte (data);	/* Sampling factor.  */
+      if (grub_errno != GRUB_ERR_NONE)
+	return grub_errno;
       if (!id)
 	{
 	  grub_uint8_t vs, hs;
@@ -504,7 +541,7 @@ grub_jpeg_idct_transform (jpeg_data_unit_t du)
     }
 }
 
-static void
+static grub_err_t
 grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du)
 {
   int h1, h2, qt;
@@ -519,6 +556,9 @@ grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du)
   data->dc_value[id] +=
     grub_jpeg_get_number (data, grub_jpeg_get_huff_code (data, h1));
 
+  if (grub_errno != GRUB_ERR_NONE)
+    return grub_errno;
+
   du[0] = data->dc_value[id] * (int) data->quan_table[qt][0];
   pos = 1;
   while (pos < ARRAY_SIZE (data->quan_table[qt]))
@@ -533,11 +573,13 @@ grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du)
       num >>= 4;
       pos += num;
 
+      if (grub_errno != GRUB_ERR_NONE)
+        return grub_errno;
+
       if (pos >= ARRAY_SIZE (jpeg_zigzag_order))
 	{
-	  grub_error (GRUB_ERR_BAD_FILE_TYPE,
-		      "jpeg: invalid position in zigzag order!?");
-	  return;
+	  return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+			     "jpeg: invalid position in zigzag order!?");
 	}
 
       du[jpeg_zigzag_order[pos]] = val * (int) data->quan_table[qt][pos];
@@ -545,6 +587,7 @@ grub_jpeg_decode_du (struct grub_jpeg_data *data, int id, jpeg_data_unit_t du)
     }
 
   grub_jpeg_idct_transform (du);
+  return GRUB_ERR_NONE;
 }
 
 static void
@@ -603,7 +646,8 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data)
   data_offset += grub_jpeg_get_word (data);
 
   cc = grub_jpeg_get_byte (data);
-
+  if (grub_errno != GRUB_ERR_NONE)
+    return grub_errno;
   if (cc != 3 && cc != 1)
     return grub_error (GRUB_ERR_BAD_FILE_TYPE,
 		       "jpeg: component count must be 1 or 3");
@@ -616,7 +660,8 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data)
       id = grub_jpeg_get_byte (data) - 1;
       if ((id < 0) || (id >= 3))
 	return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid index");
-
+      if (grub_errno != GRUB_ERR_NONE)
+	return grub_errno;
       ht = grub_jpeg_get_byte (data);
       data->comp_index[id][1] = (ht >> 4);
       data->comp_index[id][2] = (ht & 0xF) + 2;
@@ -624,11 +669,14 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data)
       if ((data->comp_index[id][1] < 0) || (data->comp_index[id][1] > 3) ||
 	  (data->comp_index[id][2] < 0) || (data->comp_index[id][2] > 3))
 	return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: invalid hufftable index");
+      if (grub_errno != GRUB_ERR_NONE)
+	return grub_errno;
     }
 
   grub_jpeg_get_byte (data);	/* Skip 3 unused bytes.  */
   grub_jpeg_get_word (data);
-
+  if (grub_errno != GRUB_ERR_NONE)
+    return grub_errno;
   if (data->file->offset != data_offset)
     return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: extra byte in sos");
 
@@ -646,6 +694,7 @@ grub_jpeg_decode_data (struct grub_jpeg_data *data)
 {
   unsigned c1, vb, hb, nr1, nc1;
   int rst = data->dri;
+  grub_err_t err = GRUB_ERR_NONE;
 
   vb = 8 << data->log_vs;
   hb = 8 << data->log_hs;
@@ -666,17 +715,22 @@ grub_jpeg_decode_data (struct grub_jpeg_data *data)
 
 	for (r2 = 0; r2 < (1U << data->log_vs); r2++)
 	  for (c2 = 0; c2 < (1U << data->log_hs); c2++)
-	    grub_jpeg_decode_du (data, 0, data->ydu[r2 * 2 + c2]);
+            {
+              err = grub_jpeg_decode_du (data, 0, data->ydu[r2 * 2 + c2]);
+              if (err != GRUB_ERR_NONE)
+                return err;
+            }
 
 	if (data->color_components >= 3)
 	  {
-	    grub_jpeg_decode_du (data, 1, data->cbdu);
-	    grub_jpeg_decode_du (data, 2, data->crdu);
+	    err = grub_jpeg_decode_du (data, 1, data->cbdu);
+	    if (err != GRUB_ERR_NONE)
+	      return err;
+	    err = grub_jpeg_decode_du (data, 2, data->crdu);
+	    if (err != GRUB_ERR_NONE)
+	      return err;
 	  }
 
-	if (grub_errno)
-	  return grub_errno;
-
 	nr2 = (data->r1 == nr1 - 1) ? (data->image_height - data->r1 * vb) : vb;
 	nc2 = (c1 == nc1 - 1) ? (data->image_width - c1 * hb) : hb;
 
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 12/30] video/readers/jpeg: Do not reallocate a given huff table
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (10 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 11/30] video/readers/jpeg: Abort sooner if a read operation fails Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 13/30] video/readers/jpeg: Refuse to handle multiple start of streams Daniel Kiper
                   ` (17 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

Fix a memory leak where an invalid file could cause us to reallocate
memory for a huffman table we had already allocated memory for.

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/video/readers/jpeg.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c
index 806c56c78..2284a6c06 100644
--- a/grub-core/video/readers/jpeg.c
+++ b/grub-core/video/readers/jpeg.c
@@ -251,6 +251,9 @@ grub_jpeg_decode_huff_table (struct grub_jpeg_data *data)
 	n += count[i];
 
       id += ac * 2;
+      if (data->huff_value[id] != NULL)
+	return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+			   "jpeg: attempt to reallocate huffman table");
       data->huff_value[id] = grub_malloc (n);
       if (grub_errno)
 	return grub_errno;
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 13/30] video/readers/jpeg: Refuse to handle multiple start of streams
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (11 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 12/30] video/readers/jpeg: Do not reallocate a given huff table Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 14/30] video/readers/jpeg: Block int underflow -> wild pointer write Daniel Kiper
                   ` (16 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

An invalid file could contain multiple start of stream blocks, which
would cause us to reallocate and leak our bitmap. Refuse to handle
multiple start of streams.

Additionally, fix a grub_error() call formatting.

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/video/readers/jpeg.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c
index 2284a6c06..579bbe8a4 100644
--- a/grub-core/video/readers/jpeg.c
+++ b/grub-core/video/readers/jpeg.c
@@ -683,6 +683,9 @@ grub_jpeg_decode_sos (struct grub_jpeg_data *data)
   if (data->file->offset != data_offset)
     return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: extra byte in sos");
 
+  if (*data->bitmap)
+    return grub_error (GRUB_ERR_BAD_FILE_TYPE, "jpeg: too many start of scan blocks");
+
   if (grub_video_bitmap_create (data->bitmap, data->image_width,
 				data->image_height,
 				GRUB_VIDEO_BLIT_FORMAT_RGB_888))
@@ -705,8 +708,8 @@ grub_jpeg_decode_data (struct grub_jpeg_data *data)
   nc1 = (data->image_width + hb - 1)  >> (3 + data->log_hs);
 
   if (data->bitmap_ptr == NULL)
-    return grub_error(GRUB_ERR_BAD_FILE_TYPE,
-		      "jpeg: attempted to decode data before start of stream");
+    return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+		       "jpeg: attempted to decode data before start of stream");
 
   for (; data->r1 < nr1 && (!data->dri || rst);
        data->r1++, data->bitmap_ptr += (vb * data->image_width - hb * nc1) * 3)
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 14/30] video/readers/jpeg: Block int underflow -> wild pointer write
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (12 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 13/30] video/readers/jpeg: Refuse to handle multiple start of streams Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 15/30] normal/charset: Fix array out-of-bounds formatting unicode for display Daniel Kiper
                   ` (15 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

Certain 1 px wide images caused a wild pointer write in
grub_jpeg_ycrcb_to_rgb(). This was caused because in grub_jpeg_decode_data(),
we have the following loop:

for (; data->r1 < nr1 && (!data->dri || rst);
     data->r1++, data->bitmap_ptr += (vb * data->image_width - hb * nc1) * 3)

We did not check if vb * width >= hb * nc1.

On a 64-bit platform, if that turns out to be negative, it will underflow,
be interpreted as unsigned 64-bit, then be added to the 64-bit pointer, so
we see data->bitmap_ptr jump, e.g.:

0x6180_0000_0480 to
0x6181_0000_0498
     ^
     ~--- carry has occurred and this pointer is now far away from
          any object.

On a 32-bit platform, it will decrement the pointer, creating a pointer
that won't crash but will overwrite random data.

Catch the underflow and error out.

Fixes: CVE-2021-3697

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/video/readers/jpeg.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/grub-core/video/readers/jpeg.c b/grub-core/video/readers/jpeg.c
index 579bbe8a4..09596fbf5 100644
--- a/grub-core/video/readers/jpeg.c
+++ b/grub-core/video/readers/jpeg.c
@@ -23,6 +23,7 @@
 #include <grub/mm.h>
 #include <grub/misc.h>
 #include <grub/bufio.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -699,6 +700,7 @@ static grub_err_t
 grub_jpeg_decode_data (struct grub_jpeg_data *data)
 {
   unsigned c1, vb, hb, nr1, nc1;
+  unsigned stride_a, stride_b, stride;
   int rst = data->dri;
   grub_err_t err = GRUB_ERR_NONE;
 
@@ -711,8 +713,14 @@ grub_jpeg_decode_data (struct grub_jpeg_data *data)
     return grub_error (GRUB_ERR_BAD_FILE_TYPE,
 		       "jpeg: attempted to decode data before start of stream");
 
+  if (grub_mul(vb, data->image_width, &stride_a) ||
+      grub_mul(hb, nc1, &stride_b) ||
+      grub_sub(stride_a, stride_b, &stride))
+    return grub_error (GRUB_ERR_BAD_FILE_TYPE,
+		       "jpeg: cannot decode image with these dimensions");
+
   for (; data->r1 < nr1 && (!data->dri || rst);
-       data->r1++, data->bitmap_ptr += (vb * data->image_width - hb * nc1) * 3)
+       data->r1++, data->bitmap_ptr += stride * 3)
     for (c1 = 0;  c1 < nc1 && (!data->dri || rst);
 	c1++, rst--, data->bitmap_ptr += hb * 3)
       {
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 15/30] normal/charset: Fix array out-of-bounds formatting unicode for display
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (13 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 14/30] video/readers/jpeg: Block int underflow -> wild pointer write Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 16/30] net/ip: Do IP fragment maths safely Daniel Kiper
                   ` (14 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

In some cases attempting to display arbitrary binary strings leads
to ASAN splats reading the widthspec array out of bounds.

Check the index. If it would be out of bounds, return a width of 1.
I don't know if that's strictly correct, but we're not really expecting
great display of arbitrary binary data, and it's certainly not worse than
an OOB read.

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/normal/charset.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/grub-core/normal/charset.c b/grub-core/normal/charset.c
index 7778f1a99..000e687bd 100644
--- a/grub-core/normal/charset.c
+++ b/grub-core/normal/charset.c
@@ -395,6 +395,8 @@ grub_unicode_estimate_width (const struct grub_unicode_glyph *c)
 {
   if (grub_unicode_get_comb_type (c->base))
     return 0;
+  if (((unsigned long) (c->base >> 3)) >= ARRAY_SIZE (widthspec))
+    return 1;
   if (widthspec[c->base >> 3] & (1 << (c->base & 7)))
     return 2;
   else
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 16/30] net/ip: Do IP fragment maths safely
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (14 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 15/30] normal/charset: Fix array out-of-bounds formatting unicode for display Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 17/30] net/netbuff: Block overly large netbuff allocs Daniel Kiper
                   ` (13 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

We can receive packets with invalid IP fragmentation information. This
can lead to rsm->total_len underflowing and becoming very large.

Then, in grub_netbuff_alloc(), we add to this very large number, which can
cause it to overflow and wrap back around to a small positive number.
The allocation then succeeds, but the resulting buffer is too small and
subsequent operations can write past the end of the buffer.

Catch the underflow here.

Fixes: CVE-2022-28733

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/net/ip.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c
index e3d62e97f..3c3d0be0e 100644
--- a/grub-core/net/ip.c
+++ b/grub-core/net/ip.c
@@ -25,6 +25,7 @@
 #include <grub/net/netbuff.h>
 #include <grub/mm.h>
 #include <grub/priority_queue.h>
+#include <grub/safemath.h>
 #include <grub/time.h>
 
 struct iphdr {
@@ -512,7 +513,14 @@ grub_net_recv_ip4_packets (struct grub_net_buff *nb,
     {
       rsm->total_len = (8 * (grub_be_to_cpu16 (iph->frags) & OFFSET_MASK)
 			+ (nb->tail - nb->data));
-      rsm->total_len -= ((iph->verhdrlen & 0xf) * sizeof (grub_uint32_t));
+
+      if (grub_sub (rsm->total_len, (iph->verhdrlen & 0xf) * sizeof (grub_uint32_t),
+		    &rsm->total_len))
+	{
+	  grub_dprintf ("net", "IP reassembly size underflow\n");
+	  return GRUB_ERR_NONE;
+	}
+
       rsm->asm_netbuff = grub_netbuff_alloc (rsm->total_len);
       if (!rsm->asm_netbuff)
 	{
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 17/30] net/netbuff: Block overly large netbuff allocs
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (15 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 16/30] net/ip: Do IP fragment maths safely Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 18/30] net/dns: Fix double-free addresses on corrupt DNS response Daniel Kiper
                   ` (12 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

A netbuff shouldn't be too huge. It's bounded by MTU and TCP segment
reassembly. If we are asked to create one that is unreasonably big, refuse.

This is a hardening measure: if we hit this code, there's a bug somewhere
else that we should catch and fix.

This commit:
  - stops the bug propagating any further.
  - provides a spot to instrument in e.g. fuzzing to try to catch these bugs.

I have put instrumentation (e.g. __builtin_trap() to force a crash) here and
have not been able to find any more crashes.

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/net/netbuff.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/grub-core/net/netbuff.c b/grub-core/net/netbuff.c
index 72e529635..8da327bfd 100644
--- a/grub-core/net/netbuff.c
+++ b/grub-core/net/netbuff.c
@@ -79,10 +79,23 @@ grub_netbuff_alloc (grub_size_t len)
 
   COMPILE_TIME_ASSERT (NETBUFF_ALIGN % sizeof (grub_properly_aligned_t) == 0);
 
+  /*
+   * The largest size of a TCP packet is 64 KiB, and everything else
+   * should be a lot smaller - most MTUs are 1500 or less. Cap data
+   * size at 64 KiB + a buffer.
+   */
+  if (len > 0xffffUL + 0x1000UL)
+    {
+      grub_error (GRUB_ERR_BUG,
+                  "attempted to allocate a packet that is too big");
+      return NULL;
+    }
+
   if (len < NETBUFFMINLEN)
     len = NETBUFFMINLEN;
 
   len = ALIGN_UP (len, NETBUFF_ALIGN);
+
 #ifdef GRUB_MACHINE_EMU
   data = grub_malloc (len + sizeof (*nb));
 #else
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 18/30] net/dns: Fix double-free addresses on corrupt DNS response
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (16 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 17/30] net/netbuff: Block overly large netbuff allocs Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 19/30] net/dns: Don't read past the end of the string we're checking against Daniel Kiper
                   ` (11 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

grub_net_dns_lookup() takes as inputs a pointer to an array of addresses
("addresses") for the given name, and pointer to a number of addresses
("naddresses"). grub_net_dns_lookup() is responsible for allocating
"addresses", and the caller is responsible for freeing it if
"naddresses" > 0.

The DNS recv_hook will sometimes set and free the addresses array,
for example if the packet is too short:

      if (ptr + 10 >= nb->tail)
	{
	  if (!*data->naddresses)
	    grub_free (*data->addresses);
	  grub_netbuff_free (nb);
	  return GRUB_ERR_NONE;
	}

Later on the nslookup command code unconditionally frees the "addresses"
array. Normally this is fine: the array is either populated with valid
data or is NULL. But in these sorts of error cases it is neither NULL
nor valid and we get a double-free.

Only free "addresses" if "naddresses" > 0.

It looks like the other use of grub_net_dns_lookup() is not affected.

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/net/dns.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c
index 27c5f4142..841ede51e 100644
--- a/grub-core/net/dns.c
+++ b/grub-core/net/dns.c
@@ -667,9 +667,11 @@ grub_cmd_nslookup (struct grub_command *cmd __attribute__ ((unused)),
       grub_net_addr_to_str (&addresses[i], buf);
       grub_printf ("%s\n", buf);
     }
-  grub_free (addresses);
   if (naddresses)
-    return GRUB_ERR_NONE;
+    {
+      grub_free (addresses);
+      return GRUB_ERR_NONE;
+    }
   return grub_error (GRUB_ERR_NET_NO_DOMAIN, N_("no DNS record found"));
 }
 
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 19/30] net/dns: Don't read past the end of the string we're checking against
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (17 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 18/30] net/dns: Fix double-free addresses on corrupt DNS response Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 20/30] net/tftp: Prevent a UAF and double-free from a failed seek Daniel Kiper
                   ` (10 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

I don't really understand what's going on here but fuzzing found
a bug where we read past the end of check_with. That's a C string,
so use grub_strlen() to make sure we don't overread it.

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/net/dns.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c
index 841ede51e..afa389494 100644
--- a/grub-core/net/dns.c
+++ b/grub-core/net/dns.c
@@ -146,11 +146,18 @@ check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head,
 		 int *length, char *set)
 {
   const char *readable_ptr = check_with;
+  int readable_len;
   const grub_uint8_t *ptr;
   char *optr = set;
   int bytes_processed = 0;
   if (length)
     *length = 0;
+
+  if (readable_ptr != NULL)
+    readable_len = grub_strlen (readable_ptr);
+  else
+    readable_len = 0;
+
   for (ptr = name_at; ptr < tail && bytes_processed < tail - head + 2; )
     {
       /* End marker.  */
@@ -172,13 +179,16 @@ check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head,
 	  ptr = head + (((ptr[0] & 0x3f) << 8) | ptr[1]);
 	  continue;
 	}
-      if (readable_ptr && grub_memcmp (ptr + 1, readable_ptr, *ptr) != 0)
+      if (readable_ptr != NULL && (*ptr > readable_len || grub_memcmp (ptr + 1, readable_ptr, *ptr) != 0))
 	return 0;
       if (grub_memchr (ptr + 1, 0, *ptr)
 	  || grub_memchr (ptr + 1, '.', *ptr))
 	return 0;
       if (readable_ptr)
-	readable_ptr += *ptr;
+	{
+	  readable_ptr += *ptr;
+	  readable_len -= *ptr;
+	}
       if (readable_ptr && *readable_ptr != '.' && *readable_ptr != 0)
 	return 0;
       bytes_processed += *ptr + 1;
@@ -192,7 +202,10 @@ check_name_real (const grub_uint8_t *name_at, const grub_uint8_t *head,
       if (optr)
 	*optr++ = '.';
       if (readable_ptr && *readable_ptr)
-	readable_ptr++;
+	{
+	  readable_ptr++;
+	  readable_len--;
+	}
       ptr += *ptr + 1;
     }
   return 0;
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 20/30] net/tftp: Prevent a UAF and double-free from a failed seek
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (18 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 19/30] net/dns: Don't read past the end of the string we're checking against Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 21/30] net/tftp: Avoid a trivial UAF Daniel Kiper
                   ` (9 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

A malicious tftp server can cause UAFs and a double free.

An attempt to read from a network file is handled by grub_net_fs_read(). If
the read is at an offset other than the current offset, grub_net_seek_real()
is invoked.

In grub_net_seek_real(), if a backwards seek cannot be satisfied from the
currently received packets, and the underlying transport does not provide
a seek method, then grub_net_seek_real() will close and reopen the network
protocol layer.

For tftp, the ->close() call goes to tftp_close() and frees the tftp_data_t
file->data. The file->data pointer is not nulled out after the free.

If the ->open() call fails, the file->data will not be reallocated and will
continue point to a freed memory block. This could happen from a server
refusing to send the requisite ack to the new tftp request, for example.

The seek and the read will then fail, but the grub_file continues to exist:
the failed seek does not necessarily cause the entire file to be thrown
away (e.g. where the file is checked to see if it is gzipped/lzio/xz/etc.,
a read failure is interpreted as a decompressor passing on the file, not as
an invalidation of the entire grub_file_t structure).

This means subsequent attempts to read or seek the file will use the old
file->data after free. Eventually, the file will be close()d again and
file->data will be freed again.

Mark a net_fs file that doesn't reopen as broken. Do not permit read() or
close() on a broken file (seek is not exposed directly to the file API -
it is only called as part of read, so this blocks seeks as well).

As an additional defence, null out the ->data pointer if tftp_open() fails.
That would have lead to a simple null pointer dereference rather than
a mess of UAFs.

This may affect other protocols, I haven't checked.

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/net/net.c  | 11 +++++++++--
 grub-core/net/tftp.c |  1 +
 include/grub/net.h   |  1 +
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/grub-core/net/net.c b/grub-core/net/net.c
index 2b6771523..9f09f8e48 100644
--- a/grub-core/net/net.c
+++ b/grub-core/net/net.c
@@ -1521,7 +1521,8 @@ grub_net_fs_close (grub_file_t file)
       grub_netbuff_free (file->device->net->packs.first->nb);
       grub_net_remove_packet (file->device->net->packs.first);
     }
-  file->device->net->protocol->close (file);
+  if (!file->device->net->broken)
+    file->device->net->protocol->close (file);
   grub_free (file->device->net->name);
   return GRUB_ERR_NONE;
 }
@@ -1744,7 +1745,10 @@ grub_net_seek_real (struct grub_file *file, grub_off_t offset)
     file->device->net->stall = 0;
     err = file->device->net->protocol->open (file, file->device->net->name);
     if (err)
-      return err;
+      {
+	file->device->net->broken = 1;
+	return err;
+      }
     grub_net_fs_read_real (file, NULL, offset);
     return grub_errno;
   }
@@ -1753,6 +1757,9 @@ grub_net_seek_real (struct grub_file *file, grub_off_t offset)
 static grub_ssize_t
 grub_net_fs_read (grub_file_t file, char *buf, grub_size_t len)
 {
+  if (file->device->net->broken)
+    return -1;
+
   if (file->offset != file->device->net->offset)
     {
       grub_err_t err;
diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c
index ebbafe7a1..ee305e18a 100644
--- a/grub-core/net/tftp.c
+++ b/grub-core/net/tftp.c
@@ -400,6 +400,7 @@ tftp_open (struct grub_file *file, const char *filename)
     {
       grub_net_udp_close (data->sock);
       grub_free (data);
+      file->data = NULL;
       return grub_errno;
     }
 
diff --git a/include/grub/net.h b/include/grub/net.h
index db21e792f..a64a04cc8 100644
--- a/include/grub/net.h
+++ b/include/grub/net.h
@@ -276,6 +276,7 @@ typedef struct grub_net
   grub_fs_t fs;
   int eof;
   int stall;
+  int broken;
 } *grub_net_t;
 
 extern grub_net_t (*EXPORT_VAR (grub_net_open)) (const char *name);
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 21/30] net/tftp: Avoid a trivial UAF
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (19 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 20/30] net/tftp: Prevent a UAF and double-free from a failed seek Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 22/30] net/http: Do not tear down socket if it's already been torn down Daniel Kiper
                   ` (8 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

Under tftp errors, we print a tftp error message from the tftp header.
However, the tftph pointer is a pointer inside nb, the netbuff. Previously,
we were freeing the nb and then dereferencing it. Don't do that, use it
and then free it later.

This isn't really _bad_ per se, especially as we're single-threaded, but
it trips up fuzzers.

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/net/tftp.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c
index ee305e18a..7dbd3056d 100644
--- a/grub-core/net/tftp.c
+++ b/grub-core/net/tftp.c
@@ -251,9 +251,9 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)),
       return GRUB_ERR_NONE;
     case TFTP_ERROR:
       data->have_oack = 1;
-      grub_netbuff_free (nb);
       grub_error (GRUB_ERR_IO, "%s", tftph->u.err.errmsg);
       grub_error_save (&data->save_err);
+      grub_netbuff_free (nb);
       return GRUB_ERR_NONE;
     default:
       grub_netbuff_free (nb);
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 22/30] net/http: Do not tear down socket if it's already been torn down
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (20 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 21/30] net/tftp: Avoid a trivial UAF Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 23/30] net/http: Fix OOB write for split http headers Daniel Kiper
                   ` (7 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

It's possible for data->sock to get torn down in tcp error handling.
If we unconditionally tear it down again we will end up doing writes
to an offset of the NULL pointer when we go to tear it down again.

Detect if it has been torn down and don't do it again.

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/net/http.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/grub-core/net/http.c b/grub-core/net/http.c
index 8d6c62c76..f8d7bf0cd 100644
--- a/grub-core/net/http.c
+++ b/grub-core/net/http.c
@@ -445,7 +445,7 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial)
       return err;
     }
 
-  for (i = 0; !data->headers_recv && i < 100; i++)
+  for (i = 0; data->sock && !data->headers_recv && i < 100; i++)
     {
       grub_net_tcp_retransmit ();
       grub_net_poll_cards (300, &data->headers_recv);
@@ -453,7 +453,8 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial)
 
   if (!data->headers_recv)
     {
-      grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
+      if (data->sock)
+        grub_net_tcp_close (data->sock, GRUB_NET_TCP_ABORT);
       if (data->err)
 	{
 	  char *str = data->errmsg;
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 23/30] net/http: Fix OOB write for split http headers
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (21 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 22/30] net/http: Do not tear down socket if it's already been torn down Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 24/30] net/http: Error out on headers with LF without CR Daniel Kiper
                   ` (6 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

GRUB has special code for handling an http header that is split
across two packets.

The code tracks the end of line by looking for a "\n" byte. The
code for split headers has always advanced the pointer just past the
end of the line, whereas the code that handles unsplit headers does
not advance the pointer. This extra advance causes the length to be
one greater, which breaks an assumption in parse_line(), leading to
it writing a NUL byte one byte past the end of the buffer where we
reconstruct the line from the two packets.

It's conceivable that an attacker controlled set of packets could
cause this to zero out the first byte of the "next" pointer of the
grub_mm_region structure following the current_line buffer.

Do not advance the pointer in the split header case.

Fixes: CVE-2022-28734

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/net/http.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/grub-core/net/http.c b/grub-core/net/http.c
index f8d7bf0cd..33a0a28c4 100644
--- a/grub-core/net/http.c
+++ b/grub-core/net/http.c
@@ -190,9 +190,7 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)),
 	  int have_line = 1;
 	  char *t;
 	  ptr = grub_memchr (nb->data, '\n', nb->tail - nb->data);
-	  if (ptr)
-	    ptr++;
-	  else
+	  if (ptr == NULL)
 	    {
 	      have_line = 0;
 	      ptr = (char *) nb->tail;
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 24/30] net/http: Error out on headers with LF without CR
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (22 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 23/30] net/http: Fix OOB write for split http headers Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 25/30] fs/f2fs: Do not read past the end of nat journal entries Daniel Kiper
                   ` (5 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Daniel Axtens <dja@axtens.net>

In a similar vein to the previous patch, parse_line() would write
a NUL byte past the end of the buffer if there was an HTTP header
with a LF rather than a CRLF.

RFC-2616 says:

  Many HTTP/1.1 header field values consist of words separated by LWS
  or special characters. These special characters MUST be in a quoted
  string to be used within a parameter value (as defined in section 3.6).

We don't support quoted sections or continuation lines, etc.

If we see an LF that's not part of a CRLF, bail out.

Fixes: CVE-2022-28734

Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/net/http.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/grub-core/net/http.c b/grub-core/net/http.c
index 33a0a28c4..9291a13e2 100644
--- a/grub-core/net/http.c
+++ b/grub-core/net/http.c
@@ -68,7 +68,15 @@ parse_line (grub_file_t file, http_data_t data, char *ptr, grub_size_t len)
   char *end = ptr + len;
   while (end > ptr && *(end - 1) == '\r')
     end--;
+
+  /* LF without CR. */
+  if (end == ptr + len)
+    {
+      data->errmsg = grub_strdup (_("invalid HTTP header - LF without CR"));
+      return GRUB_ERR_NONE;
+    }
   *end = 0;
+
   /* Trailing CRLF.  */
   if (data->in_chunk_len == 1)
     {
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 25/30] fs/f2fs: Do not read past the end of nat journal entries
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (23 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 24/30] net/http: Error out on headers with LF without CR Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 26/30] fs/f2fs: Do not read past the end of nat bitmap Daniel Kiper
                   ` (4 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>

A corrupt f2fs file system could specify a nat journal entry count
that is beyond the maximum NAT_JOURNAL_ENTRIES.

Check if the specified nat journal entry count before accessing the
array, and throw an error if it is too large.

Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/fs/f2fs.c | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c
index 8a9992ca9..63702214b 100644
--- a/grub-core/fs/f2fs.c
+++ b/grub-core/fs/f2fs.c
@@ -632,23 +632,27 @@ get_nat_journal (struct grub_f2fs_data *data)
   return err;
 }
 
-static grub_uint32_t
-get_blkaddr_from_nat_journal (struct grub_f2fs_data *data, grub_uint32_t nid)
+static grub_err_t
+get_blkaddr_from_nat_journal (struct grub_f2fs_data *data, grub_uint32_t nid,
+                              grub_uint32_t *blkaddr)
 {
   grub_uint16_t n = grub_le_to_cpu16 (data->nat_j.n_nats);
-  grub_uint32_t blkaddr = 0;
   grub_uint16_t i;
 
+  if (n >= NAT_JOURNAL_ENTRIES)
+    return grub_error (GRUB_ERR_BAD_FS,
+                       "invalid number of nat journal entries");
+
   for (i = 0; i < n; i++)
     {
       if (grub_le_to_cpu32 (data->nat_j.entries[i].nid) == nid)
         {
-          blkaddr = grub_le_to_cpu32 (data->nat_j.entries[i].ne.block_addr);
+          *blkaddr = grub_le_to_cpu32 (data->nat_j.entries[i].ne.block_addr);
           break;
         }
     }
 
-  return blkaddr;
+  return GRUB_ERR_NONE;
 }
 
 static grub_uint32_t
@@ -656,10 +660,13 @@ get_node_blkaddr (struct grub_f2fs_data *data, grub_uint32_t nid)
 {
   struct grub_f2fs_nat_block *nat_block;
   grub_uint32_t seg_off, block_off, entry_off, block_addr;
-  grub_uint32_t blkaddr;
+  grub_uint32_t blkaddr = 0;
   grub_err_t err;
 
-  blkaddr = get_blkaddr_from_nat_journal (data, nid);
+  err = get_blkaddr_from_nat_journal (data, nid, &blkaddr);
+  if (err != GRUB_ERR_NONE)
+    return 0;
+
   if (blkaddr)
     return blkaddr;
 
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 26/30] fs/f2fs: Do not read past the end of nat bitmap
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (24 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 25/30] fs/f2fs: Do not read past the end of nat journal entries Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 27/30] fs/f2fs: Do not copy file names that are too long Daniel Kiper
                   ` (3 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>

A corrupt f2fs filesystem could have a block offset or a bitmap
offset that would cause us to read beyond the bounds of the nat
bitmap.

Introduce the nat_bitmap_size member in grub_f2fs_data which holds
the size of nat bitmap.

Set the size when loading the nat bitmap in nat_bitmap_ptr(), and
catch when an invalid offset would create a pointer past the end of
the allocated space.

Check against the bitmap size in grub_f2fs_test_bit() test bit to avoid
reading past the end of the nat bitmap.

Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/fs/f2fs.c | 33 +++++++++++++++++++++++++++------
 1 file changed, 27 insertions(+), 6 deletions(-)

diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c
index 63702214b..8898b235e 100644
--- a/grub-core/fs/f2fs.c
+++ b/grub-core/fs/f2fs.c
@@ -122,6 +122,7 @@ GRUB_MOD_LICENSE ("GPLv3+");
 #define F2FS_INLINE_DOTS          0x10  /* File having implicit dot dentries. */
 
 #define MAX_VOLUME_NAME           512
+#define MAX_NAT_BITMAP_SIZE       3900
 
 enum FILE_TYPE
 {
@@ -183,7 +184,7 @@ struct grub_f2fs_checkpoint
   grub_uint32_t                   checksum_offset;
   grub_uint64_t                   elapsed_time;
   grub_uint8_t                    alloc_type[MAX_ACTIVE_LOGS];
-  grub_uint8_t                    sit_nat_version_bitmap[3900];
+  grub_uint8_t                    sit_nat_version_bitmap[MAX_NAT_BITMAP_SIZE];
   grub_uint32_t                   checksum;
 } GRUB_PACKED;
 
@@ -302,6 +303,7 @@ struct grub_f2fs_data
 
   struct grub_f2fs_nat_journal    nat_j;
   char                            *nat_bitmap;
+  grub_uint32_t                   nat_bitmap_size;
 
   grub_disk_t                     disk;
   struct grub_f2fs_node           *inode;
@@ -377,15 +379,20 @@ sum_blk_addr (struct grub_f2fs_data *data, int base, int type)
 }
 
 static void *
-nat_bitmap_ptr (struct grub_f2fs_data *data)
+nat_bitmap_ptr (struct grub_f2fs_data *data, grub_uint32_t *nat_bitmap_size)
 {
   struct grub_f2fs_checkpoint *ckpt = &data->ckpt;
   grub_uint32_t offset;
+  *nat_bitmap_size = MAX_NAT_BITMAP_SIZE;
 
   if (grub_le_to_cpu32 (data->sblock.cp_payload) > 0)
     return ckpt->sit_nat_version_bitmap;
 
   offset = grub_le_to_cpu32 (ckpt->sit_ver_bitmap_bytesize);
+  if (offset >= MAX_NAT_BITMAP_SIZE)
+     return NULL;
+
+  *nat_bitmap_size = *nat_bitmap_size - offset;
 
   return ckpt->sit_nat_version_bitmap + offset;
 }
@@ -438,11 +445,15 @@ grub_f2fs_crc_valid (grub_uint32_t blk_crc, void *buf, const grub_uint32_t len)
 }
 
 static int
-grub_f2fs_test_bit (grub_uint32_t nr, const char *p)
+grub_f2fs_test_bit (grub_uint32_t nr, const char *p, grub_uint32_t len)
 {
   int mask;
+  grub_uint32_t shifted_nr = (nr >> 3);
 
-  p += (nr >> 3);
+  if (shifted_nr >= len)
+    return -1;
+
+  p += shifted_nr;
   mask = 1 << (7 - (nr & 0x07));
 
   return mask & *p;
@@ -662,6 +673,7 @@ get_node_blkaddr (struct grub_f2fs_data *data, grub_uint32_t nid)
   grub_uint32_t seg_off, block_off, entry_off, block_addr;
   grub_uint32_t blkaddr = 0;
   grub_err_t err;
+  int result_bit;
 
   err = get_blkaddr_from_nat_journal (data, nid, &blkaddr);
   if (err != GRUB_ERR_NONE)
@@ -682,8 +694,15 @@ get_node_blkaddr (struct grub_f2fs_data *data, grub_uint32_t nid)
         ((seg_off * data->blocks_per_seg) << 1) +
         (block_off & (data->blocks_per_seg - 1));
 
-  if (grub_f2fs_test_bit (block_off, data->nat_bitmap))
+  result_bit = grub_f2fs_test_bit (block_off, data->nat_bitmap,
+                                   data->nat_bitmap_size);
+  if (result_bit > 0)
     block_addr += data->blocks_per_seg;
+  else if (result_bit == -1)
+    {
+      grub_free (nat_block);
+      return 0;
+    }
 
   err = grub_f2fs_block_read (data, block_addr, nat_block);
   if (err)
@@ -833,7 +852,9 @@ grub_f2fs_mount (grub_disk_t disk)
   if (err)
     goto fail;
 
-  data->nat_bitmap = nat_bitmap_ptr (data);
+  data->nat_bitmap = nat_bitmap_ptr (data, &data->nat_bitmap_size);
+  if (data->nat_bitmap == NULL)
+    goto fail;
 
   err = get_nat_journal (data);
   if (err)
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 27/30] fs/f2fs: Do not copy file names that are too long
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (25 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 26/30] fs/f2fs: Do not read past the end of nat bitmap Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 28/30] fs/btrfs: Fix several fuzz issues with invalid dir item sizing Daniel Kiper
                   ` (2 subsequent siblings)
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>

A corrupt f2fs file system might specify a name length which is greater
than the maximum name length supported by the GRUB f2fs driver.

We will allocate enough memory to store the overly long name, but there
are only F2FS_NAME_LEN bytes in the source, so we would read past the end
of the source.

While checking directory entries, do not copy a file name with an invalid
length.

Signed-off-by: Sudhakar Kuppusamy <sudhakar@linux.ibm.com>
Signed-off-by: Daniel Axtens <dja@axtens.net>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/fs/f2fs.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/grub-core/fs/f2fs.c b/grub-core/fs/f2fs.c
index 8898b235e..df6beb544 100644
--- a/grub-core/fs/f2fs.c
+++ b/grub-core/fs/f2fs.c
@@ -1003,6 +1003,10 @@ grub_f2fs_check_dentries (struct grub_f2fs_dir_iter_ctx *ctx)
 
       ftype = ctx->dentry[i].file_type;
       name_len = grub_le_to_cpu16 (ctx->dentry[i].name_len);
+
+      if (name_len >= F2FS_NAME_LEN)
+        return 0;
+
       filename = grub_malloc (name_len + 1);
       if (!filename)
         return 0;
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 28/30] fs/btrfs: Fix several fuzz issues with invalid dir item sizing
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (26 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 27/30] fs/f2fs: Do not copy file names that are too long Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 29/30] fs/btrfs: Fix more ASAN and SEGV issues found with fuzzing Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 30/30] fs/btrfs: Fix more fuzz issues related to chunks Daniel Kiper
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Darren Kenny <darren.kenny@oracle.com>

According to the btrfs code in Linux, the structure of a directory item
leaf should be of the form:

  |struct btrfs_dir_item|name|data|

in GRUB the name len and data len are in the grub_btrfs_dir_item
structure's n and m fields respectively.

The combined size of the structure, name and data should be less than
the allocated memory, a difference to the Linux kernel's struct
btrfs_dir_item is that the grub_btrfs_dir_item has an extra field for
where the name is stored, so we adjust for that too.

Signed-off-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/fs/btrfs.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 937153306..139c6616d 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -1991,6 +1991,7 @@ grub_btrfs_dir (grub_device_t device, const char *path,
   int r = 0;
   grub_uint64_t tree;
   grub_uint8_t type;
+  grub_size_t est_size = 0;
 
   if (!data)
     return grub_errno;
@@ -2049,6 +2050,18 @@ grub_btrfs_dir (grub_device_t device, const char *path,
 	  break;
 	}
 
+      if (direl == NULL ||
+	  grub_add (grub_le_to_cpu16 (direl->n),
+		    grub_le_to_cpu16 (direl->m), &est_size) ||
+	  grub_add (est_size, sizeof (*direl), &est_size) ||
+	  grub_sub (est_size, sizeof (direl->name), &est_size) ||
+	  est_size > allocated)
+       {
+         grub_errno = GRUB_ERR_OUT_OF_RANGE;
+         r = -grub_errno;
+         goto out;
+       }
+
       for (cdirel = direl;
 	   (grub_uint8_t *) cdirel - (grub_uint8_t *) direl
 	   < (grub_ssize_t) elemsize;
@@ -2059,6 +2072,19 @@ grub_btrfs_dir (grub_device_t device, const char *path,
 	  char c;
 	  struct grub_btrfs_inode inode;
 	  struct grub_dirhook_info info;
+
+	  if (cdirel == NULL ||
+	      grub_add (grub_le_to_cpu16 (cdirel->n),
+			grub_le_to_cpu16 (cdirel->m), &est_size) ||
+	      grub_add (est_size, sizeof (*cdirel), &est_size) ||
+	      grub_sub (est_size, sizeof (cdirel->name), &est_size) ||
+	      est_size > allocated)
+	   {
+	     grub_errno = GRUB_ERR_OUT_OF_RANGE;
+	     r = -grub_errno;
+	     goto out;
+	   }
+
 	  err = grub_btrfs_read_inode (data, &inode, cdirel->key.object_id,
 				       tree);
 	  grub_memset (&info, 0, sizeof (info));
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 29/30] fs/btrfs: Fix more ASAN and SEGV issues found with fuzzing
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (27 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 28/30] fs/btrfs: Fix several fuzz issues with invalid dir item sizing Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  2022-06-07 17:01 ` [SECURITY PATCH 30/30] fs/btrfs: Fix more fuzz issues related to chunks Daniel Kiper
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Darren Kenny <darren.kenny@oracle.com>

The fuzzer is generating btrfs file systems that have chunks with
invalid combinations of stripes and substripes for the given RAID
configurations.

After examining the Linux kernel fs/btrfs/tree-checker.c code, it
appears that sub-stripes should only be applied to RAID10, and in that
case there should only ever be 2 of them.

Similarly, RAID single should only have 1 stripe, and RAID1/1C3/1C4
should have 2. 3 or 4 stripes respectively, which is what redundancy
corresponds.

Some of the chunks ended up with a size of 0, which grub_malloc() still
returned memory for and in turn generated ASAN errors later when
accessed.

While it would be possible to specifically limit the number of stripes,
a more correct test was on the combination of the chunk item, and the
number of stripes by the size of the chunk stripe structure in
comparison to the size of the chunk itself.

Signed-off-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/fs/btrfs.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 139c6616d..73b230632 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -912,6 +912,12 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 	return grub_error (GRUB_ERR_BAD_FS,
 			   "couldn't find the chunk descriptor");
 
+      if (!chsize)
+	{
+	  grub_dprintf ("btrfs", "zero-size chunk\n");
+	  return grub_error (GRUB_ERR_BAD_FS,
+			     "got an invalid zero-size chunk");
+	}
       chunk = grub_malloc (chsize);
       if (!chunk)
 	return grub_errno;
@@ -970,6 +976,16 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 	      stripe_length = grub_divmod64 (grub_le_to_cpu64 (chunk->size),
 					     nstripes,
 					     NULL);
+
+	      /* For single, there should be exactly 1 stripe. */
+	      if (grub_le_to_cpu16 (chunk->nstripes) != 1)
+		{
+		  grub_dprintf ("btrfs", "invalid RAID_SINGLE: nstripes != 1 (%u)\n",
+				grub_le_to_cpu16 (chunk->nstripes));
+		  return grub_error (GRUB_ERR_BAD_FS,
+				     "invalid RAID_SINGLE: nstripes != 1 (%u)",
+				      grub_le_to_cpu16 (chunk->nstripes));
+		}
 	      if (stripe_length == 0)
 		stripe_length = 512;
 	      stripen = grub_divmod64 (off, stripe_length, &stripe_offset);
@@ -989,6 +1005,19 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 	      stripen = 0;
 	      stripe_offset = off;
 	      csize = grub_le_to_cpu64 (chunk->size) - off;
+
+             /*
+	      * Redundancy, and substripes only apply to RAID10, and there
+	      * should be exactly 2 sub-stripes.
+	      */
+	     if (grub_le_to_cpu16 (chunk->nstripes) != redundancy)
+               {
+                 grub_dprintf ("btrfs", "invalid RAID1: nstripes != %u (%u)\n",
+                               redundancy, grub_le_to_cpu16 (chunk->nstripes));
+                 return grub_error (GRUB_ERR_BAD_FS,
+                                    "invalid RAID1: nstripes != %u (%u)",
+                                    redundancy, grub_le_to_cpu16 (chunk->nstripes));
+               }
 	      break;
 	    }
 	  case GRUB_BTRFS_CHUNK_TYPE_RAID0:
@@ -1025,6 +1054,20 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 	      stripe_offset = low + chunk_stripe_length
 		* high;
 	      csize = chunk_stripe_length - low;
+
+	      /*
+	       * Substripes only apply to RAID10, and there
+	       * should be exactly 2 sub-stripes.
+	       */
+	      if (grub_le_to_cpu16 (chunk->nsubstripes) != 2)
+		{
+		  grub_dprintf ("btrfs", "invalid RAID10: nsubstripes != 2 (%u)",
+				grub_le_to_cpu16 (chunk->nsubstripes));
+		  return grub_error (GRUB_ERR_BAD_FS,
+				     "invalid RAID10: nsubstripes != 2 (%u)",
+				     grub_le_to_cpu16 (chunk->nsubstripes));
+		}
+
 	      break;
 	    }
 	  case GRUB_BTRFS_CHUNK_TYPE_RAID5:
@@ -1124,6 +1167,8 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 
 	for (j = 0; j < 2; j++)
 	  {
+	    grub_size_t est_chunk_alloc = 0;
+
 	    grub_dprintf ("btrfs", "chunk 0x%" PRIxGRUB_UINT64_T
 			  "+0x%" PRIxGRUB_UINT64_T
 			  " (%d stripes (%d substripes) of %"
@@ -1136,6 +1181,16 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 	    grub_dprintf ("btrfs", "reading laddr 0x%" PRIxGRUB_UINT64_T "\n",
 			  addr);
 
+	    if (grub_mul (sizeof (struct grub_btrfs_chunk_stripe),
+			  grub_le_to_cpu16 (chunk->nstripes), &est_chunk_alloc) ||
+		grub_add (est_chunk_alloc,
+			  sizeof (struct grub_btrfs_chunk_item), &est_chunk_alloc) ||
+		est_chunk_alloc > chunk->size)
+	      {
+		err = GRUB_ERR_BAD_FS;
+		break;
+	      }
+
 	    if (is_raid56)
 	      {
 		err = btrfs_read_from_chunk (data, chunk, stripen,
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [SECURITY PATCH 30/30] fs/btrfs: Fix more fuzz issues related to chunks
  2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
                   ` (28 preceding siblings ...)
  2022-06-07 17:01 ` [SECURITY PATCH 29/30] fs/btrfs: Fix more ASAN and SEGV issues found with fuzzing Daniel Kiper
@ 2022-06-07 17:01 ` Daniel Kiper
  29 siblings, 0 replies; 31+ messages in thread
From: Daniel Kiper @ 2022-06-07 17:01 UTC (permalink / raw)
  To: grub-devel

From: Darren Kenny <darren.kenny@oracle.com>

The corpus was generating issues in grub_btrfs_read_logical() when
attempting to iterate over stripe entries in the superblock's
bootmapping.

In most cases the reason for the failure was that the number of stripes
in chunk->nstripes exceeded the possible space statically allocated in
superblock bootmapping space. Each stripe entry in the bootmapping block
consists of a grub_btrfs_key followed by a grub_btrfs_chunk_stripe.

Another issue that came up was that while calculating the chunk size,
in an earlier piece of code in that function, depending on the data
provided in the btrfs file system, it would end up calculating a size
that was too small to contain even 1 grub_btrfs_chunk_item, which is
obviously invalid too.

Signed-off-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/fs/btrfs.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 73b230632..ec72f7be3 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -918,6 +918,17 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 	  return grub_error (GRUB_ERR_BAD_FS,
 			     "got an invalid zero-size chunk");
 	}
+
+      /*
+       * The space being allocated for a chunk should at least be able to
+       * contain one chunk item.
+       */
+      if (chsize < sizeof (struct grub_btrfs_chunk_item))
+       {
+         grub_dprintf ("btrfs", "chunk-size too small\n");
+         return grub_error (GRUB_ERR_BAD_FS,
+                            "got an invalid chunk size");
+       }
       chunk = grub_malloc (chsize);
       if (!chunk)
 	return grub_errno;
@@ -1165,6 +1176,13 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 	if (csize > (grub_uint64_t) size)
 	  csize = size;
 
+	/*
+	 * The space for a chunk stripe is limited to the space provide in the super-block's
+	 * bootstrap mapping with an initial btrfs key at the start of each chunk.
+	 */
+	grub_size_t avail_stripes = sizeof (data->sblock.bootstrap_mapping) /
+	  (sizeof (struct grub_btrfs_key) + sizeof (struct grub_btrfs_chunk_stripe));
+
 	for (j = 0; j < 2; j++)
 	  {
 	    grub_size_t est_chunk_alloc = 0;
@@ -1191,6 +1209,12 @@ grub_btrfs_read_logical (struct grub_btrfs_data *data, grub_disk_addr_t addr,
 		break;
 	      }
 
+	   if (grub_le_to_cpu16 (chunk->nstripes) > avail_stripes)
+             {
+               err = GRUB_ERR_BAD_FS;
+               break;
+             }
+
 	    if (is_raid56)
 	      {
 		err = btrfs_read_from_chunk (data, chunk, stripen,
-- 
2.11.0



^ permalink raw reply related	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2022-06-07 17:03 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-07 17:00 [SECURITY PATCH 00/30] Multiple GRUB2 vulnerabilities - 2022/06/07 round Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 01/30] loader/efi/chainloader: Simplify the loader state Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 02/30] commands/boot: Add API to pass context to loader Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 03/30] loader/efi/chainloader: Use grub_loader_set_ex() Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 04/30] kern/efi/sb: Reject non-kernel files in the shim_lock verifier Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 05/30] kern/file: Do not leak device_name on error in grub_file_open() Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 06/30] video/readers/png: Abort sooner if a read operation fails Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 07/30] video/readers/png: Refuse to handle multiple image headers Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 08/30] video/readers/png: Drop greyscale support to fix heap out-of-bounds write Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 09/30] video/readers/png: Avoid heap OOB R/W inserting huff table items Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 10/30] video/readers/png: Sanity check some huffman codes Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 11/30] video/readers/jpeg: Abort sooner if a read operation fails Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 12/30] video/readers/jpeg: Do not reallocate a given huff table Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 13/30] video/readers/jpeg: Refuse to handle multiple start of streams Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 14/30] video/readers/jpeg: Block int underflow -> wild pointer write Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 15/30] normal/charset: Fix array out-of-bounds formatting unicode for display Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 16/30] net/ip: Do IP fragment maths safely Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 17/30] net/netbuff: Block overly large netbuff allocs Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 18/30] net/dns: Fix double-free addresses on corrupt DNS response Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 19/30] net/dns: Don't read past the end of the string we're checking against Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 20/30] net/tftp: Prevent a UAF and double-free from a failed seek Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 21/30] net/tftp: Avoid a trivial UAF Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 22/30] net/http: Do not tear down socket if it's already been torn down Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 23/30] net/http: Fix OOB write for split http headers Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 24/30] net/http: Error out on headers with LF without CR Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 25/30] fs/f2fs: Do not read past the end of nat journal entries Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 26/30] fs/f2fs: Do not read past the end of nat bitmap Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 27/30] fs/f2fs: Do not copy file names that are too long Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 28/30] fs/btrfs: Fix several fuzz issues with invalid dir item sizing Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 29/30] fs/btrfs: Fix more ASAN and SEGV issues found with fuzzing Daniel Kiper
2022-06-07 17:01 ` [SECURITY PATCH 30/30] fs/btrfs: Fix more fuzz issues related to chunks Daniel Kiper

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.