All of lore.kernel.org
 help / color / mirror / Atom feed
* [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole
@ 2020-07-29 17:00 Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 01/28] yylex: Make lexer fatal errors actually be fatal Daniel Kiper
                   ` (28 more replies)
  0 siblings, 29 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

Hi all,

We have recently been made aware of a problem with GRUB2 by security research
firm Eclypsium that allows a bad actor to circumvent UEFI Secure Boot. Normally,
when Secure Boot is enabled, only modules [1] that have a valid signature can
be loaded. The bug allows this to be circumvented and allow a module to be
loaded that is not signed and therefore breaks the chain of trust that Secure
Boot is supposed to guarantee.

The issue has got assigned following CVE and score:
  CVE-2020-10713, 8.2/CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H

This is the original flaw discovered by Eclypsium, also known as "BootHole" and
is described in Eclypsium's paper [2].

In the deeper analysis prompted by by that bug we have found the additional bugs:
  - CVE-2020-14308, 6.4/CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H
    grub2: grub_malloc does not validate allocation size allowing for
    arithmetic overflow and subsequent heap-based buffer overflow,

  - CVE-2020-14309, 5.7/CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:H/A:H
    grub2: Integer overflow in grub_squash_read_symlink may lead to
    heap based overflow,

  - CVE-2020-14310, 5.7/CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:H/A:H
    grub2: Integer overflow read_section_from_string may lead to heap
    based overflow,

  - CVE-2020-14311, 5.7/CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:H/A:H
    grub2: Integer overflow in grub_ext2_read_link leads to heap based
    buffer overflow,

  - CVE-2020-15705, 6.4/CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H
    grub2: Avoid loading unsigned kernels when grub is booted directly
    under secureboot without shim (this is distros specific issue and
    does not apply to the GRUB2 upstream),

  - CVE-2020-15706, 6.4/CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H
    script: Avoid a use-after-free when redefining a function during execution,

  - CVE-2020-15707, 5.7/CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:H/A:H
    grub2: Integer overflow in initrd size handling.

Mitigation of these bugs will involve not just a new version of GRUB2 for all
the affected platforms but may also require a new shim or a new kernel or both.
Details of exactly what needs updating will be provided by the respective
distros and vendors when updates become available. Here [3] we are listing at
least some links to the messaging known at the time of this posting.

At some stage, the UEFI revocation list (dbx) on new hardware will be updated
so that today's kernels will not boot on the new hardware. Full mitigation
against the CVE-2020-10713 will require an updated dbx which, in at least some
cases, will not allow Secure Boot with today's kernels. Vendor shims may
explicitly permit known older kernels to boot.

Updated GRUB2, shim and kernels from all the affected vendors will be made
available when the embargo lifts or shortly thereafter. An updated dbx from
the various affected vendors will also ship, although possibly not at the same
time. The new Microsoft dbx will be provided for download here [4].

I am posting all the GRUB2 upstream patches which fixes 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 [5] too.

The initial issue was discovered and reported by Mickey Shkatov and Jesse Michael,
both working for Eclypsium.

In particular I would like to thank, in alphabetical order, the following people
who were working really hard on the GRUB, kernel, shim, legal, organizational
and other stuff related to these issues:
  - Alexander Burmashev (Oracle),
  - Alexey Makhalov (VMware),
  - Chris Coulson (Canonical),
  - Cliff Perry (Red Hat),
  - Colin Watson (Debian),
  - Darren Kenny (Oracle),
  - Darren Moffat (Oracle),
  - Dave Miner (Oracle),
  - Derek Granito (Microsoft),
  - Dimitri John Ledkov (Canonical),
  - Eric Snowberg (Oracle),
  - Ilya Okomin (Oracle),
  - Jan Setje-Eilers (Oracle),
  - Jeremiah Cox (Microsoft),
  - Jesse Michael (Eclypsium),
  - John Haxby (Oracle),
  - Kanth Ghatraju (Oracle),
  - Konrad Rzeszutek Wilk (Oracle),
  - Marco Benatto (Red Hat),
  - Mickey Shkatov (Eclypsium),
  - Peter Jones (Red Hat),
  - Sarah Jacobus (Microsoft),
  - Steve McIntyre (Debian),
  - Todd Vierling (Oracle).

Without you all hard work and late hours this joint community work would not
have been possible.

I am proud to be working with you all and thank you.

Daniel

[1] "Modules" used here is a catch-all for things that are loaded and
    covers everything from UEFI applications all the way up through the
    platform's kernel and any drivers that it may load.  Different loaders
    have different ways of checking signatures but there is a chain of
    trust reaching right back to the origin where Secure Boot is first
    enabled.

[2] https://www.eclypsium.com/2020/07/29/theres-a-hole-in-the-boot/

[3] Canonical: https://ubuntu.com/security/notices/USN-4432-1
    Debian:    https://www.debian.org/security/2020-GRUB-UEFI-SecureBoot
    Microsoft: https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/ADV200011
    Red Hat:   https://access.redhat.com/security/vulnerabilities/grub2bootloader
    SUSE:      https://www.suse.com/c/suse-addresses-grub2-secure-boot-issue/
               https://www.suse.com/support/kb/doc/?id=000019673
    VMware:    https://kb.vmware.com/s/article/80181

[4] https://uefi.org/revocationlistfile

[5] https://git.savannah.gnu.org/gitweb/?p=grub.git&view=view+git+repository
    https://git.savannah.gnu.org/git/grub.git

 INSTALL                                   |  22 +---
 grub-core/bus/usb/usbhub.c                |   8 +-
 grub-core/commands/efi/lsefisystab.c      |   3 +-
 grub-core/commands/legacycfg.c            |  35 +++++--
 grub-core/commands/menuentry.c            |   2 +-
 grub-core/commands/nativedisk.c           |   2 +-
 grub-core/commands/parttool.c             |  12 ++-
 grub-core/commands/regexp.c               |   2 +-
 grub-core/commands/search_wrap.c          |   2 +-
 grub-core/commands/wildcard.c             |  36 ++++++-
 grub-core/disk/diskfilter.c               |   4 +-
 grub-core/disk/ieee1275/ofdisk.c          |   2 +-
 grub-core/disk/ldm.c                      |  46 +++++---
 grub-core/disk/luks.c                     |   2 +-
 grub-core/disk/lvm.c                      |  60 ++++++++---
 grub-core/disk/xen/xendisk.c              |   2 +-
 grub-core/efiemu/loadcore.c               |   2 +-
 grub-core/efiemu/mm.c                     |   6 +-
 grub-core/font/font.c                     |  16 ++-
 grub-core/fs/affs.c                       |   6 +-
 grub-core/fs/btrfs.c                      |  34 +++---
 grub-core/fs/ext2.c                       |  10 +-
 grub-core/fs/hfs.c                        |   2 +-
 grub-core/fs/hfsplus.c                    |  17 +--
 grub-core/fs/iso9660.c                    |  73 +++++++++----
 grub-core/fs/ntfs.c                       |   4 +-
 grub-core/fs/sfs.c                        |  29 ++++--
 grub-core/fs/squash4.c                    |  45 ++++++--
 grub-core/fs/tar.c                        |   2 +-
 grub-core/fs/udf.c                        |  62 +++++++----
 grub-core/fs/xfs.c                        |  11 +-
 grub-core/fs/zfs/zfs.c                    |  26 +++--
 grub-core/fs/zfs/zfscrypt.c               |   7 +-
 grub-core/gfxmenu/gui_image.c             |   5 +-
 grub-core/gfxmenu/gui_string_util.c       |   2 +-
 grub-core/gfxmenu/widget-box.c            |   4 +-
 grub-core/io/gzio.c                       |   2 +-
 grub-core/kern/arm/efi/init.c             |   3 +
 grub-core/kern/arm64/efi/init.c           |   3 +
 grub-core/kern/efi/efi.c                  |  73 ++++++++++---
 grub-core/kern/efi/init.c                 |   1 -
 grub-core/kern/emu/hostdisk.c             |   2 +-
 grub-core/kern/emu/misc.c                 |  12 +++
 grub-core/kern/emu/mm.c                   |  13 ++-
 grub-core/kern/fs.c                       |   2 +-
 grub-core/kern/i386/efi/init.c            |   9 +-
 grub-core/kern/ia64/efi/init.c            |   9 +-
 grub-core/kern/misc.c                     |   2 +-
 grub-core/kern/mm.c                       |  40 +++++++
 grub-core/kern/parser.c                   |   2 +-
 grub-core/kern/riscv/efi/init.c           |   3 +
 grub-core/kern/uboot/uboot.c              |   2 +-
 grub-core/lib/LzmaEnc.c                   |  10 +-
 grub-core/lib/arg.c                       |  20 +++-
 grub-core/lib/efi/halt.c                  |   3 +-
 grub-core/lib/i386/relocator.c            |  28 ++---
 grub-core/lib/json/json.c                 |  11 +-
 grub-core/lib/json/json.h                 |   5 +-
 grub-core/lib/libgcrypt/cipher/ac.c       |   8 +-
 grub-core/lib/libgcrypt/cipher/primegen.c |   4 +-
 grub-core/lib/libgcrypt/cipher/pubkey.c   |   4 +-
 grub-core/lib/libgcrypt_wrap/mem.c        |  11 +-
 grub-core/lib/mips/relocator.c            |   6 +-
 grub-core/lib/posix_wrap/stdlib.h         |   8 +-
 grub-core/lib/powerpc/relocator.c         |   6 +-
 grub-core/lib/priority_queue.c            |   2 +-
 grub-core/lib/reed_solomon.c              |   7 +-
 grub-core/lib/relocator.c                 |  14 +--
 grub-core/lib/x86_64/efi/relocator.c      |   7 +-
 grub-core/lib/zstd/fse_decompress.c       |   2 +-
 grub-core/loader/arm/linux.c              |   2 +-
 grub-core/loader/efi/chainloader.c        |  34 ++++--
 grub-core/loader/i386/bsd.c               |   8 +-
 grub-core/loader/i386/bsdXX.c             |   2 +-
 grub-core/loader/i386/linux.c             |  14 ++-
 grub-core/loader/i386/multiboot_mbi.c     |   7 +-
 grub-core/loader/i386/pc/linux.c          |  15 +--
 grub-core/loader/i386/xen.c               |  12 ++-
 grub-core/loader/i386/xnu.c               |  30 +++---
 grub-core/loader/linux.c                  |  77 ++++++++++----
 grub-core/loader/macho.c                  |   2 +-
 grub-core/loader/mips/linux.c             |   9 +-
 grub-core/loader/multiboot.c              |   2 +-
 grub-core/loader/multiboot_elfxx.c        |  12 +--
 grub-core/loader/multiboot_mbi2.c         |  16 +--
 grub-core/loader/xnu.c                    |  13 ++-
 grub-core/loader/xnu_resume.c             |   2 +-
 grub-core/mmap/mmap.c                     |   4 +-
 grub-core/net/bootp.c                     |   2 +-
 grub-core/net/dns.c                       |  19 ++--
 grub-core/net/net.c                       |   4 +-
 grub-core/net/tftp.c                      | 168 ++++++++++--------------------
 grub-core/normal/charset.c                |  20 ++--
 grub-core/normal/cmdline.c                |  28 +++--
 grub-core/normal/menu_entry.c             |  27 +++--
 grub-core/normal/menu_text.c              |   4 +-
 grub-core/normal/term.c                   |   4 +-
 grub-core/osdep/linux/getroot.c           |   6 +-
 grub-core/osdep/unix/config.c             |   2 +-
 grub-core/osdep/windows/getroot.c         |   2 +-
 grub-core/osdep/windows/hostdisk.c        |   4 +-
 grub-core/osdep/windows/init.c            |   2 +-
 grub-core/osdep/windows/platform.c        |   4 +-
 grub-core/osdep/windows/relpath.c         |   2 +-
 grub-core/partmap/gpt.c                   |   2 +-
 grub-core/partmap/msdos.c                 |   2 +-
 grub-core/script/argv.c                   |  16 ++-
 grub-core/script/execute.c                |   4 +-
 grub-core/script/function.c               |  16 ++-
 grub-core/script/lexer.c                  |  21 +++-
 grub-core/script/parser.y                 |   3 +-
 grub-core/script/yylex.l                  |   4 +-
 grub-core/term/terminfo.c                 |   9 +-
 grub-core/tests/fake_input.c              |   2 +-
 grub-core/tests/video_checksum.c          |   6 +-
 grub-core/video/bitmap.c                  |  25 +++--
 grub-core/video/capture.c                 |   2 +-
 grub-core/video/emu/sdl.c                 |   2 +-
 grub-core/video/i386/pc/vga.c             |   2 +-
 grub-core/video/readers/png.c             |  15 ++-
 include/grub/compiler.h                   |   8 ++
 include/grub/efi/api.h                    |  14 ++-
 include/grub/emu/misc.h                   |   1 +
 include/grub/loader.h                     |   1 +
 include/grub/mm.h                         |   6 ++
 include/grub/relocator.h                  |  29 ++++++
 include/grub/safemath.h                   |  37 +++++++
 include/grub/script_sh.h                  |   5 +-
 include/grub/unicode.h                    |   4 +-
 util/getroot.c                            |   2 +-
 util/grub-file.c                          |   2 +-
 util/grub-fstest.c                        |   4 +-
 util/grub-install-common.c                |   2 +-
 util/grub-install.c                       |   4 +-
 util/grub-mkimagexx.c                     |   6 +-
 util/grub-mkrescue.c                      |   4 +-
 util/grub-mkstandalone.c                  |   2 +-
 util/grub-pe2elf.c                        |  12 +--
 util/grub-probe.c                         |   4 +-
 139 files changed, 1168 insertions(+), 606 deletions(-)

Alexey Makhalov (7):
      gfxmenu: Fix double free in load_image()
      xnu: Fix double free in grub_xnu_devprop_add_property()
      tftp: Do not use priority queue
      relocator: Protect grub_relocator_alloc_chunk_addr() input args against integer underflow/overflow
      relocator: Protect grub_relocator_alloc_chunk_align() max_addr against integer underflow
      relocator: Fix grub_relocator_alloc_chunk_align() top memory allocation
      efi: Fix use-after-free in halt/reboot path

Chris Coulson (3):
      json: Avoid a double-free when parsing fails.
      script: Remove unused fields from grub_script_function struct
      script: Avoid a use-after-free when redefining a function during execution

Colin Watson (1):
      linux: Fix integer overflows in initrd size handling

Daniel Kiper (2):
      font: Do not load more than one NAME section
      efi/chainloader: Propagate errors from copy_file_path()

Konrad Rzeszutek Wilk (4):
      lzma: Make sure we don't dereference past array
      term: Fix overflow on user inputs
      udf: Fix memory leak
      multiboot2: Fix memory leak if grub_create_loader_cmdline() fails

Peter Jones (11):
      yylex: Make lexer fatal errors actually be fatal
      safemath: Add some arithmetic primitives that check for overflow
      calloc: Make sure we always have an overflow-checking calloc() available
      calloc: Use calloc() at most places
      malloc: Use overflow checking primitives where we do complex allocations
      iso9660: Don't leak memory on realloc() failures
      hfsplus: Fix two more overflows
      lvm: Fix two more potential data-dependent alloc overflows
      emu: Make grub_free(NULL) safe
      efi: Fix some malformed device path arithmetic errors
      loader/linux: Avoid overflow on initrd size calculation



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

* [SECURITY PATCH 01/28] yylex: Make lexer fatal errors actually be fatal
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 02/28] safemath: Add some arithmetic primitives that check for overflow Daniel Kiper
                   ` (27 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Peter Jones <pjones@redhat.com>

When presented with a command that can't be tokenized to anything
smaller than YYLMAX characters, the parser calls YY_FATAL_ERROR(errmsg),
expecting that will stop further processing, as such:

  #define YY_DO_BEFORE_ACTION \
        yyg->yytext_ptr = yy_bp; \
        yyleng = (int) (yy_cp - yy_bp); \
        yyg->yy_hold_char = *yy_cp; \
        *yy_cp = '\0'; \
        if ( yyleng >= YYLMAX ) \
                YY_FATAL_ERROR( "token too large, exceeds YYLMAX" ); \
        yy_flex_strncpy( yytext, yyg->yytext_ptr, yyleng + 1 , yyscanner); \
        yyg->yy_c_buf_p = yy_cp;

The code flex generates expects that YY_FATAL_ERROR() will either return
for it or do some form of longjmp(), or handle the error in some way at
least, and so the strncpy() call isn't in an "else" clause, and thus if
YY_FATAL_ERROR() is *not* actually fatal, it does the call with the
questionable limit, and predictable results ensue.

Unfortunately, our implementation of YY_FATAL_ERROR() is:

   #define YY_FATAL_ERROR(msg)                     \
     do {                                          \
       grub_printf (_("fatal error: %s\n"), _(msg));     \
     } while (0)

The same pattern exists in yyless(), and similar problems exist in users
of YY_INPUT(), several places in the main parsing loop,
yy_get_next_buffer(), yy_load_buffer_state(), yyensure_buffer_stack,
yy_scan_buffer(), etc.

All of these callers expect YY_FATAL_ERROR() to actually be fatal, and
the things they do if it returns after calling it are wildly unsafe.

Fixes: CVE-2020-10713

Signed-off-by: Peter Jones <pjones@redhat.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/script/yylex.l | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/grub-core/script/yylex.l b/grub-core/script/yylex.l
index 7b44c37b7..b7203c823 100644
--- a/grub-core/script/yylex.l
+++ b/grub-core/script/yylex.l
@@ -37,11 +37,11 @@
 
 /* 
  * As we don't have access to yyscanner, we cannot do much except to
- * print the fatal error.
+ * print the fatal error and exit.
  */
 #define YY_FATAL_ERROR(msg)                     \
   do {                                          \
-    grub_printf (_("fatal error: %s\n"), _(msg));     \
+    grub_fatal (_("fatal error: %s\n"), _(msg));\
   } while (0)
 
 #define COPY(str, hint)                         \
-- 
2.11.0



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

* [SECURITY PATCH 02/28] safemath: Add some arithmetic primitives that check for overflow
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 01/28] yylex: Make lexer fatal errors actually be fatal Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 03/28] calloc: Make sure we always have an overflow-checking calloc() available Daniel Kiper
                   ` (26 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Peter Jones <pjones@redhat.com>

This adds a new header, include/grub/safemath.h, that includes easy to
use wrappers for __builtin_{add,sub,mul}_overflow() declared like:

  bool OP(a, b, res)

where OP is grub_add, grub_sub or grub_mul. OP() returns true in the
case where the operation would overflow and res is not modified.
Otherwise, false is returned and the operation is executed.

These arithmetic primitives require newer compiler versions. So, bump
these requirements in the INSTALL file too.

Signed-off-by: Peter Jones <pjones@redhat.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 INSTALL                 | 22 ++--------------------
 include/grub/compiler.h |  8 ++++++++
 include/grub/safemath.h | 37 +++++++++++++++++++++++++++++++++++++
 3 files changed, 47 insertions(+), 20 deletions(-)
 create mode 100644 include/grub/safemath.h

diff --git a/INSTALL b/INSTALL
index dedf236a8..79a0af7d9 100644
--- a/INSTALL
+++ b/INSTALL
@@ -11,27 +11,9 @@ GRUB depends on some software packages installed into your system. If
 you don't have any of them, please obtain and install them before
 configuring the GRUB.
 
-* GCC 4.1.3 or later
-  Note: older versions may work but support is limited
-
-  Experimental support for clang 3.3 or later (results in much bigger binaries)
+* GCC 5.1.0 or later
+  Experimental support for clang 3.8.0 or later (results in much bigger binaries)
   for i386, x86_64, arm (including thumb), arm64, mips(el), powerpc, sparc64
-  Note: clang 3.2 or later works for i386 and x86_64 targets but results in
-        much bigger binaries.
-	earlier versions not tested
-  Note: clang 3.2 or later works for arm
-	earlier versions not tested
-  Note: clang on arm64 is not supported due to
-	https://llvm.org/bugs/show_bug.cgi?id=26030
-  Note: clang 3.3 or later works for mips(el)
-	earlier versions fail to generate .reginfo and hence gprel relocations
-	fail.
-  Note: clang 3.2 or later works for powerpc
-	earlier versions not tested
-  Note: clang 3.5 or later works for sparc64
-        earlier versions return "error: unable to interface with target machine"
-  Note: clang has no support for ia64 and hence you can't compile GRUB
-	for ia64 with clang
 * GNU Make
 * GNU Bison 2.3 or later
 * GNU gettext 0.17 or later
diff --git a/include/grub/compiler.h b/include/grub/compiler.h
index c9e1d7a73..8f3be3ae7 100644
--- a/include/grub/compiler.h
+++ b/include/grub/compiler.h
@@ -48,4 +48,12 @@
 #  define WARN_UNUSED_RESULT
 #endif
 
+#if defined(__clang__) && defined(__clang_major__) && defined(__clang_minor__)
+#  define CLANG_PREREQ(maj,min) \
+          ((__clang_major__ > (maj)) || \
+	   (__clang_major__ == (maj) && __clang_minor__ >= (min)))
+#else
+#  define CLANG_PREREQ(maj,min) 0
+#endif
+
 #endif /* ! GRUB_COMPILER_HEADER */
diff --git a/include/grub/safemath.h b/include/grub/safemath.h
new file mode 100644
index 000000000..c17b89bba
--- /dev/null
+++ b/include/grub/safemath.h
@@ -0,0 +1,37 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2020  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ *  Arithmetic operations that protect against overflow.
+ */
+
+#ifndef GRUB_SAFEMATH_H
+#define GRUB_SAFEMATH_H 1
+
+#include <grub/compiler.h>
+
+/* These appear in gcc 5.1 and clang 3.8. */
+#if GNUC_PREREQ(5, 1) || CLANG_PREREQ(3, 8)
+
+#define grub_add(a, b, res)	__builtin_add_overflow(a, b, res)
+#define grub_sub(a, b, res)	__builtin_sub_overflow(a, b, res)
+#define grub_mul(a, b, res)	__builtin_mul_overflow(a, b, res)
+
+#else
+#error gcc 5.1 or newer or clang 3.8 or newer is required
+#endif
+
+#endif /* GRUB_SAFEMATH_H */
-- 
2.11.0



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

* [SECURITY PATCH 03/28] calloc: Make sure we always have an overflow-checking calloc() available
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 01/28] yylex: Make lexer fatal errors actually be fatal Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 02/28] safemath: Add some arithmetic primitives that check for overflow Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 04/28] calloc: Use calloc() at most places Daniel Kiper
                   ` (25 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Peter Jones <pjones@redhat.com>

This tries to make sure that everywhere in this source tree, we always have
an appropriate version of calloc() (i.e. grub_calloc(), xcalloc(), etc.)
available, and that they all safely check for overflow and return NULL when
it would occur.

Signed-off-by: Peter Jones <pjones@redhat.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/kern/emu/misc.c          | 12 ++++++++++++
 grub-core/kern/emu/mm.c            | 10 ++++++++++
 grub-core/kern/mm.c                | 40 ++++++++++++++++++++++++++++++++++++++
 grub-core/lib/libgcrypt_wrap/mem.c | 11 +++++++++--
 grub-core/lib/posix_wrap/stdlib.h  |  8 +++++++-
 include/grub/emu/misc.h            |  1 +
 include/grub/mm.h                  |  6 ++++++
 7 files changed, 85 insertions(+), 3 deletions(-)

diff --git a/grub-core/kern/emu/misc.c b/grub-core/kern/emu/misc.c
index 65db79baa..dfd8a8ec4 100644
--- a/grub-core/kern/emu/misc.c
+++ b/grub-core/kern/emu/misc.c
@@ -86,6 +86,18 @@ grub_util_error (const char *fmt, ...)
 }
 
 void *
+xcalloc (grub_size_t nmemb, grub_size_t size)
+{
+  void *p;
+
+  p = calloc (nmemb, size);
+  if (!p)
+    grub_util_error ("%s", _("out of memory"));
+
+  return p;
+}
+
+void *
 xmalloc (grub_size_t size)
 {
   void *p;
diff --git a/grub-core/kern/emu/mm.c b/grub-core/kern/emu/mm.c
index f262e95e3..145b01d37 100644
--- a/grub-core/kern/emu/mm.c
+++ b/grub-core/kern/emu/mm.c
@@ -26,6 +26,16 @@
 #include <grub/i18n.h>
 
 void *
+grub_calloc (grub_size_t nmemb, grub_size_t size)
+{
+  void *ret;
+  ret = calloc (nmemb, size);
+  if (!ret)
+    grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+  return ret;
+}
+
+void *
 grub_malloc (grub_size_t size)
 {
   void *ret;
diff --git a/grub-core/kern/mm.c b/grub-core/kern/mm.c
index ee88ff611..f2822a836 100644
--- a/grub-core/kern/mm.c
+++ b/grub-core/kern/mm.c
@@ -67,8 +67,10 @@
 #include <grub/dl.h>
 #include <grub/i18n.h>
 #include <grub/mm_private.h>
+#include <grub/safemath.h>
 
 #ifdef MM_DEBUG
+# undef grub_calloc
 # undef grub_malloc
 # undef grub_zalloc
 # undef grub_realloc
@@ -375,6 +377,30 @@ grub_memalign (grub_size_t align, grub_size_t size)
   return 0;
 }
 
+/*
+ * Allocate NMEMB instances of SIZE bytes and return the pointer, or error on
+ * integer overflow.
+ */
+void *
+grub_calloc (grub_size_t nmemb, grub_size_t size)
+{
+  void *ret;
+  grub_size_t sz = 0;
+
+  if (grub_mul (nmemb, size, &sz))
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
+      return NULL;
+    }
+
+  ret = grub_memalign (0, sz);
+  if (!ret)
+    return NULL;
+
+  grub_memset (ret, 0, sz);
+  return ret;
+}
+
 /* Allocate SIZE bytes and return the pointer.  */
 void *
 grub_malloc (grub_size_t size)
@@ -562,6 +588,20 @@ grub_mm_dump (unsigned lineno)
 }
 
 void *
+grub_debug_calloc (const char *file, int line, grub_size_t nmemb, grub_size_t size)
+{
+  void *ptr;
+
+  if (grub_mm_debug)
+    grub_printf ("%s:%d: calloc (0x%" PRIxGRUB_SIZE ", 0x%" PRIxGRUB_SIZE ") = ",
+		 file, line, size);
+  ptr = grub_calloc (nmemb, size);
+  if (grub_mm_debug)
+    grub_printf ("%p\n", ptr);
+  return ptr;
+}
+
+void *
 grub_debug_malloc (const char *file, int line, grub_size_t size)
 {
   void *ptr;
diff --git a/grub-core/lib/libgcrypt_wrap/mem.c b/grub-core/lib/libgcrypt_wrap/mem.c
index beeb661a3..74c6eafe5 100644
--- a/grub-core/lib/libgcrypt_wrap/mem.c
+++ b/grub-core/lib/libgcrypt_wrap/mem.c
@@ -4,6 +4,7 @@
 #include <grub/crypto.h>
 #include <grub/dl.h>
 #include <grub/env.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -36,7 +37,10 @@ void *
 gcry_xcalloc (size_t n, size_t m)
 {
   void *ret;
-  ret = grub_zalloc (n * m);
+  size_t sz;
+  if (grub_mul (n, m, &sz))
+    grub_fatal ("gcry_xcalloc would overflow");
+  ret = grub_zalloc (sz);
   if (!ret)
     grub_fatal ("gcry_xcalloc failed");
   return ret;
@@ -56,7 +60,10 @@ void *
 gcry_xcalloc_secure (size_t n, size_t m)
 {
   void *ret;
-  ret = grub_zalloc (n * m);
+  size_t sz;
+  if (grub_mul (n, m, &sz))
+    grub_fatal ("gcry_xcalloc would overflow");
+  ret = grub_zalloc (sz);
   if (!ret)
     grub_fatal ("gcry_xcalloc failed");
   return ret;
diff --git a/grub-core/lib/posix_wrap/stdlib.h b/grub-core/lib/posix_wrap/stdlib.h
index 3b46f47ff..7a8d385e9 100644
--- a/grub-core/lib/posix_wrap/stdlib.h
+++ b/grub-core/lib/posix_wrap/stdlib.h
@@ -21,6 +21,7 @@
 
 #include <grub/mm.h>
 #include <grub/misc.h>
+#include <grub/safemath.h>
 
 static inline void 
 free (void *ptr)
@@ -37,7 +38,12 @@ malloc (grub_size_t size)
 static inline void *
 calloc (grub_size_t size, grub_size_t nelem)
 {
-  return grub_zalloc (size * nelem);
+  grub_size_t sz;
+
+  if (grub_mul (size, nelem, &sz))
+    return NULL;
+
+  return grub_zalloc (sz);
 }
 
 static inline void *
diff --git a/include/grub/emu/misc.h b/include/grub/emu/misc.h
index ce464cfd0..ff9c48a64 100644
--- a/include/grub/emu/misc.h
+++ b/include/grub/emu/misc.h
@@ -47,6 +47,7 @@ grub_util_device_is_mapped (const char *dev);
 #define GRUB_HOST_PRIuLONG_LONG "llu"
 #define GRUB_HOST_PRIxLONG_LONG "llx"
 
+void * EXPORT_FUNC(xcalloc) (grub_size_t nmemb, grub_size_t size) WARN_UNUSED_RESULT;
 void * EXPORT_FUNC(xmalloc) (grub_size_t size) WARN_UNUSED_RESULT;
 void * EXPORT_FUNC(xrealloc) (void *ptr, grub_size_t size) WARN_UNUSED_RESULT;
 char * EXPORT_FUNC(xstrdup) (const char *str) WARN_UNUSED_RESULT;
diff --git a/include/grub/mm.h b/include/grub/mm.h
index 28e2e53eb..9c38dd3ca 100644
--- a/include/grub/mm.h
+++ b/include/grub/mm.h
@@ -29,6 +29,7 @@
 #endif
 
 void grub_mm_init_region (void *addr, grub_size_t size);
+void *EXPORT_FUNC(grub_calloc) (grub_size_t nmemb, grub_size_t size);
 void *EXPORT_FUNC(grub_malloc) (grub_size_t size);
 void *EXPORT_FUNC(grub_zalloc) (grub_size_t size);
 void EXPORT_FUNC(grub_free) (void *ptr);
@@ -48,6 +49,9 @@ extern int EXPORT_VAR(grub_mm_debug);
 void grub_mm_dump_free (void);
 void grub_mm_dump (unsigned lineno);
 
+#define grub_calloc(nmemb, size)	\
+  grub_debug_calloc (GRUB_FILE, __LINE__, nmemb, size)
+
 #define grub_malloc(size)	\
   grub_debug_malloc (GRUB_FILE, __LINE__, size)
 
@@ -63,6 +67,8 @@ void grub_mm_dump (unsigned lineno);
 #define grub_free(ptr)	\
   grub_debug_free (GRUB_FILE, __LINE__, ptr)
 
+void *EXPORT_FUNC(grub_debug_calloc) (const char *file, int line,
+				      grub_size_t nmemb, grub_size_t size);
 void *EXPORT_FUNC(grub_debug_malloc) (const char *file, int line,
 				      grub_size_t size);
 void *EXPORT_FUNC(grub_debug_zalloc) (const char *file, int line,
-- 
2.11.0



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

* [SECURITY PATCH 04/28] calloc: Use calloc() at most places
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (2 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 03/28] calloc: Make sure we always have an overflow-checking calloc() available Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 05/28] malloc: Use overflow checking primitives where we do complex allocations Daniel Kiper
                   ` (24 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Peter Jones <pjones@redhat.com>

This modifies most of the places we do some form of:

  X = malloc(Y * Z);

to use calloc(Y, Z) instead.

Among other issues, this fixes:
  - allocation of integer overflow in grub_png_decode_image_header()
    reported by Chris Coulson,
  - allocation of integer overflow in luks_recover_key()
    reported by Chris Coulson,
  - allocation of integer overflow in grub_lvm_detect()
    reported by Chris Coulson.

Fixes: CVE-2020-14308

Signed-off-by: Peter Jones <pjones@redhat.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/bus/usb/usbhub.c                |  8 ++++----
 grub-core/commands/efi/lsefisystab.c      |  3 ++-
 grub-core/commands/legacycfg.c            |  6 +++---
 grub-core/commands/menuentry.c            |  2 +-
 grub-core/commands/nativedisk.c           |  2 +-
 grub-core/commands/parttool.c             | 12 +++++++++---
 grub-core/commands/regexp.c               |  2 +-
 grub-core/commands/search_wrap.c          |  2 +-
 grub-core/disk/diskfilter.c               |  4 ++--
 grub-core/disk/ieee1275/ofdisk.c          |  2 +-
 grub-core/disk/ldm.c                      | 14 +++++++-------
 grub-core/disk/luks.c                     |  2 +-
 grub-core/disk/lvm.c                      | 12 ++++++------
 grub-core/disk/xen/xendisk.c              |  2 +-
 grub-core/efiemu/loadcore.c               |  2 +-
 grub-core/efiemu/mm.c                     |  6 +++---
 grub-core/font/font.c                     |  3 +--
 grub-core/fs/affs.c                       |  6 +++---
 grub-core/fs/btrfs.c                      |  6 +++---
 grub-core/fs/hfs.c                        |  2 +-
 grub-core/fs/hfsplus.c                    |  6 +++---
 grub-core/fs/iso9660.c                    |  2 +-
 grub-core/fs/ntfs.c                       |  4 ++--
 grub-core/fs/sfs.c                        |  2 +-
 grub-core/fs/tar.c                        |  2 +-
 grub-core/fs/udf.c                        |  4 ++--
 grub-core/fs/zfs/zfs.c                    |  4 ++--
 grub-core/gfxmenu/gui_string_util.c       |  2 +-
 grub-core/gfxmenu/widget-box.c            |  4 ++--
 grub-core/io/gzio.c                       |  2 +-
 grub-core/kern/efi/efi.c                  |  6 +++---
 grub-core/kern/emu/hostdisk.c             |  2 +-
 grub-core/kern/fs.c                       |  2 +-
 grub-core/kern/misc.c                     |  2 +-
 grub-core/kern/parser.c                   |  2 +-
 grub-core/kern/uboot/uboot.c              |  2 +-
 grub-core/lib/json/json.c                 |  2 +-
 grub-core/lib/libgcrypt/cipher/ac.c       |  8 ++++----
 grub-core/lib/libgcrypt/cipher/primegen.c |  4 ++--
 grub-core/lib/libgcrypt/cipher/pubkey.c   |  4 ++--
 grub-core/lib/priority_queue.c            |  2 +-
 grub-core/lib/reed_solomon.c              |  7 +++----
 grub-core/lib/relocator.c                 | 10 +++++-----
 grub-core/lib/zstd/fse_decompress.c       |  2 +-
 grub-core/loader/arm/linux.c              |  2 +-
 grub-core/loader/efi/chainloader.c        |  2 +-
 grub-core/loader/i386/bsdXX.c             |  2 +-
 grub-core/loader/i386/xnu.c               |  4 ++--
 grub-core/loader/macho.c                  |  2 +-
 grub-core/loader/multiboot_elfxx.c        |  2 +-
 grub-core/loader/xnu.c                    |  2 +-
 grub-core/mmap/mmap.c                     |  4 ++--
 grub-core/net/bootp.c                     |  2 +-
 grub-core/net/dns.c                       | 10 +++++-----
 grub-core/net/net.c                       |  4 ++--
 grub-core/normal/charset.c                | 10 +++++-----
 grub-core/normal/cmdline.c                | 14 +++++++-------
 grub-core/normal/menu_entry.c             | 14 +++++++-------
 grub-core/normal/menu_text.c              |  4 ++--
 grub-core/normal/term.c                   |  4 ++--
 grub-core/osdep/linux/getroot.c           |  6 +++---
 grub-core/osdep/unix/config.c             |  2 +-
 grub-core/osdep/windows/getroot.c         |  2 +-
 grub-core/osdep/windows/hostdisk.c        |  4 ++--
 grub-core/osdep/windows/init.c            |  2 +-
 grub-core/osdep/windows/platform.c        |  4 ++--
 grub-core/osdep/windows/relpath.c         |  2 +-
 grub-core/partmap/gpt.c                   |  2 +-
 grub-core/partmap/msdos.c                 |  2 +-
 grub-core/script/execute.c                |  2 +-
 grub-core/tests/fake_input.c              |  2 +-
 grub-core/tests/video_checksum.c          |  6 +++---
 grub-core/video/capture.c                 |  2 +-
 grub-core/video/emu/sdl.c                 |  2 +-
 grub-core/video/i386/pc/vga.c             |  2 +-
 grub-core/video/readers/png.c             |  2 +-
 include/grub/unicode.h                    |  4 ++--
 util/getroot.c                            |  2 +-
 util/grub-file.c                          |  2 +-
 util/grub-fstest.c                        |  4 ++--
 util/grub-install-common.c                |  2 +-
 util/grub-install.c                       |  4 ++--
 util/grub-mkimagexx.c                     |  6 ++----
 util/grub-mkrescue.c                      |  4 ++--
 util/grub-mkstandalone.c                  |  2 +-
 util/grub-pe2elf.c                        | 12 +++++-------
 util/grub-probe.c                         |  4 ++--
 87 files changed, 179 insertions(+), 178 deletions(-)

diff --git a/grub-core/bus/usb/usbhub.c b/grub-core/bus/usb/usbhub.c
index 34a7ff1b5..a06cce302 100644
--- a/grub-core/bus/usb/usbhub.c
+++ b/grub-core/bus/usb/usbhub.c
@@ -149,8 +149,8 @@ grub_usb_add_hub (grub_usb_device_t dev)
   grub_usb_set_configuration (dev, 1);
 
   dev->nports = hubdesc.portcnt;
-  dev->children = grub_zalloc (hubdesc.portcnt * sizeof (dev->children[0]));
-  dev->ports = grub_zalloc (dev->nports * sizeof (dev->ports[0]));
+  dev->children = grub_calloc (hubdesc.portcnt, sizeof (dev->children[0]));
+  dev->ports = grub_calloc (dev->nports, sizeof (dev->ports[0]));
   if (!dev->children || !dev->ports)
     {
       grub_free (dev->children);
@@ -268,8 +268,8 @@ grub_usb_controller_dev_register_iter (grub_usb_controller_t controller, void *d
 
   /* Query the number of ports the root Hub has.  */
   hub->nports = controller->dev->hubports (controller);
-  hub->devices = grub_zalloc (sizeof (hub->devices[0]) * hub->nports);
-  hub->ports = grub_zalloc (sizeof (hub->ports[0]) * hub->nports);
+  hub->devices = grub_calloc (hub->nports, sizeof (hub->devices[0]));
+  hub->ports = grub_calloc (hub->nports, sizeof (hub->ports[0]));
   if (!hub->devices || !hub->ports)
     {
       grub_free (hub->devices);
diff --git a/grub-core/commands/efi/lsefisystab.c b/grub-core/commands/efi/lsefisystab.c
index 902788250..d29188efa 100644
--- a/grub-core/commands/efi/lsefisystab.c
+++ b/grub-core/commands/efi/lsefisystab.c
@@ -73,7 +73,8 @@ grub_cmd_lsefisystab (struct grub_command *cmd __attribute__ ((unused)),
     grub_printf ("Vendor: ");
     
     for (vendor_utf16 = st->firmware_vendor; *vendor_utf16; vendor_utf16++);
-    vendor = grub_malloc (4 * (vendor_utf16 - st->firmware_vendor) + 1);
+    /* Allocate extra 3 bytes to simplify math. */
+    vendor = grub_calloc (4, vendor_utf16 - st->firmware_vendor + 1);
     if (!vendor)
       return grub_errno;
     *grub_utf16_to_utf8 ((grub_uint8_t *) vendor, st->firmware_vendor,
diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c
index db7a8f002..5e3ec0d5e 100644
--- a/grub-core/commands/legacycfg.c
+++ b/grub-core/commands/legacycfg.c
@@ -314,7 +314,7 @@ grub_cmd_legacy_kernel (struct grub_command *mycmd __attribute__ ((unused)),
   if (argc < 2)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
 
-  cutargs = grub_malloc (sizeof (cutargs[0]) * (argc - 1));
+  cutargs = grub_calloc (argc - 1, sizeof (cutargs[0]));
   if (!cutargs)
     return grub_errno;
   cutargc = argc - 1;
@@ -436,7 +436,7 @@ grub_cmd_legacy_kernel (struct grub_command *mycmd __attribute__ ((unused)),
 	    {
 	      char rbuf[3] = "-r";
 	      bsdargc = cutargc + 2;
-	      bsdargs = grub_malloc (sizeof (bsdargs[0]) * bsdargc);
+	      bsdargs = grub_calloc (bsdargc, sizeof (bsdargs[0]));
 	      if (!bsdargs)
 		{
 		  err = grub_errno;
@@ -559,7 +559,7 @@ grub_cmd_legacy_initrdnounzip (struct grub_command *mycmd __attribute__ ((unused
 	return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("can't find command `%s'"),
 			   "module");
 
-      newargs = grub_malloc ((argc + 1) * sizeof (newargs[0]));
+      newargs = grub_calloc (argc + 1, sizeof (newargs[0]));
       if (!newargs)
 	return grub_errno;
       grub_memcpy (newargs + 1, args, argc * sizeof (newargs[0]));
diff --git a/grub-core/commands/menuentry.c b/grub-core/commands/menuentry.c
index 2c5363da7..9164df744 100644
--- a/grub-core/commands/menuentry.c
+++ b/grub-core/commands/menuentry.c
@@ -154,7 +154,7 @@ grub_normal_add_menu_entry (int argc, const char **args,
     goto fail;
 
   /* Save argc, args to pass as parameters to block arg later. */
-  menu_args = grub_malloc (sizeof (char*) * (argc + 1));
+  menu_args = grub_calloc (argc + 1, sizeof (char *));
   if (! menu_args)
     goto fail;
 
diff --git a/grub-core/commands/nativedisk.c b/grub-core/commands/nativedisk.c
index 699447d11..7c8f97f6a 100644
--- a/grub-core/commands/nativedisk.c
+++ b/grub-core/commands/nativedisk.c
@@ -195,7 +195,7 @@ grub_cmd_nativedisk (grub_command_t cmd __attribute__ ((unused)),
   else
     path_prefix = prefix;
 
-  mods = grub_malloc (argc * sizeof (mods[0]));
+  mods = grub_calloc (argc, sizeof (mods[0]));
   if (!mods)
     return grub_errno;
 
diff --git a/grub-core/commands/parttool.c b/grub-core/commands/parttool.c
index 22b46b187..051e31320 100644
--- a/grub-core/commands/parttool.c
+++ b/grub-core/commands/parttool.c
@@ -59,7 +59,13 @@ grub_parttool_register(const char *part_name,
   for (nargs = 0; args[nargs].name != 0; nargs++);
   cur->nargs = nargs;
   cur->args = (struct grub_parttool_argdesc *)
-    grub_malloc ((nargs + 1) * sizeof (struct grub_parttool_argdesc));
+    grub_calloc (nargs + 1, sizeof (struct grub_parttool_argdesc));
+  if (!cur->args)
+    {
+      grub_free (cur);
+      curhandle--;
+      return -1;
+    }
   grub_memcpy (cur->args, args,
 	       (nargs + 1) * sizeof (struct grub_parttool_argdesc));
 
@@ -257,7 +263,7 @@ grub_cmd_parttool (grub_command_t cmd __attribute__ ((unused)),
 	return err;
       }
 
-  parsed = (int *) grub_zalloc (argc * sizeof (int));
+  parsed = (int *) grub_calloc (argc, sizeof (int));
 
   for (i = 1; i < argc; i++)
     if (! parsed[i])
@@ -290,7 +296,7 @@ grub_cmd_parttool (grub_command_t cmd __attribute__ ((unused)),
 	  }
 	ptool = cur;
 	pargs = (struct grub_parttool_args *)
-	  grub_zalloc (ptool->nargs * sizeof (struct grub_parttool_args));
+	  grub_calloc (ptool->nargs, sizeof (struct grub_parttool_args));
 	for (j = i; j < argc; j++)
 	  if (! parsed[j])
 	    {
diff --git a/grub-core/commands/regexp.c b/grub-core/commands/regexp.c
index 7c5c72fe4..612003f94 100644
--- a/grub-core/commands/regexp.c
+++ b/grub-core/commands/regexp.c
@@ -116,7 +116,7 @@ grub_cmd_regexp (grub_extcmd_context_t ctxt, int argc, char **args)
   if (ret)
     goto fail;
 
-  matches = grub_zalloc (sizeof (*matches) * (regex.re_nsub + 1));
+  matches = grub_calloc (regex.re_nsub + 1, sizeof (*matches));
   if (! matches)
     goto fail;
 
diff --git a/grub-core/commands/search_wrap.c b/grub-core/commands/search_wrap.c
index d7fd26b94..47fc8eb99 100644
--- a/grub-core/commands/search_wrap.c
+++ b/grub-core/commands/search_wrap.c
@@ -122,7 +122,7 @@ grub_cmd_search (grub_extcmd_context_t ctxt, int argc, char **args)
     for (i = 0; state[SEARCH_HINT_BAREMETAL].args[i]; i++)
       nhints++;
 
-  hints = grub_malloc (sizeof (hints[0]) * nhints);
+  hints = grub_calloc (nhints, sizeof (hints[0]));
   if (!hints)
     return grub_errno;
   j = 0;
diff --git a/grub-core/disk/diskfilter.c b/grub-core/disk/diskfilter.c
index 67bf37a9c..86557f923 100644
--- a/grub-core/disk/diskfilter.c
+++ b/grub-core/disk/diskfilter.c
@@ -1135,7 +1135,7 @@ grub_diskfilter_make_raid (grub_size_t uuidlen, char *uuid, int nmemb,
   array->lvs->segments->node_count = nmemb;
   array->lvs->segments->raid_member_size = disk_size;
   array->lvs->segments->nodes
-    = grub_zalloc (nmemb * sizeof (array->lvs->segments->nodes[0]));
+    = grub_calloc (nmemb, sizeof (array->lvs->segments->nodes[0]));
   array->lvs->segments->stripe_size = stripe_size;
   for (i = 0; i < nmemb; i++)
     {
@@ -1227,7 +1227,7 @@ insert_array (grub_disk_t disk, const struct grub_diskfilter_pv_id *id,
 	  grub_partition_t p;
 	  for (p = disk->partition; p; p = p->parent)
 	    s++;
-	  pv->partmaps = xmalloc (s * sizeof (pv->partmaps[0]));
+	  pv->partmaps = xcalloc (s, sizeof (pv->partmaps[0]));
 	  s = 0;
 	  for (p = disk->partition; p; p = p->parent)
 	    pv->partmaps[s++] = xstrdup (p->partmap->name);
diff --git a/grub-core/disk/ieee1275/ofdisk.c b/grub-core/disk/ieee1275/ofdisk.c
index f73257e66..03674cb47 100644
--- a/grub-core/disk/ieee1275/ofdisk.c
+++ b/grub-core/disk/ieee1275/ofdisk.c
@@ -297,7 +297,7 @@ dev_iterate (const struct grub_ieee1275_devalias *alias)
       /* Power machines documentation specify 672 as maximum SAS disks in
          one system. Using a slightly larger value to be safe. */
       table_size = 768;
-      table = grub_malloc (table_size * sizeof (grub_uint64_t));
+      table = grub_calloc (table_size, sizeof (grub_uint64_t));
 
       if (!table)
         {
diff --git a/grub-core/disk/ldm.c b/grub-core/disk/ldm.c
index 2a22d2d6c..e6323701a 100644
--- a/grub-core/disk/ldm.c
+++ b/grub-core/disk/ldm.c
@@ -323,8 +323,8 @@ make_vg (grub_disk_t disk,
 	  lv->segments->type = GRUB_DISKFILTER_MIRROR;
 	  lv->segments->node_count = 0;
 	  lv->segments->node_alloc = 8;
-	  lv->segments->nodes = grub_zalloc (sizeof (*lv->segments->nodes)
-					     * lv->segments->node_alloc);
+	  lv->segments->nodes = grub_calloc (lv->segments->node_alloc,
+					     sizeof (*lv->segments->nodes));
 	  if (!lv->segments->nodes)
 	    goto fail2;
 	  ptr = vblk[i].dynamic;
@@ -543,8 +543,8 @@ make_vg (grub_disk_t disk,
 	    {
 	      comp->segment_alloc = 8;
 	      comp->segment_count = 0;
-	      comp->segments = grub_malloc (sizeof (*comp->segments)
-					    * comp->segment_alloc);
+	      comp->segments = grub_calloc (comp->segment_alloc,
+					    sizeof (*comp->segments));
 	      if (!comp->segments)
 		goto fail2;
 	    }
@@ -590,8 +590,8 @@ make_vg (grub_disk_t disk,
 		}
 	      comp->segments->node_count = read_int (ptr + 1, *ptr);
 	      comp->segments->node_alloc = comp->segments->node_count;
-	      comp->segments->nodes = grub_zalloc (sizeof (*comp->segments->nodes)
-						   * comp->segments->node_alloc);
+	      comp->segments->nodes = grub_calloc (comp->segments->node_alloc,
+						   sizeof (*comp->segments->nodes));
 	      if (!lv->segments->nodes)
 		goto fail2;
 	    }
@@ -1017,7 +1017,7 @@ grub_util_ldm_embed (struct grub_disk *disk, unsigned int *nsectors,
       *nsectors = lv->size;
       if (*nsectors > max_nsectors)
 	*nsectors = max_nsectors;
-      *sectors = grub_malloc (*nsectors * sizeof (**sectors));
+      *sectors = grub_calloc (*nsectors, sizeof (**sectors));
       if (!*sectors)
 	return grub_errno;
       for (i = 0; i < *nsectors; i++)
diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c
index 410cd6f84..6ae162601 100644
--- a/grub-core/disk/luks.c
+++ b/grub-core/disk/luks.c
@@ -178,7 +178,7 @@ luks_recover_key (grub_disk_t source,
 	&& grub_be_to_cpu32 (header.keyblock[i].stripes) > max_stripes)
       max_stripes = grub_be_to_cpu32 (header.keyblock[i].stripes);
 
-  split_key = grub_malloc (keysize * max_stripes);
+  split_key = grub_calloc (keysize, max_stripes);
   if (!split_key)
     return grub_errno;
 
diff --git a/grub-core/disk/lvm.c b/grub-core/disk/lvm.c
index e47be642a..48e36b4f3 100644
--- a/grub-core/disk/lvm.c
+++ b/grub-core/disk/lvm.c
@@ -210,7 +210,7 @@ grub_lvm_detect (grub_disk_t disk,
      first one.  */
 
   /* Allocate buffer space for the circular worst-case scenario. */
-  metadatabuf = grub_malloc (2 * mda_size);
+  metadatabuf = grub_calloc (2, mda_size);
   if (! metadatabuf)
     goto fail;
 
@@ -465,7 +465,7 @@ grub_lvm_detect (grub_disk_t disk,
 #endif
 		  goto lvs_fail;
 		}
-	      lv->segments = grub_zalloc (sizeof (*seg) * lv->segment_count);
+	      lv->segments = grub_calloc (lv->segment_count, sizeof (*seg));
 	      seg = lv->segments;
 
 	      for (i = 0; i < lv->segment_count; i++)
@@ -522,8 +522,8 @@ grub_lvm_detect (grub_disk_t disk,
 		      if (seg->node_count != 1)
 			seg->stripe_size = grub_lvm_getvalue (&p, "stripe_size = ");
 
-		      seg->nodes = grub_zalloc (sizeof (*stripe)
-						* seg->node_count);
+		      seg->nodes = grub_calloc (seg->node_count,
+						sizeof (*stripe));
 		      stripe = seg->nodes;
 
 		      p = grub_strstr (p, "stripes = [");
@@ -899,7 +899,7 @@ grub_lvm_detect (grub_disk_t disk,
 		break;
 	    if (lv)
 	      {
-		cache->lv->segments = grub_malloc (lv->segment_count * sizeof (*lv->segments));
+		cache->lv->segments = grub_calloc (lv->segment_count, sizeof (*lv->segments));
 		if (!cache->lv->segments)
 		  {
 		    grub_lvm_free_cache_lvs (cache_lvs);
@@ -912,7 +912,7 @@ grub_lvm_detect (grub_disk_t disk,
 		    struct grub_diskfilter_node *nodes = lv->segments[i].nodes;
 		    grub_size_t node_count = lv->segments[i].node_count;
 
-		    cache->lv->segments[i].nodes = grub_malloc (node_count * sizeof (*nodes));
+		    cache->lv->segments[i].nodes = grub_calloc (node_count, sizeof (*nodes));
 		    if (!cache->lv->segments[i].nodes)
 		      {
 			for (j = 0; j < i; ++j)
diff --git a/grub-core/disk/xen/xendisk.c b/grub-core/disk/xen/xendisk.c
index 48476cbbf..d6612eebd 100644
--- a/grub-core/disk/xen/xendisk.c
+++ b/grub-core/disk/xen/xendisk.c
@@ -426,7 +426,7 @@ grub_xendisk_init (void)
   if (!ctr)
     return;
 
-  virtdisks = grub_malloc (ctr * sizeof (virtdisks[0]));
+  virtdisks = grub_calloc (ctr, sizeof (virtdisks[0]));
   if (!virtdisks)
     return;
   if (grub_xenstore_dir ("device/vbd", fill, &ctr))
diff --git a/grub-core/efiemu/loadcore.c b/grub-core/efiemu/loadcore.c
index 44085ef81..2b924623f 100644
--- a/grub-core/efiemu/loadcore.c
+++ b/grub-core/efiemu/loadcore.c
@@ -201,7 +201,7 @@ grub_efiemu_count_symbols (const Elf_Ehdr *e)
 
   grub_efiemu_nelfsyms = (unsigned) s->sh_size / (unsigned) s->sh_entsize;
   grub_efiemu_elfsyms = (struct grub_efiemu_elf_sym *)
-    grub_malloc (sizeof (struct grub_efiemu_elf_sym) * grub_efiemu_nelfsyms);
+    grub_calloc (grub_efiemu_nelfsyms, sizeof (struct grub_efiemu_elf_sym));
 
   /* Relocators */
   for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
diff --git a/grub-core/efiemu/mm.c b/grub-core/efiemu/mm.c
index 52a032f7b..9b8e0d0ad 100644
--- a/grub-core/efiemu/mm.c
+++ b/grub-core/efiemu/mm.c
@@ -554,11 +554,11 @@ grub_efiemu_mmap_sort_and_uniq (void)
   /* Initialize variables*/
   grub_memset (present, 0, sizeof (int) * GRUB_EFI_MAX_MEMORY_TYPE);
   scanline_events = (struct grub_efiemu_mmap_scan *)
-    grub_malloc (sizeof (struct grub_efiemu_mmap_scan) * 2 * mmap_num);
+    grub_calloc (mmap_num, sizeof (struct grub_efiemu_mmap_scan) * 2);
 
   /* Number of chunks can't increase more than by factor of 2 */
   result = (grub_efi_memory_descriptor_t *)
-    grub_malloc (sizeof (grub_efi_memory_descriptor_t) * 2 * mmap_num);
+    grub_calloc (mmap_num, sizeof (grub_efi_memory_descriptor_t) * 2);
   if (!result || !scanline_events)
     {
       grub_free (result);
@@ -660,7 +660,7 @@ grub_efiemu_mm_do_alloc (void)
 
   /* Preallocate mmap */
   efiemu_mmap = (grub_efi_memory_descriptor_t *)
-    grub_malloc (mmap_reserved_size * sizeof (grub_efi_memory_descriptor_t));
+    grub_calloc (mmap_reserved_size, sizeof (grub_efi_memory_descriptor_t));
   if (!efiemu_mmap)
     {
       grub_efiemu_unload ();
diff --git a/grub-core/font/font.c b/grub-core/font/font.c
index 85a292557..8e118b315 100644
--- a/grub-core/font/font.c
+++ b/grub-core/font/font.c
@@ -293,8 +293,7 @@ load_font_index (grub_file_t file, grub_uint32_t sect_length, struct
   font->num_chars = sect_length / FONT_CHAR_INDEX_ENTRY_SIZE;
 
   /* Allocate the character index array.  */
-  font->char_index = grub_malloc (font->num_chars
-				  * sizeof (struct char_index_entry));
+  font->char_index = grub_calloc (font->num_chars, sizeof (struct char_index_entry));
   if (!font->char_index)
     return 1;
   font->bmp_idx = grub_malloc (0x10000 * sizeof (grub_uint16_t));
diff --git a/grub-core/fs/affs.c b/grub-core/fs/affs.c
index 6b6a2bc91..220b3712f 100644
--- a/grub-core/fs/affs.c
+++ b/grub-core/fs/affs.c
@@ -301,7 +301,7 @@ grub_affs_read_symlink (grub_fshelp_node_t node)
       return 0;
     }
   latin1[symlink_size] = 0;
-  utf8 = grub_malloc (symlink_size * GRUB_MAX_UTF8_PER_LATIN1 + 1);
+  utf8 = grub_calloc (GRUB_MAX_UTF8_PER_LATIN1 + 1, symlink_size);
   if (!utf8)
     {
       grub_free (latin1);
@@ -422,7 +422,7 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
 	return 1;
     }
 
-  hashtable = grub_zalloc (data->htsize * sizeof (*hashtable));
+  hashtable = grub_calloc (data->htsize, sizeof (*hashtable));
   if (!hashtable)
     return 1;
 
@@ -628,7 +628,7 @@ grub_affs_label (grub_device_t device, char **label)
       len = file.namelen;
       if (len > sizeof (file.name))
 	len = sizeof (file.name);
-      *label = grub_malloc (len * GRUB_MAX_UTF8_PER_LATIN1 + 1);
+      *label = grub_calloc (GRUB_MAX_UTF8_PER_LATIN1 + 1, len);
       if (*label)
 	*grub_latin1_to_utf8 ((grub_uint8_t *) *label, file.name, len) = '\0';
     }
diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 63f9657a6..4b8380439 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -415,7 +415,7 @@ lower_bound (struct grub_btrfs_data *data,
     {
       desc->allocated = 16;
       desc->depth = 0;
-      desc->data = grub_malloc (sizeof (desc->data[0]) * desc->allocated);
+      desc->data = grub_calloc (desc->allocated, sizeof (desc->data[0]));
       if (!desc->data)
 	return grub_errno;
     }
@@ -754,7 +754,7 @@ raid56_read_retry (struct grub_btrfs_data *data,
   grub_err_t ret = GRUB_ERR_OUT_OF_MEMORY;
   grub_uint64_t i, failed_devices;
 
-  buffers = grub_zalloc (sizeof(*buffers) * nstripes);
+  buffers = grub_calloc (nstripes, sizeof (*buffers));
   if (!buffers)
     goto cleanup;
 
@@ -2167,7 +2167,7 @@ grub_btrfs_embed (grub_device_t device __attribute__ ((unused)),
   *nsectors = 64 * 2 - 1;
   if (*nsectors > max_nsectors)
     *nsectors = max_nsectors;
-  *sectors = grub_malloc (*nsectors * sizeof (**sectors));
+  *sectors = grub_calloc (*nsectors, sizeof (**sectors));
   if (!*sectors)
     return grub_errno;
   for (i = 0; i < *nsectors; i++)
diff --git a/grub-core/fs/hfs.c b/grub-core/fs/hfs.c
index ac0a40990..3fe842b4d 100644
--- a/grub-core/fs/hfs.c
+++ b/grub-core/fs/hfs.c
@@ -1360,7 +1360,7 @@ grub_hfs_label (grub_device_t device, char **label)
       grub_size_t len = data->sblock.volname[0];
       if (len > sizeof (data->sblock.volname) - 1)
 	len = sizeof (data->sblock.volname) - 1;
-      *label = grub_malloc (len * MAX_UTF8_PER_MAC_ROMAN + 1);
+      *label = grub_calloc (MAX_UTF8_PER_MAC_ROMAN + 1, len);
       if (*label)
 	macroman_to_utf8 (*label, data->sblock.volname + 1,
 			  len + 1, 0);
diff --git a/grub-core/fs/hfsplus.c b/grub-core/fs/hfsplus.c
index 54786bb1c..dae43becc 100644
--- a/grub-core/fs/hfsplus.c
+++ b/grub-core/fs/hfsplus.c
@@ -720,7 +720,7 @@ list_nodes (void *record, void *hook_arg)
   if (! filename)
     return 0;
 
-  keyname = grub_malloc (grub_be_to_cpu16 (catkey->namelen) * sizeof (*keyname));
+  keyname = grub_calloc (grub_be_to_cpu16 (catkey->namelen), sizeof (*keyname));
   if (!keyname)
     {
       grub_free (filename);
@@ -1007,7 +1007,7 @@ grub_hfsplus_label (grub_device_t device, char **label)
     grub_hfsplus_btree_recptr (&data->catalog_tree, node, ptr);
 
   label_len = grub_be_to_cpu16 (catkey->namelen);
-  label_name = grub_malloc (label_len * sizeof (*label_name));
+  label_name = grub_calloc (label_len, sizeof (*label_name));
   if (!label_name)
     {
       grub_free (node);
@@ -1029,7 +1029,7 @@ grub_hfsplus_label (grub_device_t device, char **label)
 	}
     }
 
-  *label = grub_malloc (label_len * GRUB_MAX_UTF8_PER_UTF16 + 1);
+  *label = grub_calloc (label_len, GRUB_MAX_UTF8_PER_UTF16 + 1);
   if (! *label)
     {
       grub_free (label_name);
diff --git a/grub-core/fs/iso9660.c b/grub-core/fs/iso9660.c
index 49c0c632b..4f1b52a55 100644
--- a/grub-core/fs/iso9660.c
+++ b/grub-core/fs/iso9660.c
@@ -331,7 +331,7 @@ grub_iso9660_convert_string (grub_uint8_t *us, int len)
   int i;
   grub_uint16_t t[MAX_NAMELEN / 2 + 1];
 
-  p = grub_malloc (len * GRUB_MAX_UTF8_PER_UTF16 + 1);
+  p = grub_calloc (len, GRUB_MAX_UTF8_PER_UTF16 + 1);
   if (! p)
     return NULL;
 
diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c
index fc4e1f678..2f34f76da 100644
--- a/grub-core/fs/ntfs.c
+++ b/grub-core/fs/ntfs.c
@@ -556,8 +556,8 @@ get_utf8 (grub_uint8_t *in, grub_size_t len)
   grub_uint16_t *tmp;
   grub_size_t i;
 
-  buf = grub_malloc (len * GRUB_MAX_UTF8_PER_UTF16 + 1);
-  tmp = grub_malloc (len * sizeof (tmp[0]));
+  buf = grub_calloc (len, GRUB_MAX_UTF8_PER_UTF16 + 1);
+  tmp = grub_calloc (len, sizeof (tmp[0]));
   if (!buf || !tmp)
     {
       grub_free (buf);
diff --git a/grub-core/fs/sfs.c b/grub-core/fs/sfs.c
index 50c1fe72f..90f7fb379 100644
--- a/grub-core/fs/sfs.c
+++ b/grub-core/fs/sfs.c
@@ -266,7 +266,7 @@ grub_sfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
       node->next_extent = node->block;
       node->cache_size = 0;
 
-      node->cache = grub_malloc (sizeof (node->cache[0]) * cache_size);
+      node->cache = grub_calloc (cache_size, sizeof (node->cache[0]));
       if (!node->cache)
 	{
 	  grub_errno = 0;
diff --git a/grub-core/fs/tar.c b/grub-core/fs/tar.c
index 7d63e0c99..c551ed6b5 100644
--- a/grub-core/fs/tar.c
+++ b/grub-core/fs/tar.c
@@ -120,7 +120,7 @@ grub_cpio_find_file (struct grub_archelp_data *data, char **name,
 	  if (data->linkname_alloc < linksize + 1)
 	    {
 	      char *n;
-	      n = grub_malloc (2 * (linksize + 1));
+	      n = grub_calloc (2, linksize + 1);
 	      if (!n)
 		return grub_errno;
 	      grub_free (data->linkname);
diff --git a/grub-core/fs/udf.c b/grub-core/fs/udf.c
index dc8b6e2d1..a83761674 100644
--- a/grub-core/fs/udf.c
+++ b/grub-core/fs/udf.c
@@ -873,7 +873,7 @@ read_string (const grub_uint8_t *raw, grub_size_t sz, char *outbuf)
     {
       unsigned i;
       utf16len = sz - 1;
-      utf16 = grub_malloc (utf16len * sizeof (utf16[0]));
+      utf16 = grub_calloc (utf16len, sizeof (utf16[0]));
       if (!utf16)
 	return NULL;
       for (i = 0; i < utf16len; i++)
@@ -883,7 +883,7 @@ read_string (const grub_uint8_t *raw, grub_size_t sz, char *outbuf)
     {
       unsigned i;
       utf16len = (sz - 1) / 2;
-      utf16 = grub_malloc (utf16len * sizeof (utf16[0]));
+      utf16 = grub_calloc (utf16len, sizeof (utf16[0]));
       if (!utf16)
 	return NULL;
       for (i = 0; i < utf16len; i++)
diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c
index b5e10fd0b..0c676ac41 100644
--- a/grub-core/fs/zfs/zfs.c
+++ b/grub-core/fs/zfs/zfs.c
@@ -3328,7 +3328,7 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
 	}
       subvol->nkeys = 0;
       zap_iterate (&keychain_dn, 8, count_zap_keys, &ctx, data);
-      subvol->keyring = grub_zalloc (subvol->nkeys * sizeof (subvol->keyring[0]));
+      subvol->keyring = grub_calloc (subvol->nkeys, sizeof (subvol->keyring[0]));
       if (!subvol->keyring)
 	{
 	  grub_free (fsname);
@@ -4339,7 +4339,7 @@ grub_zfs_embed (grub_device_t device __attribute__ ((unused)),
   *nsectors = (VDEV_BOOT_SIZE >> GRUB_DISK_SECTOR_BITS);
   if (*nsectors > max_nsectors)
     *nsectors = max_nsectors;
-  *sectors = grub_malloc (*nsectors * sizeof (**sectors));
+  *sectors = grub_calloc (*nsectors, sizeof (**sectors));
   if (!*sectors)
     return grub_errno;
   for (i = 0; i < *nsectors; i++)
diff --git a/grub-core/gfxmenu/gui_string_util.c b/grub-core/gfxmenu/gui_string_util.c
index a9a415e31..ba1e1eab3 100644
--- a/grub-core/gfxmenu/gui_string_util.c
+++ b/grub-core/gfxmenu/gui_string_util.c
@@ -55,7 +55,7 @@ canonicalize_path (const char *path)
     if (*p == '/')
       components++;
 
-  char **path_array = grub_malloc (components * sizeof (*path_array));
+  char **path_array = grub_calloc (components, sizeof (*path_array));
   if (! path_array)
     return 0;
 
diff --git a/grub-core/gfxmenu/widget-box.c b/grub-core/gfxmenu/widget-box.c
index b60602889..470597ded 100644
--- a/grub-core/gfxmenu/widget-box.c
+++ b/grub-core/gfxmenu/widget-box.c
@@ -303,10 +303,10 @@ grub_gfxmenu_create_box (const char *pixmaps_prefix,
   box->content_height = 0;
   box->raw_pixmaps =
     (struct grub_video_bitmap **)
-    grub_malloc (BOX_NUM_PIXMAPS * sizeof (struct grub_video_bitmap *));
+    grub_calloc (BOX_NUM_PIXMAPS, sizeof (struct grub_video_bitmap *));
   box->scaled_pixmaps =
     (struct grub_video_bitmap **)
-    grub_malloc (BOX_NUM_PIXMAPS * sizeof (struct grub_video_bitmap *));
+    grub_calloc (BOX_NUM_PIXMAPS, sizeof (struct grub_video_bitmap *));
 
   /* Initialize all pixmap pointers to NULL so that proper destruction can
      be performed if an error is encountered partway through construction.  */
diff --git a/grub-core/io/gzio.c b/grub-core/io/gzio.c
index 6208a9763..43d98a7bd 100644
--- a/grub-core/io/gzio.c
+++ b/grub-core/io/gzio.c
@@ -554,7 +554,7 @@ huft_build (unsigned *b,	/* code lengths in bits (all assumed <= BMAX) */
 	      z = 1 << j;	/* table entries for j-bit table */
 
 	      /* allocate and link in new table */
-	      q = (struct huft *) grub_zalloc ((z + 1) * sizeof (struct huft));
+	      q = (struct huft *) grub_calloc (z + 1, sizeof (struct huft));
 	      if (! q)
 		{
 		  if (h)
diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
index 3a708ed72..b2d07a3fa 100644
--- a/grub-core/kern/efi/efi.c
+++ b/grub-core/kern/efi/efi.c
@@ -202,7 +202,7 @@ grub_efi_set_variable(const char *var, const grub_efi_guid_t *guid,
 
   len = grub_strlen (var);
   len16 = len * GRUB_MAX_UTF16_PER_UTF8;
-  var16 = grub_malloc ((len16 + 1) * sizeof (var16[0]));
+  var16 = grub_calloc (len16 + 1, sizeof (var16[0]));
   if (!var16)
     return grub_errno;
   len16 = grub_utf8_to_utf16 (var16, len16, (grub_uint8_t *) var, len, NULL);
@@ -237,7 +237,7 @@ grub_efi_get_variable (const char *var, const grub_efi_guid_t *guid,
 
   len = grub_strlen (var);
   len16 = len * GRUB_MAX_UTF16_PER_UTF8;
-  var16 = grub_malloc ((len16 + 1) * sizeof (var16[0]));
+  var16 = grub_calloc (len16 + 1, sizeof (var16[0]));
   if (!var16)
     return NULL;
   len16 = grub_utf8_to_utf16 (var16, len16, (grub_uint8_t *) var, len, NULL);
@@ -393,7 +393,7 @@ grub_efi_get_filename (grub_efi_device_path_t *dp0)
 	  while (len > 0 && fp->path_name[len - 1] == 0)
 	    len--;
 
-	  dup_name = grub_malloc (len * sizeof (*dup_name));
+	  dup_name = grub_calloc (len, sizeof (*dup_name));
 	  if (!dup_name)
 	    {
 	      grub_free (name);
diff --git a/grub-core/kern/emu/hostdisk.c b/grub-core/kern/emu/hostdisk.c
index e9ec680cd..d975265b2 100644
--- a/grub-core/kern/emu/hostdisk.c
+++ b/grub-core/kern/emu/hostdisk.c
@@ -615,7 +615,7 @@ static char *
 grub_util_path_concat_real (size_t n, int ext, va_list ap)
 {
   size_t totlen = 0;
-  char **l = xmalloc ((n + ext) * sizeof (l[0]));
+  char **l = xcalloc (n + ext, sizeof (l[0]));
   char *r, *p, *pi;
   size_t i;
   int first = 1;
diff --git a/grub-core/kern/fs.c b/grub-core/kern/fs.c
index 88d39360d..fb30da9f4 100644
--- a/grub-core/kern/fs.c
+++ b/grub-core/kern/fs.c
@@ -151,7 +151,7 @@ grub_fs_blocklist_open (grub_file_t file, const char *name)
   while (p);
 
   /* Allocate a block list.  */
-  blocks = grub_zalloc (sizeof (struct grub_fs_block) * (num + 1));
+  blocks = grub_calloc (num + 1, sizeof (struct grub_fs_block));
   if (! blocks)
     return 0;
 
diff --git a/grub-core/kern/misc.c b/grub-core/kern/misc.c
index ce92ddd07..a278e069b 100644
--- a/grub-core/kern/misc.c
+++ b/grub-core/kern/misc.c
@@ -704,7 +704,7 @@ parse_printf_args (const char *fmt0, struct printf_args *args,
     args->ptr = args->prealloc;
   else
     {
-      args->ptr = grub_malloc (args->count * sizeof (args->ptr[0]));
+      args->ptr = grub_calloc (args->count, sizeof (args->ptr[0]));
       if (!args->ptr)
 	{
 	  grub_errno = GRUB_ERR_NONE;
diff --git a/grub-core/kern/parser.c b/grub-core/kern/parser.c
index 78175aac2..619db3122 100644
--- a/grub-core/kern/parser.c
+++ b/grub-core/kern/parser.c
@@ -213,7 +213,7 @@ grub_parser_split_cmdline (const char *cmdline,
     return grub_errno;
   grub_memcpy (args, buffer, bp - buffer);
 
-  *argv = grub_malloc (sizeof (char *) * (*argc + 1));
+  *argv = grub_calloc (*argc + 1, sizeof (char *));
   if (!*argv)
     {
       grub_free (args);
diff --git a/grub-core/kern/uboot/uboot.c b/grub-core/kern/uboot/uboot.c
index be4816fe6..aac8f9ae1 100644
--- a/grub-core/kern/uboot/uboot.c
+++ b/grub-core/kern/uboot/uboot.c
@@ -133,7 +133,7 @@ grub_uboot_dev_enum (void)
     return num_devices;
 
   max_devices = 2;
-  enum_devices = grub_malloc (sizeof(struct device_info) * max_devices);
+  enum_devices = grub_calloc (max_devices, sizeof(struct device_info));
   if (!enum_devices)
     return 0;
 
diff --git a/grub-core/lib/json/json.c b/grub-core/lib/json/json.c
index 694af4f3a..913b8f404 100644
--- a/grub-core/lib/json/json.c
+++ b/grub-core/lib/json/json.c
@@ -53,7 +53,7 @@ grub_json_parse (grub_json_t **out, char *string, grub_size_t string_len)
       goto err;
     }
 
-  json->tokens = grub_malloc (sizeof (jsmntok_t) * jsmn_ret);
+  json->tokens = grub_calloc (jsmn_ret, sizeof (jsmntok_t));
   if (!json->tokens)
     {
       ret = GRUB_ERR_OUT_OF_MEMORY;
diff --git a/grub-core/lib/libgcrypt/cipher/ac.c b/grub-core/lib/libgcrypt/cipher/ac.c
index f5e946a2d..63f6fcd11 100644
--- a/grub-core/lib/libgcrypt/cipher/ac.c
+++ b/grub-core/lib/libgcrypt/cipher/ac.c
@@ -185,7 +185,7 @@ ac_data_mpi_copy (gcry_ac_mpi_t *data_mpis, unsigned int data_mpis_n,
   gcry_mpi_t mpi;
   char *label;
 
-  data_mpis_new = gcry_malloc (sizeof (*data_mpis_new) * data_mpis_n);
+  data_mpis_new = gcry_calloc (data_mpis_n, sizeof (*data_mpis_new));
   if (! data_mpis_new)
     {
       err = gcry_error_from_errno (errno);
@@ -572,7 +572,7 @@ _gcry_ac_data_to_sexp (gcry_ac_data_t data, gcry_sexp_t *sexp,
     }
 
   /* Add MPI list.  */
-  arg_list = gcry_malloc (sizeof (*arg_list) * (data_n + 1));
+  arg_list = gcry_calloc (data_n + 1, sizeof (*arg_list));
   if (! arg_list)
     {
       err = gcry_error_from_errno (errno);
@@ -1283,7 +1283,7 @@ ac_data_construct (const char *identifier, int include_flags,
   /* We build a list of arguments to pass to
      gcry_sexp_build_array().  */
   data_length = _gcry_ac_data_length (data);
-  arg_list = gcry_malloc (sizeof (*arg_list) * (data_length * 2));
+  arg_list = gcry_calloc (data_length, sizeof (*arg_list) * 2);
   if (! arg_list)
     {
       err = gcry_error_from_errno (errno);
@@ -1593,7 +1593,7 @@ _gcry_ac_key_pair_generate (gcry_ac_handle_t handle, unsigned int nbits,
 	arg_list_n += 2;
 
   /* Allocate list.  */
-  arg_list = gcry_malloc (sizeof (*arg_list) * arg_list_n);
+  arg_list = gcry_calloc (arg_list_n, sizeof (*arg_list));
   if (! arg_list)
     {
       err = gcry_error_from_errno (errno);
diff --git a/grub-core/lib/libgcrypt/cipher/primegen.c b/grub-core/lib/libgcrypt/cipher/primegen.c
index 2788e349f..b12e79b19 100644
--- a/grub-core/lib/libgcrypt/cipher/primegen.c
+++ b/grub-core/lib/libgcrypt/cipher/primegen.c
@@ -383,7 +383,7 @@ prime_generate_internal (int need_q_factor,
     }
 
   /* Allocate an array to track pool usage. */
-  pool_in_use = gcry_malloc (n * sizeof *pool_in_use);
+  pool_in_use = gcry_calloc (n, sizeof *pool_in_use);
   if (!pool_in_use)
     {
       err = gpg_err_code_from_errno (errno);
@@ -765,7 +765,7 @@ gen_prime (unsigned int nbits, int secret, int randomlevel,
   if (nbits < 16)
     log_fatal ("can't generate a prime with less than %d bits\n", 16);
 
-  mods = gcry_xmalloc( no_of_small_prime_numbers * sizeof *mods );
+  mods = gcry_xcalloc( no_of_small_prime_numbers, sizeof *mods);
   /* Make nbits fit into gcry_mpi_t implementation. */
   val_2  = mpi_alloc_set_ui( 2 );
   val_3 = mpi_alloc_set_ui( 3);
diff --git a/grub-core/lib/libgcrypt/cipher/pubkey.c b/grub-core/lib/libgcrypt/cipher/pubkey.c
index 910982141..ca087ad75 100644
--- a/grub-core/lib/libgcrypt/cipher/pubkey.c
+++ b/grub-core/lib/libgcrypt/cipher/pubkey.c
@@ -2941,7 +2941,7 @@ gcry_pk_encrypt (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t s_pkey)
        * array to a format string, so we have to do it this way :-(.  */
       /* FIXME: There is now such a format specifier, so we can
          change the code to be more clear. */
-      arg_list = malloc (nelem * sizeof *arg_list);
+      arg_list = calloc (nelem, sizeof *arg_list);
       if (!arg_list)
         {
           rc = gpg_err_code_from_syserror ();
@@ -3233,7 +3233,7 @@ gcry_pk_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_hash, gcry_sexp_t s_skey)
         }
       strcpy (p, "))");
 
-      arg_list = malloc (nelem * sizeof *arg_list);
+      arg_list = calloc (nelem, sizeof *arg_list);
       if (!arg_list)
         {
           rc = gpg_err_code_from_syserror ();
diff --git a/grub-core/lib/priority_queue.c b/grub-core/lib/priority_queue.c
index 659be0b7f..7d5e7c05a 100644
--- a/grub-core/lib/priority_queue.c
+++ b/grub-core/lib/priority_queue.c
@@ -92,7 +92,7 @@ grub_priority_queue_new (grub_size_t elsize,
 {
   struct grub_priority_queue *ret;
   void *els;
-  els = grub_malloc (elsize * 8);
+  els = grub_calloc (8, elsize);
   if (!els)
     return 0;
   ret = (struct grub_priority_queue *) grub_malloc (sizeof (*ret));
diff --git a/grub-core/lib/reed_solomon.c b/grub-core/lib/reed_solomon.c
index ee9fa7b4f..467305b46 100644
--- a/grub-core/lib/reed_solomon.c
+++ b/grub-core/lib/reed_solomon.c
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
+#define xcalloc calloc
 #define xmalloc malloc
 #define grub_memset memset
 #define grub_memcpy memcpy
@@ -158,11 +159,9 @@ rs_encode (gf_single_t *data, grub_size_t s, grub_size_t rs)
   gf_single_t *rs_polynomial;
   int i, j;
   gf_single_t *m;
-  m = xmalloc ((s + rs) * sizeof (gf_single_t));
+  m = xcalloc (s + rs, sizeof (gf_single_t));
   grub_memcpy (m, data, s * sizeof (gf_single_t));
-  grub_memset (m + s, 0, rs * sizeof (gf_single_t));
-  rs_polynomial = xmalloc ((rs + 1) * sizeof (gf_single_t));
-  grub_memset (rs_polynomial, 0, (rs + 1) * sizeof (gf_single_t));
+  rs_polynomial = xcalloc (rs + 1, sizeof (gf_single_t));
   rs_polynomial[rs] = 1;
   /* Multiply with X - a^r */
   for (j = 0; j < rs; j++)
diff --git a/grub-core/lib/relocator.c b/grub-core/lib/relocator.c
index ea3ebc719..5847aac36 100644
--- a/grub-core/lib/relocator.c
+++ b/grub-core/lib/relocator.c
@@ -495,9 +495,9 @@ malloc_in_range (struct grub_relocator *rel,
   }
 #endif
 
-  eventt = grub_malloc (maxevents * sizeof (events[0]));
+  eventt = grub_calloc (maxevents, sizeof (events[0]));
   counter = grub_malloc ((DIGITSORT_MASK + 2) * sizeof (counter[0]));
-  events = grub_malloc (maxevents * sizeof (events[0]));
+  events = grub_calloc (maxevents, sizeof (events[0]));
   if (!events || !eventt || !counter)
     {
       grub_dprintf ("relocator", "events or counter allocation failed %d\n",
@@ -963,7 +963,7 @@ malloc_in_range (struct grub_relocator *rel,
 #endif
     unsigned cural = 0;
     int oom = 0;
-    res->subchunks = grub_malloc (sizeof (res->subchunks[0]) * nallocs);
+    res->subchunks = grub_calloc (nallocs, sizeof (res->subchunks[0]));
     if (!res->subchunks)
       oom = 1;
     res->nsubchunks = nallocs;
@@ -1562,8 +1562,8 @@ grub_relocator_prepare_relocs (struct grub_relocator *rel, grub_addr_t addr,
 	    count[(chunk->src & 0xff) + 1]++;
 	  }
     }
-    from = grub_malloc (nchunks * sizeof (sorted[0]));
-    to = grub_malloc (nchunks * sizeof (sorted[0]));
+    from = grub_calloc (nchunks, sizeof (sorted[0]));
+    to = grub_calloc (nchunks, sizeof (sorted[0]));
     if (!from || !to)
       {
 	grub_free (from);
diff --git a/grub-core/lib/zstd/fse_decompress.c b/grub-core/lib/zstd/fse_decompress.c
index 72bbead5b..2227b84bc 100644
--- a/grub-core/lib/zstd/fse_decompress.c
+++ b/grub-core/lib/zstd/fse_decompress.c
@@ -82,7 +82,7 @@
 FSE_DTable* FSE_createDTable (unsigned tableLog)
 {
     if (tableLog > FSE_TABLELOG_ABSOLUTE_MAX) tableLog = FSE_TABLELOG_ABSOLUTE_MAX;
-    return (FSE_DTable*)malloc( FSE_DTABLE_SIZE_U32(tableLog) * sizeof (U32) );
+    return (FSE_DTable*)calloc( FSE_DTABLE_SIZE_U32(tableLog), sizeof (U32) );
 }
 
 void FSE_freeDTable (FSE_DTable* dt)
diff --git a/grub-core/loader/arm/linux.c b/grub-core/loader/arm/linux.c
index 51684914c..d70c17486 100644
--- a/grub-core/loader/arm/linux.c
+++ b/grub-core/loader/arm/linux.c
@@ -78,7 +78,7 @@ linux_prepare_atag (void *target_atag)
 
   /* some place for cmdline, initrd and terminator.  */
   tmp_size = get_atag_size (atag_orig) + 20 + (arg_size) / 4;
-  tmp_atag = grub_malloc (tmp_size * sizeof (grub_uint32_t));
+  tmp_atag = grub_calloc (tmp_size, sizeof (grub_uint32_t));
   if (!tmp_atag)
     return grub_errno;
 
diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
index cd92ea3f2..daf8c6b54 100644
--- a/grub-core/loader/efi/chainloader.c
+++ b/grub-core/loader/efi/chainloader.c
@@ -116,7 +116,7 @@ copy_file_path (grub_efi_file_path_device_path_t *fp,
   fp->header.type = GRUB_EFI_MEDIA_DEVICE_PATH_TYPE;
   fp->header.subtype = GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE;
 
-  path_name = grub_malloc (len * GRUB_MAX_UTF16_PER_UTF8 * sizeof (*path_name));
+  path_name = grub_calloc (len, GRUB_MAX_UTF16_PER_UTF8 * sizeof (*path_name));
   if (!path_name)
     return;
 
diff --git a/grub-core/loader/i386/bsdXX.c b/grub-core/loader/i386/bsdXX.c
index af6741d15..a8d8bf7da 100644
--- a/grub-core/loader/i386/bsdXX.c
+++ b/grub-core/loader/i386/bsdXX.c
@@ -48,7 +48,7 @@ read_headers (grub_file_t file, const char *filename, Elf_Ehdr *e, char **shdr)
   if (e->e_ident[EI_CLASS] != SUFFIX (ELFCLASS))
     return grub_error (GRUB_ERR_BAD_OS, N_("invalid arch-dependent ELF magic"));
 
-  *shdr = grub_malloc ((grub_uint32_t) e->e_shnum * e->e_shentsize);
+  *shdr = grub_calloc (e->e_shnum, e->e_shentsize);
   if (! *shdr)
     return grub_errno;
 
diff --git a/grub-core/loader/i386/xnu.c b/grub-core/loader/i386/xnu.c
index e64ed08f5..b7d176b5d 100644
--- a/grub-core/loader/i386/xnu.c
+++ b/grub-core/loader/i386/xnu.c
@@ -295,7 +295,7 @@ grub_xnu_devprop_add_property_utf8 (struct grub_xnu_devprop_device_descriptor *d
     return grub_errno;
 
   len = grub_strlen (name);
-  utf16 = grub_malloc (sizeof (grub_uint16_t) * len);
+  utf16 = grub_calloc (len, sizeof (grub_uint16_t));
   if (!utf16)
     {
       grub_free (utf8);
@@ -331,7 +331,7 @@ grub_xnu_devprop_add_property_utf16 (struct grub_xnu_devprop_device_descriptor *
   grub_uint16_t *utf16;
   grub_err_t err;
 
-  utf16 = grub_malloc (sizeof (grub_uint16_t) * namelen);
+  utf16 = grub_calloc (namelen, sizeof (grub_uint16_t));
   if (!utf16)
     return grub_errno;
   grub_memcpy (utf16, name, sizeof (grub_uint16_t) * namelen);
diff --git a/grub-core/loader/macho.c b/grub-core/loader/macho.c
index 085f9c689..05710c48e 100644
--- a/grub-core/loader/macho.c
+++ b/grub-core/loader/macho.c
@@ -97,7 +97,7 @@ grub_macho_file (grub_file_t file, const char *filename, int is_64bit)
       if (grub_file_seek (macho->file, sizeof (struct grub_macho_fat_header))
 	  == (grub_off_t) -1)
 	goto fail;
-      archs = grub_malloc (sizeof (struct grub_macho_fat_arch) * narchs);
+      archs = grub_calloc (narchs, sizeof (struct grub_macho_fat_arch));
       if (!archs)
 	goto fail;
       if (grub_file_read (macho->file, archs,
diff --git a/grub-core/loader/multiboot_elfxx.c b/grub-core/loader/multiboot_elfxx.c
index 70cd1db51..cc6853692 100644
--- a/grub-core/loader/multiboot_elfxx.c
+++ b/grub-core/loader/multiboot_elfxx.c
@@ -217,7 +217,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
     {
       grub_uint8_t *shdr, *shdrptr;
 
-      shdr = grub_malloc ((grub_uint32_t) ehdr->e_shnum * ehdr->e_shentsize);
+      shdr = grub_calloc (ehdr->e_shnum, ehdr->e_shentsize);
       if (!shdr)
 	return grub_errno;
       
diff --git a/grub-core/loader/xnu.c b/grub-core/loader/xnu.c
index 7f74d1d6f..77d7060e1 100644
--- a/grub-core/loader/xnu.c
+++ b/grub-core/loader/xnu.c
@@ -800,7 +800,7 @@ grub_cmd_xnu_mkext (grub_command_t cmd __attribute__ ((unused)),
   if (grub_be_to_cpu32 (head.magic) == GRUB_MACHO_FAT_MAGIC)
     {
       narchs = grub_be_to_cpu32 (head.nfat_arch);
-      archs = grub_malloc (sizeof (struct grub_macho_fat_arch) * narchs);
+      archs = grub_calloc (narchs, sizeof (struct grub_macho_fat_arch));
       if (! archs)
 	{
 	  grub_file_close (file);
diff --git a/grub-core/mmap/mmap.c b/grub-core/mmap/mmap.c
index b569cb23b..64684c23d 100644
--- a/grub-core/mmap/mmap.c
+++ b/grub-core/mmap/mmap.c
@@ -143,9 +143,9 @@ grub_mmap_iterate (grub_memory_hook_t hook, void *hook_data)
 
   /* Initialize variables. */
   ctx.scanline_events = (struct grub_mmap_scan *)
-    grub_malloc (sizeof (struct grub_mmap_scan) * 2 * mmap_num);
+    grub_calloc (mmap_num, sizeof (struct grub_mmap_scan) * 2);
 
-  present = grub_zalloc (sizeof (present[0]) * current_priority);
+  present = grub_calloc (current_priority, sizeof (present[0]));
 
   if (! ctx.scanline_events || !present)
     {
diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c
index 351ec2b45..e33be51f8 100644
--- a/grub-core/net/bootp.c
+++ b/grub-core/net/bootp.c
@@ -802,7 +802,7 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
   if (ncards == 0)
     return grub_error (GRUB_ERR_NET_NO_CARD, N_("no network card found"));
 
-  ifaces = grub_zalloc (ncards * sizeof (ifaces[0]));
+  ifaces = grub_calloc (ncards, sizeof (ifaces[0]));
   if (!ifaces)
     return grub_errno;
 
diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c
index 5d9afe093..e332d5eb4 100644
--- a/grub-core/net/dns.c
+++ b/grub-core/net/dns.c
@@ -285,8 +285,8 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)),
       ptr++;
       ptr += 4;
     }
-  *data->addresses = grub_malloc (sizeof ((*data->addresses)[0])
-				 * grub_be_to_cpu16 (head->ancount));
+  *data->addresses = grub_calloc (grub_be_to_cpu16 (head->ancount),
+				  sizeof ((*data->addresses)[0]));
   if (!*data->addresses)
     {
       grub_errno = GRUB_ERR_NONE;
@@ -406,8 +406,8 @@ recv_hook (grub_net_udp_socket_t sock __attribute__ ((unused)),
       dns_cache[h].addresses = 0;
       dns_cache[h].name = grub_strdup (data->oname);
       dns_cache[h].naddresses = *data->naddresses;
-      dns_cache[h].addresses = grub_malloc (*data->naddresses
-					    * sizeof (dns_cache[h].addresses[0]));
+      dns_cache[h].addresses = grub_calloc (*data->naddresses,
+					    sizeof (dns_cache[h].addresses[0]));
       dns_cache[h].limit_time = grub_get_time_ms () + 1000 * ttl_all;
       if (!dns_cache[h].addresses || !dns_cache[h].name)
 	{
@@ -479,7 +479,7 @@ grub_net_dns_lookup (const char *name,
 	}
     }
 
-  sockets = grub_malloc (sizeof (sockets[0]) * n_servers);
+  sockets = grub_calloc (n_servers, sizeof (sockets[0]));
   if (!sockets)
     return grub_errno;
 
diff --git a/grub-core/net/net.c b/grub-core/net/net.c
index 3a310c939..04ad10682 100644
--- a/grub-core/net/net.c
+++ b/grub-core/net/net.c
@@ -333,8 +333,8 @@ grub_cmd_ipv6_autoconf (struct grub_command *cmd __attribute__ ((unused)),
     ncards++;
   }
 
-  ifaces = grub_zalloc (ncards * sizeof (ifaces[0]));
-  slaacs = grub_zalloc (ncards * sizeof (slaacs[0]));
+  ifaces = grub_calloc (ncards, sizeof (ifaces[0]));
+  slaacs = grub_calloc (ncards, sizeof (slaacs[0]));
   if (!ifaces || !slaacs)
     {
       grub_free (ifaces);
diff --git a/grub-core/normal/charset.c b/grub-core/normal/charset.c
index b0ab47d73..d57fb72fa 100644
--- a/grub-core/normal/charset.c
+++ b/grub-core/normal/charset.c
@@ -203,7 +203,7 @@ grub_utf8_to_ucs4_alloc (const char *msg, grub_uint32_t **unicode_msg,
 {
   grub_size_t msg_len = grub_strlen (msg);
 
-  *unicode_msg = grub_malloc (msg_len * sizeof (grub_uint32_t));
+  *unicode_msg = grub_calloc (msg_len, sizeof (grub_uint32_t));
  
   if (!*unicode_msg)
     return -1;
@@ -488,7 +488,7 @@ grub_unicode_aglomerate_comb (const grub_uint32_t *in, grub_size_t inlen,
 	    }
 	  else
 	    {
-	      n = grub_malloc (sizeof (n[0]) * (out->ncomb + 1));
+	      n = grub_calloc (out->ncomb + 1, sizeof (n[0]));
 	      if (!n)
 		{
 		  grub_errno = GRUB_ERR_NONE;
@@ -842,7 +842,7 @@ grub_bidi_line_logical_to_visual (const grub_uint32_t *logical,
       }							\
   }
 
-  visual = grub_malloc (sizeof (visual[0]) * logical_len);
+  visual = grub_calloc (logical_len, sizeof (visual[0]));
   if (!visual)
     return -1;
 
@@ -1165,8 +1165,8 @@ grub_bidi_logical_to_visual (const grub_uint32_t *logical,
 {
   const grub_uint32_t *line_start = logical, *ptr;
   struct grub_unicode_glyph *visual_ptr;
-  *visual_out = visual_ptr = grub_malloc (3 * sizeof (visual_ptr[0])
-					  * (logical_len + 2));
+  *visual_out = visual_ptr = grub_calloc (logical_len + 2,
+					  3 * sizeof (visual_ptr[0]));
   if (!visual_ptr)
     return -1;
   for (ptr = logical; ptr <= logical + logical_len; ptr++)
diff --git a/grub-core/normal/cmdline.c b/grub-core/normal/cmdline.c
index c037d5050..c57242e2e 100644
--- a/grub-core/normal/cmdline.c
+++ b/grub-core/normal/cmdline.c
@@ -41,7 +41,7 @@ grub_err_t
 grub_set_history (int newsize)
 {
   grub_uint32_t **old_hist_lines = hist_lines;
-  hist_lines = grub_malloc (sizeof (grub_uint32_t *) * newsize);
+  hist_lines = grub_calloc (newsize, sizeof (grub_uint32_t *));
 
   /* Copy the old lines into the new buffer.  */
   if (old_hist_lines)
@@ -114,7 +114,7 @@ static void
 grub_history_set (int pos, grub_uint32_t *s, grub_size_t len)
 {
   grub_free (hist_lines[pos]);
-  hist_lines[pos] = grub_malloc ((len + 1) * sizeof (grub_uint32_t));
+  hist_lines[pos] = grub_calloc (len + 1, sizeof (grub_uint32_t));
   if (!hist_lines[pos])
     {
       grub_print_error ();
@@ -349,7 +349,7 @@ grub_cmdline_get (const char *prompt_translated)
   char *ret;
   unsigned nterms;
 
-  buf = grub_malloc (max_len * sizeof (grub_uint32_t));
+  buf = grub_calloc (max_len, sizeof (grub_uint32_t));
   if (!buf)
     return 0;
 
@@ -377,7 +377,7 @@ grub_cmdline_get (const char *prompt_translated)
     FOR_ACTIVE_TERM_OUTPUTS(cur)
       nterms++;
 
-    cl_terms = grub_malloc (sizeof (cl_terms[0]) * nterms);
+    cl_terms = grub_calloc (nterms, sizeof (cl_terms[0]));
     if (!cl_terms)
       {
 	grub_free (buf);
@@ -385,7 +385,7 @@ grub_cmdline_get (const char *prompt_translated)
       }
     cl_term_cur = cl_terms;
 
-    unicode_msg = grub_malloc (msg_len * sizeof (grub_uint32_t));
+    unicode_msg = grub_calloc (msg_len, sizeof (grub_uint32_t));
     if (!unicode_msg)
       {
 	grub_free (buf);
@@ -495,7 +495,7 @@ grub_cmdline_get (const char *prompt_translated)
 		grub_uint32_t *insert;
 
 		insertlen = grub_strlen (insertu8);
-		insert = grub_malloc ((insertlen + 1) * sizeof (grub_uint32_t));
+		insert = grub_calloc (insertlen + 1, sizeof (grub_uint32_t));
 		if (!insert)
 		  {
 		    grub_free (insertu8);
@@ -602,7 +602,7 @@ grub_cmdline_get (const char *prompt_translated)
 
 	      grub_free (kill_buf);
 
-	      kill_buf = grub_malloc ((n + 1) * sizeof(grub_uint32_t));
+	      kill_buf = grub_calloc (n + 1, sizeof (grub_uint32_t));
 	      if (grub_errno)
 		{
 		  grub_print_error ();
diff --git a/grub-core/normal/menu_entry.c b/grub-core/normal/menu_entry.c
index cdf3590a3..1993995be 100644
--- a/grub-core/normal/menu_entry.c
+++ b/grub-core/normal/menu_entry.c
@@ -95,8 +95,8 @@ init_line (struct screen *screen, struct line *linep)
 {
   linep->len = 0;
   linep->max_len = 80;
-  linep->buf = grub_malloc ((linep->max_len + 1) * sizeof (linep->buf[0]));
-  linep->pos = grub_zalloc (screen->nterms * sizeof (linep->pos[0]));
+  linep->buf = grub_calloc (linep->max_len + 1, sizeof (linep->buf[0]));
+  linep->pos = grub_calloc (screen->nterms, sizeof (linep->pos[0]));
   if (! linep->buf || !linep->pos)
     {
       grub_free (linep->buf);
@@ -287,7 +287,7 @@ update_screen (struct screen *screen, struct per_term_screen *term_screen,
 	  pos = linep->pos + (term_screen - screen->terms);
 
 	  if (!*pos)
-	    *pos = grub_zalloc ((linep->len + 1) * sizeof (**pos));
+	    *pos = grub_calloc (linep->len + 1, sizeof (**pos));
 
 	  if (i == region_start || linep == screen->lines + screen->line
 	      || (i > region_start && mode == ALL_LINES))
@@ -471,7 +471,7 @@ insert_string (struct screen *screen, const char *s, int update)
 
 	  /* Insert the string.  */
 	  current_linep = screen->lines + screen->line;
-	  unicode_msg = grub_malloc ((p - s) * sizeof (grub_uint32_t));
+	  unicode_msg = grub_calloc (p - s, sizeof (grub_uint32_t));
 
 	  if (!unicode_msg)
 	    return 0;
@@ -1023,7 +1023,7 @@ complete (struct screen *screen, int continuous, int update)
   if (completion_buffer.buf)
     {
       buflen = grub_strlen (completion_buffer.buf);
-      ucs4 = grub_malloc (sizeof (grub_uint32_t) * (buflen + 1));
+      ucs4 = grub_calloc (buflen + 1, sizeof (grub_uint32_t));
       
       if (!ucs4)
 	{
@@ -1268,7 +1268,7 @@ grub_menu_entry_run (grub_menu_entry_t entry)
   for (i = 0; i < (unsigned) screen->num_lines; i++)
     {
       grub_free (screen->lines[i].pos);
-      screen->lines[i].pos = grub_zalloc (screen->nterms * sizeof (screen->lines[i].pos[0]));
+      screen->lines[i].pos = grub_calloc (screen->nterms, sizeof (screen->lines[i].pos[0]));
       if (! screen->lines[i].pos)
 	{
 	  grub_print_error ();
@@ -1278,7 +1278,7 @@ grub_menu_entry_run (grub_menu_entry_t entry)
 	}
     }
 
-  screen->terms = grub_zalloc (screen->nterms * sizeof (screen->terms[0]));
+  screen->terms = grub_calloc (screen->nterms, sizeof (screen->terms[0]));
   if (!screen->terms)
     {
       grub_print_error ();
diff --git a/grub-core/normal/menu_text.c b/grub-core/normal/menu_text.c
index e22bb91f6..18240e76c 100644
--- a/grub-core/normal/menu_text.c
+++ b/grub-core/normal/menu_text.c
@@ -78,7 +78,7 @@ grub_print_message_indented_real (const char *msg, int margin_left,
   grub_size_t msg_len = grub_strlen (msg) + 2;
   int ret = 0;
 
-  unicode_msg = grub_malloc (msg_len * sizeof (grub_uint32_t));
+  unicode_msg = grub_calloc (msg_len, sizeof (grub_uint32_t));
  
   if (!unicode_msg)
     return 0;
@@ -211,7 +211,7 @@ print_entry (int y, int highlight, grub_menu_entry_t entry,
 
   title = entry ? entry->title : "";
   title_len = grub_strlen (title);
-  unicode_title = grub_malloc (title_len * sizeof (*unicode_title));
+  unicode_title = grub_calloc (title_len, sizeof (*unicode_title));
   if (! unicode_title)
     /* XXX How to show this error?  */
     return;
diff --git a/grub-core/normal/term.c b/grub-core/normal/term.c
index a1e5c5a0d..cc8c173b6 100644
--- a/grub-core/normal/term.c
+++ b/grub-core/normal/term.c
@@ -264,7 +264,7 @@ grub_term_save_pos (void)
   FOR_ACTIVE_TERM_OUTPUTS(cur)
     cnt++;
 
-  ret = grub_malloc (cnt * sizeof (ret[0]));
+  ret = grub_calloc (cnt, sizeof (ret[0]));
   if (!ret)
     return NULL;
 
@@ -1013,7 +1013,7 @@ grub_xnputs (const char *str, grub_size_t msg_len)
 
   grub_error_push ();
 
-  unicode_str = grub_malloc (msg_len * sizeof (grub_uint32_t));
+  unicode_str = grub_calloc (msg_len, sizeof (grub_uint32_t));
  
   grub_error_pop ();
 
diff --git a/grub-core/osdep/linux/getroot.c b/grub-core/osdep/linux/getroot.c
index 6d9f4e5fa..001b818fe 100644
--- a/grub-core/osdep/linux/getroot.c
+++ b/grub-core/osdep/linux/getroot.c
@@ -168,7 +168,7 @@ grub_util_raid_getmembers (const char *name, int bootable)
   if (ret != 0)
     grub_util_error (_("ioctl GET_ARRAY_INFO error: %s"), strerror (errno));
 
-  devicelist = xmalloc ((info.nr_disks + 1) * sizeof (char *));
+  devicelist = xcalloc (info.nr_disks + 1, sizeof (char *));
 
   for (i = 0, j = 0; j < info.nr_disks; i++)
     {
@@ -241,7 +241,7 @@ grub_find_root_devices_from_btrfs (const char *dir)
       return NULL;
     }
 
-  ret = xmalloc ((fsi.num_devices + 1) * sizeof (ret[0]));
+  ret = xcalloc (fsi.num_devices + 1, sizeof (ret[0]));
 
   for (i = 1; i <= fsi.max_id && j < fsi.num_devices; i++)
     {
@@ -396,7 +396,7 @@ grub_find_root_devices_from_mountinfo (const char *dir, char **relroot)
   if (relroot)
     *relroot = NULL;
 
-  entries = xmalloc (entry_max * sizeof (*entries));
+  entries = xcalloc (entry_max, sizeof (*entries));
 
 again:
   fp = grub_util_fopen ("/proc/self/mountinfo", "r");
diff --git a/grub-core/osdep/unix/config.c b/grub-core/osdep/unix/config.c
index 65effa9f3..7d6325138 100644
--- a/grub-core/osdep/unix/config.c
+++ b/grub-core/osdep/unix/config.c
@@ -89,7 +89,7 @@ grub_util_load_config (struct grub_util_config *cfg)
   argv[0] = "sh";
   argv[1] = "-c";
 
-  script = xmalloc (4 * strlen (cfgfile) + 300);
+  script = xcalloc (4, strlen (cfgfile) + 300);
 
   ptr = script;
   memcpy (ptr, ". '", 3);
diff --git a/grub-core/osdep/windows/getroot.c b/grub-core/osdep/windows/getroot.c
index 661d95461..eada663b2 100644
--- a/grub-core/osdep/windows/getroot.c
+++ b/grub-core/osdep/windows/getroot.c
@@ -59,7 +59,7 @@ grub_get_mount_point (const TCHAR *path)
 
   for (ptr = path; *ptr; ptr++);
   allocsize = (ptr - path + 10) * 2;
-  out = xmalloc (allocsize * sizeof (out[0]));
+  out = xcalloc (allocsize, sizeof (out[0]));
 
   /* When pointing to EFI system partition GetVolumePathName fails
      for ESP root and returns abberant information for everything
diff --git a/grub-core/osdep/windows/hostdisk.c b/grub-core/osdep/windows/hostdisk.c
index 355100789..0be327394 100644
--- a/grub-core/osdep/windows/hostdisk.c
+++ b/grub-core/osdep/windows/hostdisk.c
@@ -111,7 +111,7 @@ grub_util_get_windows_path_real (const char *path)
 
   while (1)
     {
-      fpa = xmalloc (alloc * sizeof (fpa[0]));
+      fpa = xcalloc (alloc, sizeof (fpa[0]));
 
       len = GetFullPathName (tpath, alloc, fpa, NULL);
       if (len >= alloc)
@@ -399,7 +399,7 @@ grub_util_fd_opendir (const char *name)
   for (l = 0; name_windows[l]; l++);
   for (l--; l >= 0 && (name_windows[l] == '\\' || name_windows[l] == '/'); l--);
   l++;
-  pattern = xmalloc ((l + 3) * sizeof (pattern[0]));
+  pattern = xcalloc (l + 3, sizeof (pattern[0]));
   memcpy (pattern, name_windows, l * sizeof (pattern[0]));
   pattern[l] = '\\';
   pattern[l + 1] = '*';
diff --git a/grub-core/osdep/windows/init.c b/grub-core/osdep/windows/init.c
index e8ffd62c6..6297de632 100644
--- a/grub-core/osdep/windows/init.c
+++ b/grub-core/osdep/windows/init.c
@@ -161,7 +161,7 @@ grub_util_host_init (int *argc __attribute__ ((unused)),
   LPWSTR *targv;
 
   targv = CommandLineToArgvW (tcmdline, argc);
-  *argv = xmalloc ((*argc + 1) * sizeof (argv[0]));
+  *argv = xcalloc (*argc + 1, sizeof (argv[0]));
 
   for (i = 0; i < *argc; i++)
     (*argv)[i] = grub_util_tchar_to_utf8 (targv[i]); 
diff --git a/grub-core/osdep/windows/platform.c b/grub-core/osdep/windows/platform.c
index 7eb53fe01..1ef86bf58 100644
--- a/grub-core/osdep/windows/platform.c
+++ b/grub-core/osdep/windows/platform.c
@@ -225,8 +225,8 @@ grub_install_register_efi (grub_device_t efidir_grub_dev,
     grub_util_error ("%s", _("no EFI routines are available when running in BIOS mode"));
 
   distrib8_len = grub_strlen (efi_distributor);
-  distributor16 = xmalloc ((distrib8_len + 1) * GRUB_MAX_UTF16_PER_UTF8
-			   * sizeof (grub_uint16_t));
+  distributor16 = xcalloc (distrib8_len + 1,
+			   GRUB_MAX_UTF16_PER_UTF8 * sizeof (grub_uint16_t));
   distrib16_len = grub_utf8_to_utf16 (distributor16, distrib8_len * GRUB_MAX_UTF16_PER_UTF8,
 				      (const grub_uint8_t *) efi_distributor,
 				      distrib8_len, 0);
diff --git a/grub-core/osdep/windows/relpath.c b/grub-core/osdep/windows/relpath.c
index cb0861744..478e8ef14 100644
--- a/grub-core/osdep/windows/relpath.c
+++ b/grub-core/osdep/windows/relpath.c
@@ -72,7 +72,7 @@ grub_make_system_path_relative_to_its_root (const char *path)
       if (dirwindows[0] && dirwindows[1] == ':')
 	offset = 2;
     }
-  ret = xmalloc (sizeof (ret[0]) * (flen - offset + 2));
+  ret = xcalloc (flen - offset + 2, sizeof (ret[0]));
   if (dirwindows[offset] != '\\'
       && dirwindows[offset] != '/'
       && dirwindows[offset])
diff --git a/grub-core/partmap/gpt.c b/grub-core/partmap/gpt.c
index 103f6796f..72a2e37cd 100644
--- a/grub-core/partmap/gpt.c
+++ b/grub-core/partmap/gpt.c
@@ -199,7 +199,7 @@ gpt_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors,
   *nsectors = ctx.len;
   if (*nsectors > max_nsectors)
     *nsectors = max_nsectors;
-  *sectors = grub_malloc (*nsectors * sizeof (**sectors));
+  *sectors = grub_calloc (*nsectors, sizeof (**sectors));
   if (!*sectors)
     return grub_errno;
   for (i = 0; i < *nsectors; i++)
diff --git a/grub-core/partmap/msdos.c b/grub-core/partmap/msdos.c
index 7b8e45076..ee3f24982 100644
--- a/grub-core/partmap/msdos.c
+++ b/grub-core/partmap/msdos.c
@@ -337,7 +337,7 @@ pc_partition_map_embed (struct grub_disk *disk, unsigned int *nsectors,
       avail_nsectors = *nsectors;
       if (*nsectors > max_nsectors)
 	*nsectors = max_nsectors;
-      *sectors = grub_malloc (*nsectors * sizeof (**sectors));
+      *sectors = grub_calloc (*nsectors, sizeof (**sectors));
       if (!*sectors)
 	return grub_errno;
       for (i = 0; i < *nsectors; i++)
diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c
index 8cc61ee7d..8a9161cc8 100644
--- a/grub-core/script/execute.c
+++ b/grub-core/script/execute.c
@@ -553,7 +553,7 @@ gettext_append (struct grub_script_argv *result, const char *orig_str)
   for (iptr = orig_str; *iptr; iptr++)
     if (*iptr == '$')
       dollar_cnt++;
-  ctx.allowed_strings = grub_malloc (sizeof (ctx.allowed_strings[0]) * dollar_cnt);
+  ctx.allowed_strings = grub_calloc (dollar_cnt, sizeof (ctx.allowed_strings[0]));
 
   if (parse_string (orig_str, gettext_save_allow, &ctx, 0))
     goto fail;
diff --git a/grub-core/tests/fake_input.c b/grub-core/tests/fake_input.c
index 2d6085298..b5eb516be 100644
--- a/grub-core/tests/fake_input.c
+++ b/grub-core/tests/fake_input.c
@@ -49,7 +49,7 @@ grub_terminal_input_fake_sequence (int *seq_in, int nseq_in)
     saved = grub_term_inputs;
   if (seq)
     grub_free (seq);
-  seq = grub_malloc (nseq_in * sizeof (seq[0]));
+  seq = grub_calloc (nseq_in, sizeof (seq[0]));
   if (!seq)
     return;
 
diff --git a/grub-core/tests/video_checksum.c b/grub-core/tests/video_checksum.c
index 74d5b65e5..44d081069 100644
--- a/grub-core/tests/video_checksum.c
+++ b/grub-core/tests/video_checksum.c
@@ -336,7 +336,7 @@ grub_video_capture_write_bmp (const char *fname,
     {
     case 4:
       {
-	grub_uint8_t *buffer = xmalloc (mode_info->width * 3);
+	grub_uint8_t *buffer = xcalloc (3, mode_info->width);
 	grub_uint32_t rmask = ((1 << mode_info->red_mask_size) - 1);
 	grub_uint32_t gmask = ((1 << mode_info->green_mask_size) - 1);
 	grub_uint32_t bmask = ((1 << mode_info->blue_mask_size) - 1);
@@ -367,7 +367,7 @@ grub_video_capture_write_bmp (const char *fname,
       }
     case 3:
       {
-	grub_uint8_t *buffer = xmalloc (mode_info->width * 3);
+	grub_uint8_t *buffer = xcalloc (3, mode_info->width);
 	grub_uint32_t rmask = ((1 << mode_info->red_mask_size) - 1);
 	grub_uint32_t gmask = ((1 << mode_info->green_mask_size) - 1);
 	grub_uint32_t bmask = ((1 << mode_info->blue_mask_size) - 1);
@@ -407,7 +407,7 @@ grub_video_capture_write_bmp (const char *fname,
       }
     case 2:
       {
-	grub_uint8_t *buffer = xmalloc (mode_info->width * 3);
+	grub_uint8_t *buffer = xcalloc (3, mode_info->width);
 	grub_uint16_t rmask = ((1 << mode_info->red_mask_size) - 1);
 	grub_uint16_t gmask = ((1 << mode_info->green_mask_size) - 1);
 	grub_uint16_t bmask = ((1 << mode_info->blue_mask_size) - 1);
diff --git a/grub-core/video/capture.c b/grub-core/video/capture.c
index 4f83c7441..4d3195e01 100644
--- a/grub-core/video/capture.c
+++ b/grub-core/video/capture.c
@@ -89,7 +89,7 @@ grub_video_capture_start (const struct grub_video_mode_info *mode_info,
   framebuffer.mode_info = *mode_info;
   framebuffer.mode_info.blit_format = grub_video_get_blit_format (&framebuffer.mode_info);
 
-  framebuffer.ptr = grub_malloc (framebuffer.mode_info.height * framebuffer.mode_info.pitch);
+  framebuffer.ptr = grub_calloc (framebuffer.mode_info.height, framebuffer.mode_info.pitch);
   if (!framebuffer.ptr)
     return grub_errno;
   
diff --git a/grub-core/video/emu/sdl.c b/grub-core/video/emu/sdl.c
index a2f639f66..0ebab6f57 100644
--- a/grub-core/video/emu/sdl.c
+++ b/grub-core/video/emu/sdl.c
@@ -172,7 +172,7 @@ grub_video_sdl_set_palette (unsigned int start, unsigned int count,
       if (start + count > mode_info.number_of_colors)
 	count = mode_info.number_of_colors - start;
 
-      tmp = grub_malloc (count * sizeof (tmp[0]));
+      tmp = grub_calloc (count, sizeof (tmp[0]));
       for (i = 0; i < count; i++)
 	{
 	  tmp[i].r = palette_data[i].r;
diff --git a/grub-core/video/i386/pc/vga.c b/grub-core/video/i386/pc/vga.c
index 01f47112d..b2f776c99 100644
--- a/grub-core/video/i386/pc/vga.c
+++ b/grub-core/video/i386/pc/vga.c
@@ -127,7 +127,7 @@ grub_video_vga_setup (unsigned int width, unsigned int height,
 
   vga_height = height ? : 480;
 
-  framebuffer.temporary_buffer = grub_malloc (vga_height * VGA_WIDTH);
+  framebuffer.temporary_buffer = grub_calloc (vga_height, VGA_WIDTH);
   framebuffer.front_page = 0;
   framebuffer.back_page = 0;
   if (!framebuffer.temporary_buffer)
diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c
index 777e71334..61bd64537 100644
--- a/grub-core/video/readers/png.c
+++ b/grub-core/video/readers/png.c
@@ -309,7 +309,7 @@ grub_png_decode_image_header (struct grub_png_data *data)
   if (data->is_16bit || data->is_gray || data->is_palette)
 #endif
     {
-      data->image_data = grub_malloc (data->image_height * data->row_bytes);
+      data->image_data = grub_calloc (data->image_height, data->row_bytes);
       if (grub_errno)
         return grub_errno;
 
diff --git a/include/grub/unicode.h b/include/grub/unicode.h
index a0403e91f..4de986a85 100644
--- a/include/grub/unicode.h
+++ b/include/grub/unicode.h
@@ -293,7 +293,7 @@ grub_unicode_glyph_dup (const struct grub_unicode_glyph *in)
   grub_memcpy (out, in, sizeof (*in));
   if (in->ncomb > ARRAY_SIZE (out->combining_inline))
     {
-      out->combining_ptr = grub_malloc (in->ncomb * sizeof (out->combining_ptr[0]));
+      out->combining_ptr = grub_calloc (in->ncomb, sizeof (out->combining_ptr[0]));
       if (!out->combining_ptr)
 	{
 	  grub_free (out);
@@ -315,7 +315,7 @@ grub_unicode_set_glyph (struct grub_unicode_glyph *out,
   grub_memcpy (out, in, sizeof (*in));
   if (in->ncomb > ARRAY_SIZE (out->combining_inline))
     {
-      out->combining_ptr = grub_malloc (in->ncomb * sizeof (out->combining_ptr[0]));
+      out->combining_ptr = grub_calloc (in->ncomb, sizeof (out->combining_ptr[0]));
       if (!out->combining_ptr)
 	return;
       grub_memcpy (out->combining_ptr, in->combining_ptr,
diff --git a/util/getroot.c b/util/getroot.c
index 847406fba..a5eaa64fd 100644
--- a/util/getroot.c
+++ b/util/getroot.c
@@ -200,7 +200,7 @@ make_device_name (const char *drive)
   char *ret, *ptr;
   const char *iptr;
 
-  ret = xmalloc (strlen (drive) * 2);
+  ret = xcalloc (2, strlen (drive));
   ptr = ret;
   for (iptr = drive; *iptr; iptr++)
     {
diff --git a/util/grub-file.c b/util/grub-file.c
index 50c18b683..b2e7dd69f 100644
--- a/util/grub-file.c
+++ b/util/grub-file.c
@@ -54,7 +54,7 @@ main (int argc, char *argv[])
 
   grub_util_host_init (&argc, &argv);
 
-  argv2 = xmalloc (argc * sizeof (argv2[0]));
+  argv2 = xcalloc (argc, sizeof (argv2[0]));
 
   if (argc == 2 && strcmp (argv[1], "--version") == 0)
     {
diff --git a/util/grub-fstest.c b/util/grub-fstest.c
index 7f14620cd..838656420 100644
--- a/util/grub-fstest.c
+++ b/util/grub-fstest.c
@@ -650,7 +650,7 @@ argp_parser (int key, char *arg, struct argp_state *state)
   if (args_count < num_disks)
     {
       if (args_count == 0)
-	images = xmalloc (num_disks * sizeof (images[0]));
+	images = xcalloc (num_disks, sizeof (images[0]));
       images[args_count] = grub_canonicalize_file_name (arg);
       args_count++;
       return 0;
@@ -734,7 +734,7 @@ main (int argc, char *argv[])
 
   grub_util_host_init (&argc, &argv);
 
-  args = xmalloc (argc * sizeof (args[0]));
+  args = xcalloc (argc, sizeof (args[0]));
 
   argp_parse (&argp, argc, argv, 0, 0, 0);
 
diff --git a/util/grub-install-common.c b/util/grub-install-common.c
index ca0ac612a..0295d40f5 100644
--- a/util/grub-install-common.c
+++ b/util/grub-install-common.c
@@ -286,7 +286,7 @@ handle_install_list (struct install_list *il, const char *val,
       il->n_entries++;
     }
   il->n_alloc = il->n_entries + 1;
-  il->entries = xmalloc (il->n_alloc * sizeof (il->entries[0]));
+  il->entries = xcalloc (il->n_alloc, sizeof (il->entries[0]));
   ptr = val;
   for (ce = il->entries; ; ce++)
     {
diff --git a/util/grub-install.c b/util/grub-install.c
index 8970b73aa..a35a2e2e8 100644
--- a/util/grub-install.c
+++ b/util/grub-install.c
@@ -634,7 +634,7 @@ device_map_check_duplicates (const char *dev_map)
   if (! fp)
     return;
 
-  d = xmalloc (alloced * sizeof (d[0]));
+  d = xcalloc (alloced, sizeof (d[0]));
 
   while (fgets (buf, sizeof (buf), fp))
     {
@@ -1268,7 +1268,7 @@ main (int argc, char *argv[])
       ndev++;
     }
 
-  grub_drives = xmalloc (sizeof (grub_drives[0]) * (ndev + 1)); 
+  grub_drives = xcalloc (ndev + 1, sizeof (grub_drives[0]));
 
   for (curdev = grub_devices, curdrive = grub_drives; *curdev; curdev++,
        curdrive++)
diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c
index ab6dfab79..00f49ccaa 100644
--- a/util/grub-mkimagexx.c
+++ b/util/grub-mkimagexx.c
@@ -2294,10 +2294,8 @@ SUFFIX (grub_mkimage_load_image) (const char *kernel_path,
 		      + grub_host_to_target16 (e->e_shstrndx) * smd.section_entsize);
   smd.strtab = (char *) e + grub_host_to_target_addr (s->sh_offset);
 
-  smd.addrs = xmalloc (sizeof (*smd.addrs) * smd.num_sections);
-  memset (smd.addrs, 0, sizeof (*smd.addrs) * smd.num_sections);
-  smd.vaddrs = xmalloc (sizeof (*smd.vaddrs) * smd.num_sections);
-  memset (smd.vaddrs, 0, sizeof (*smd.vaddrs) * smd.num_sections);
+  smd.addrs = xcalloc (smd.num_sections, sizeof (*smd.addrs));
+  smd.vaddrs = xcalloc (smd.num_sections, sizeof (*smd.vaddrs));
 
   SUFFIX (locate_sections) (e, kernel_path, &smd, layout, image_target);
 
diff --git a/util/grub-mkrescue.c b/util/grub-mkrescue.c
index ce2cbc4f1..51831027f 100644
--- a/util/grub-mkrescue.c
+++ b/util/grub-mkrescue.c
@@ -441,8 +441,8 @@ main (int argc, char *argv[])
   xorriso = xstrdup ("xorriso");
   label_font = grub_util_path_concat (2, pkgdatadir, "unicode.pf2");
 
-  argp_argv = xmalloc (sizeof (argp_argv[0]) * argc);
-  xorriso_tail_argv = xmalloc (sizeof (argp_argv[0]) * argc);
+  argp_argv = xcalloc (argc, sizeof (argp_argv[0]));
+  xorriso_tail_argv = xcalloc (argc, sizeof (argp_argv[0]));
 
   xorriso_tail_argc = 0;
   /* Program name */
diff --git a/util/grub-mkstandalone.c b/util/grub-mkstandalone.c
index 4907d44c0..edf309717 100644
--- a/util/grub-mkstandalone.c
+++ b/util/grub-mkstandalone.c
@@ -296,7 +296,7 @@ main (int argc, char *argv[])
   grub_util_host_init (&argc, &argv);
   grub_util_disable_fd_syncs ();
 
-  files = xmalloc ((argc + 1) * sizeof (files[0]));
+  files = xcalloc (argc + 1, sizeof (files[0]));
 
   argp_parse (&argp, argc, argv, 0, 0, 0);
 
diff --git a/util/grub-pe2elf.c b/util/grub-pe2elf.c
index 0d4084a10..11331294f 100644
--- a/util/grub-pe2elf.c
+++ b/util/grub-pe2elf.c
@@ -100,9 +100,9 @@ write_section_data (FILE* fp, const char *name, char *image,
   char *pe_strtab = (image + pe_chdr->symtab_offset
 		     + pe_chdr->num_symbols * sizeof (struct grub_pe32_symbol));
 
-  section_map = xmalloc ((2 * pe_chdr->num_sections + 5) * sizeof (int));
+  section_map = xcalloc (2 * pe_chdr->num_sections + 5, sizeof (int));
   section_map[0] = 0;
-  shdr = xmalloc ((2 * pe_chdr->num_sections + 5) * sizeof (shdr[0]));
+  shdr = xcalloc (2 * pe_chdr->num_sections + 5, sizeof (shdr[0]));
   idx = 1;
   idx_reloc = pe_chdr->num_sections + 1;
 
@@ -233,7 +233,7 @@ write_reloc_section (FILE* fp, const char *name, char *image,
 
       pe_sec = pe_shdr + shdr[i].sh_link;
       pe_rel = (struct grub_pe32_reloc *) (image + pe_sec->relocations_offset);
-      rel = (elf_reloc_t *) xmalloc (pe_sec->num_relocations * sizeof (elf_reloc_t));
+      rel = (elf_reloc_t *) xcalloc (pe_sec->num_relocations, sizeof (elf_reloc_t));
       num_rels = 0;
       modified = 0;
 
@@ -365,12 +365,10 @@ write_symbol_table (FILE* fp, const char *name, char *image,
   pe_symtab = (struct grub_pe32_symbol *) (image + pe_chdr->symtab_offset);
   pe_strtab = (char *) (pe_symtab + pe_chdr->num_symbols);
 
-  symtab = (Elf_Sym *) xmalloc ((pe_chdr->num_symbols + 1) *
-				sizeof (Elf_Sym));
-  memset (symtab, 0, (pe_chdr->num_symbols + 1) * sizeof (Elf_Sym));
+  symtab = (Elf_Sym *) xcalloc (pe_chdr->num_symbols + 1, sizeof (Elf_Sym));
   num_syms = 1;
 
-  symtab_map = (int *) xmalloc (pe_chdr->num_symbols * sizeof (int));
+  symtab_map = (int *) xcalloc (pe_chdr->num_symbols, sizeof (int));
 
   for (i = 0; i < (int) pe_chdr->num_symbols;
        i += pe_symtab->num_aux + 1, pe_symtab += pe_symtab->num_aux + 1)
diff --git a/util/grub-probe.c b/util/grub-probe.c
index 81d27eead..cbe6ed94c 100644
--- a/util/grub-probe.c
+++ b/util/grub-probe.c
@@ -361,8 +361,8 @@ probe (const char *path, char **device_names, char delim)
       grub_util_pull_device (*curdev);
       ndev++;
     }
-  
-  drives_names = xmalloc (sizeof (drives_names[0]) * (ndev + 1)); 
+
+  drives_names = xcalloc (ndev + 1, sizeof (drives_names[0]));
 
   for (curdev = device_names, curdrive = drives_names; *curdev; curdev++,
        curdrive++)
-- 
2.11.0



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

* [SECURITY PATCH 05/28] malloc: Use overflow checking primitives where we do complex allocations
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (3 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 04/28] calloc: Use calloc() at most places Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2021-09-10 16:10   ` Glenn Washburn
  2020-07-29 17:00 ` [SECURITY PATCH 06/28] iso9660: Don't leak memory on realloc() failures Daniel Kiper
                   ` (23 subsequent siblings)
  28 siblings, 1 reply; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Peter Jones <pjones@redhat.com>

This attempts to fix the places where we do the following where
arithmetic_expr may include unvalidated data:

  X = grub_malloc(arithmetic_expr);

It accomplishes this by doing the arithmetic ahead of time using grub_add(),
grub_sub(), grub_mul() and testing for overflow before proceeding.

Among other issues, this fixes:
  - allocation of integer overflow in grub_video_bitmap_create()
    reported by Chris Coulson,
  - allocation of integer overflow in grub_png_decode_image_header()
    reported by Chris Coulson,
  - allocation of integer overflow in grub_squash_read_symlink()
    reported by Chris Coulson,
  - allocation of integer overflow in grub_ext2_read_symlink()
    reported by Chris Coulson,
  - allocation of integer overflow in read_section_as_string()
    reported by Chris Coulson.

Fixes: CVE-2020-14309, CVE-2020-14310, CVE-2020-14311

Signed-off-by: Peter Jones <pjones@redhat.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/commands/legacycfg.c | 29 +++++++++++++++++++-----
 grub-core/commands/wildcard.c  | 36 ++++++++++++++++++++++++-----
 grub-core/disk/ldm.c           | 32 ++++++++++++++++++--------
 grub-core/font/font.c          |  7 +++++-
 grub-core/fs/btrfs.c           | 28 +++++++++++++++--------
 grub-core/fs/ext2.c            | 10 ++++++++-
 grub-core/fs/iso9660.c         | 51 +++++++++++++++++++++++++++++-------------
 grub-core/fs/sfs.c             | 27 +++++++++++++++++-----
 grub-core/fs/squash4.c         | 45 ++++++++++++++++++++++++++++---------
 grub-core/fs/udf.c             | 41 +++++++++++++++++++++------------
 grub-core/fs/xfs.c             | 11 +++++----
 grub-core/fs/zfs/zfs.c         | 22 ++++++++++++------
 grub-core/fs/zfs/zfscrypt.c    |  7 +++++-
 grub-core/lib/arg.c            | 20 +++++++++++++++--
 grub-core/loader/i386/bsd.c    |  8 ++++++-
 grub-core/net/dns.c            |  9 +++++++-
 grub-core/normal/charset.c     | 10 +++++++--
 grub-core/normal/cmdline.c     | 14 ++++++++++--
 grub-core/normal/menu_entry.c  | 13 +++++++++--
 grub-core/script/argv.c        | 16 +++++++++++--
 grub-core/script/lexer.c       | 21 ++++++++++++++---
 grub-core/video/bitmap.c       | 25 +++++++++++++--------
 grub-core/video/readers/png.c  | 13 +++++++++--
 23 files changed, 382 insertions(+), 113 deletions(-)

diff --git a/grub-core/commands/legacycfg.c b/grub-core/commands/legacycfg.c
index 5e3ec0d5e..cc5971f4d 100644
--- a/grub-core/commands/legacycfg.c
+++ b/grub-core/commands/legacycfg.c
@@ -32,6 +32,7 @@
 #include <grub/auth.h>
 #include <grub/disk.h>
 #include <grub/partition.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -104,13 +105,22 @@ legacy_file (const char *filename)
 	if (newsuffix)
 	  {
 	    char *t;
-	    
+	    grub_size_t sz;
+
+	    if (grub_add (grub_strlen (suffix), grub_strlen (newsuffix), &sz) ||
+		grub_add (sz, 1, &sz))
+	      {
+		grub_errno = GRUB_ERR_OUT_OF_RANGE;
+		goto fail_0;
+	      }
+
 	    t = suffix;
-	    suffix = grub_realloc (suffix, grub_strlen (suffix)
-				   + grub_strlen (newsuffix) + 1);
+	    suffix = grub_realloc (suffix, sz);
 	    if (!suffix)
 	      {
 		grub_free (t);
+
+ fail_0:
 		grub_free (entrysrc);
 		grub_free (parsed);
 		grub_free (newsuffix);
@@ -154,13 +164,22 @@ legacy_file (const char *filename)
 	  else
 	    {
 	      char *t;
+	      grub_size_t sz;
+
+	      if (grub_add (grub_strlen (entrysrc), grub_strlen (parsed), &sz) ||
+		  grub_add (sz, 1, &sz))
+		{
+		  grub_errno = GRUB_ERR_OUT_OF_RANGE;
+		  goto fail_1;
+		}
 
 	      t = entrysrc;
-	      entrysrc = grub_realloc (entrysrc, grub_strlen (entrysrc)
-				       + grub_strlen (parsed) + 1);
+	      entrysrc = grub_realloc (entrysrc, sz);
 	      if (!entrysrc)
 		{
 		  grub_free (t);
+
+ fail_1:
 		  grub_free (parsed);
 		  grub_free (suffix);
 		  return grub_errno;
diff --git a/grub-core/commands/wildcard.c b/grub-core/commands/wildcard.c
index 4a106ca04..cc3290311 100644
--- a/grub-core/commands/wildcard.c
+++ b/grub-core/commands/wildcard.c
@@ -23,6 +23,7 @@
 #include <grub/file.h>
 #include <grub/device.h>
 #include <grub/script_sh.h>
+#include <grub/safemath.h>
 
 #include <regex.h>
 
@@ -48,6 +49,7 @@ merge (char **dest, char **ps)
   int i;
   int j;
   char **p;
+  grub_size_t sz;
 
   if (! dest)
     return ps;
@@ -60,7 +62,12 @@ merge (char **dest, char **ps)
   for (j = 0; ps[j]; j++)
     ;
 
-  p = grub_realloc (dest, sizeof (char*) * (i + j + 1));
+  if (grub_add (i, j, &sz) ||
+      grub_add (sz, 1, &sz) ||
+      grub_mul (sz, sizeof (char *), &sz))
+    return dest;
+
+  p = grub_realloc (dest, sz);
   if (! p)
     {
       grub_free (dest);
@@ -115,8 +122,15 @@ make_regex (const char *start, const char *end, regex_t *regexp)
   char ch;
   int i = 0;
   unsigned len = end - start;
-  char *buffer = grub_malloc (len * 2 + 2 + 1); /* worst case size. */
+  char *buffer;
+  grub_size_t sz;
 
+  /* Worst case size is (len * 2 + 2 + 1). */
+  if (grub_mul (len, 2, &sz) ||
+      grub_add (sz, 3, &sz))
+    return 1;
+
+  buffer = grub_malloc (sz);
   if (! buffer)
     return 1;
 
@@ -226,6 +240,7 @@ match_devices_iter (const char *name, void *data)
   struct match_devices_ctx *ctx = data;
   char **t;
   char *buffer;
+  grub_size_t sz;
 
   /* skip partitions if asked to. */
   if (ctx->noparts && grub_strchr (name, ','))
@@ -239,11 +254,16 @@ match_devices_iter (const char *name, void *data)
   if (regexec (ctx->regexp, buffer, 0, 0, 0))
     {
       grub_dprintf ("expand", "not matched\n");
+ fail:
       grub_free (buffer);
       return 0;
     }
 
-  t = grub_realloc (ctx->devs, sizeof (char*) * (ctx->ndev + 2));
+  if (grub_add (ctx->ndev, 2, &sz) ||
+      grub_mul (sz, sizeof (char *), &sz))
+    goto fail;
+
+  t = grub_realloc (ctx->devs, sz);
   if (! t)
     {
       grub_free (buffer);
@@ -300,6 +320,7 @@ match_files_iter (const char *name,
   struct match_files_ctx *ctx = data;
   char **t;
   char *buffer;
+  grub_size_t sz;
 
   /* skip . and .. names */
   if (grub_strcmp(".", name) == 0 || grub_strcmp("..", name) == 0)
@@ -315,9 +336,14 @@ match_files_iter (const char *name,
   if (! buffer)
     return 1;
 
-  t = grub_realloc (ctx->files, sizeof (char*) * (ctx->nfile + 2));
-  if (! t)
+  if (grub_add (ctx->nfile, 2, &sz) ||
+      grub_mul (sz, sizeof (char *), &sz))
+    goto fail;
+
+  t = grub_realloc (ctx->files, sz);
+  if (!t)
     {
+ fail:
       grub_free (buffer);
       return 1;
     }
diff --git a/grub-core/disk/ldm.c b/grub-core/disk/ldm.c
index e6323701a..58f8a53e1 100644
--- a/grub-core/disk/ldm.c
+++ b/grub-core/disk/ldm.c
@@ -25,6 +25,7 @@
 #include <grub/msdos_partition.h>
 #include <grub/gpt_partition.h>
 #include <grub/i18n.h>
+#include <grub/safemath.h>
 
 #ifdef GRUB_UTIL
 #include <grub/emu/misc.h>
@@ -289,6 +290,7 @@ make_vg (grub_disk_t disk,
       struct grub_ldm_vblk vblk[GRUB_DISK_SECTOR_SIZE
 				/ sizeof (struct grub_ldm_vblk)];
       unsigned i;
+      grub_size_t sz;
       err = grub_disk_read (disk, cursec, 0,
 			    sizeof(vblk), &vblk);
       if (err)
@@ -350,7 +352,13 @@ make_vg (grub_disk_t disk,
 	      grub_free (lv);
 	      goto fail2;
 	    }
-	  lv->name = grub_malloc (*ptr + 1);
+	  if (grub_add (*ptr, 1, &sz))
+	    {
+	      grub_free (lv->internal_id);
+	      grub_free (lv);
+	      goto fail2;
+	    }
+	  lv->name = grub_malloc (sz);
 	  if (!lv->name)
 	    {
 	      grub_free (lv->internal_id);
@@ -599,10 +607,13 @@ make_vg (grub_disk_t disk,
 	  if (lv->segments->node_alloc == lv->segments->node_count)
 	    {
 	      void *t;
-	      lv->segments->node_alloc *= 2; 
-	      t = grub_realloc (lv->segments->nodes,
-				sizeof (*lv->segments->nodes)
-				* lv->segments->node_alloc);
+	      grub_size_t sz;
+
+	      if (grub_mul (lv->segments->node_alloc, 2, &lv->segments->node_alloc) ||
+		  grub_mul (lv->segments->node_alloc, sizeof (*lv->segments->nodes), &sz))
+		goto fail2;
+
+	      t = grub_realloc (lv->segments->nodes, sz);
 	      if (!t)
 		goto fail2;
 	      lv->segments->nodes = t;
@@ -723,10 +734,13 @@ make_vg (grub_disk_t disk,
 	      if (comp->segment_alloc == comp->segment_count)
 		{
 		  void *t;
-		  comp->segment_alloc *= 2;
-		  t = grub_realloc (comp->segments,
-				    comp->segment_alloc
-				    * sizeof (*comp->segments));
+		  grub_size_t sz;
+
+		  if (grub_mul (comp->segment_alloc, 2, &comp->segment_alloc) ||
+		      grub_mul (comp->segment_alloc, sizeof (*comp->segments), &sz))
+		    goto fail2;
+
+		  t = grub_realloc (comp->segments, sz);
 		  if (!t)
 		    goto fail2;
 		  comp->segments = t;
diff --git a/grub-core/font/font.c b/grub-core/font/font.c
index 8e118b315..5edb477ac 100644
--- a/grub-core/font/font.c
+++ b/grub-core/font/font.c
@@ -30,6 +30,7 @@
 #include <grub/unicode.h>
 #include <grub/fontformat.h>
 #include <grub/env.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -360,9 +361,13 @@ static char *
 read_section_as_string (struct font_file_section *section)
 {
   char *str;
+  grub_size_t sz;
   grub_ssize_t ret;
 
-  str = grub_malloc (section->length + 1);
+  if (grub_add (section->length, 1, &sz))
+    return NULL;
+
+  str = grub_malloc (sz);
   if (!str)
     return 0;
 
diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 4b8380439..27339bdb3 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -40,6 +40,7 @@
 #include <grub/btrfs.h>
 #include <grub/crypto.h>
 #include <grub/diskfilter.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -331,9 +332,13 @@ save_ref (struct grub_btrfs_leaf_descriptor *desc,
   if (desc->allocated < desc->depth)
     {
       void *newdata;
-      desc->allocated *= 2;
-      newdata = grub_realloc (desc->data, sizeof (desc->data[0])
-			      * desc->allocated);
+      grub_size_t sz;
+
+      if (grub_mul (desc->allocated, 2, &desc->allocated) ||
+	  grub_mul (desc->allocated, sizeof (desc->data[0]), &sz))
+	return GRUB_ERR_OUT_OF_RANGE;
+
+      newdata = grub_realloc (desc->data, sz);
       if (!newdata)
 	return grub_errno;
       desc->data = newdata;
@@ -624,16 +629,21 @@ find_device (struct grub_btrfs_data *data, grub_uint64_t id)
   if (data->n_devices_attached > data->n_devices_allocated)
     {
       void *tmp;
-      data->n_devices_allocated = 2 * data->n_devices_attached + 1;
-      data->devices_attached
-	= grub_realloc (tmp = data->devices_attached,
-			data->n_devices_allocated
-			* sizeof (data->devices_attached[0]));
+      grub_size_t sz;
+
+      if (grub_mul (data->n_devices_attached, 2, &data->n_devices_allocated) ||
+	  grub_add (data->n_devices_allocated, 1, &data->n_devices_allocated) ||
+	  grub_mul (data->n_devices_allocated, sizeof (data->devices_attached[0]), &sz))
+	goto fail;
+
+      data->devices_attached = grub_realloc (tmp = data->devices_attached, sz);
       if (!data->devices_attached)
 	{
+	  data->devices_attached = tmp;
+
+ fail:
 	  if (ctx.dev_found)
 	    grub_device_close (ctx.dev_found);
-	  data->devices_attached = tmp;
 	  return NULL;
 	}
     }
diff --git a/grub-core/fs/ext2.c b/grub-core/fs/ext2.c
index 9b389802a..ac33bcd68 100644
--- a/grub-core/fs/ext2.c
+++ b/grub-core/fs/ext2.c
@@ -46,6 +46,7 @@
 #include <grub/dl.h>
 #include <grub/types.h>
 #include <grub/fshelp.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -703,6 +704,7 @@ grub_ext2_read_symlink (grub_fshelp_node_t node)
 {
   char *symlink;
   struct grub_fshelp_node *diro = node;
+  grub_size_t sz;
 
   if (! diro->inode_read)
     {
@@ -717,7 +719,13 @@ grub_ext2_read_symlink (grub_fshelp_node_t node)
        }
     }
 
-  symlink = grub_malloc (grub_le_to_cpu32 (diro->inode.size) + 1);
+  if (grub_add (grub_le_to_cpu32 (diro->inode.size), 1, &sz))
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
+      return NULL;
+    }
+
+  symlink = grub_malloc (sz);
   if (! symlink)
     return 0;
 
diff --git a/grub-core/fs/iso9660.c b/grub-core/fs/iso9660.c
index 4f1b52a55..7ba5b300b 100644
--- a/grub-core/fs/iso9660.c
+++ b/grub-core/fs/iso9660.c
@@ -28,6 +28,7 @@
 #include <grub/fshelp.h>
 #include <grub/charset.h>
 #include <grub/datetime.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -531,8 +532,13 @@ add_part (struct iterate_dir_ctx *ctx,
 	  int len2)
 {
   int size = ctx->symlink ? grub_strlen (ctx->symlink) : 0;
+  grub_size_t sz;
 
-  ctx->symlink = grub_realloc (ctx->symlink, size + len2 + 1);
+  if (grub_add (size, len2, &sz) ||
+      grub_add (sz, 1, &sz))
+    return;
+
+  ctx->symlink = grub_realloc (ctx->symlink, sz);
   if (! ctx->symlink)
     return;
 
@@ -560,17 +566,24 @@ susp_iterate_dir (struct grub_iso9660_susp_entry *entry,
 	{
 	  grub_size_t off = 0, csize = 1;
 	  char *old;
+	  grub_size_t sz;
+
 	  csize = entry->len - 5;
 	  old = ctx->filename;
 	  if (ctx->filename_alloc)
 	    {
 	      off = grub_strlen (ctx->filename);
-	      ctx->filename = grub_realloc (ctx->filename, csize + off + 1);
+	      if (grub_add (csize, off, &sz) ||
+		  grub_add (sz, 1, &sz))
+		return GRUB_ERR_OUT_OF_RANGE;
+	      ctx->filename = grub_realloc (ctx->filename, sz);
 	    }
 	  else
 	    {
 	      off = 0;
-	      ctx->filename = grub_zalloc (csize + 1);
+	      if (grub_add (csize, 1, &sz))
+		return GRUB_ERR_OUT_OF_RANGE;
+	      ctx->filename = grub_zalloc (sz);
 	    }
 	  if (!ctx->filename)
 	    {
@@ -776,14 +789,18 @@ grub_iso9660_iterate_dir (grub_fshelp_node_t dir,
 	    if (node->have_dirents >= node->alloc_dirents)
 	      {
 		struct grub_fshelp_node *new_node;
-		node->alloc_dirents *= 2;
-		new_node = grub_realloc (node, 
-					 sizeof (struct grub_fshelp_node)
-					 + ((node->alloc_dirents
-					     - ARRAY_SIZE (node->dirents))
-					    * sizeof (node->dirents[0])));
+		grub_size_t sz;
+
+		if (grub_mul (node->alloc_dirents, 2, &node->alloc_dirents) ||
+		    grub_sub (node->alloc_dirents, ARRAY_SIZE (node->dirents), &sz) ||
+		    grub_mul (sz, sizeof (node->dirents[0]), &sz) ||
+		    grub_add (sz, sizeof (struct grub_fshelp_node), &sz))
+		  goto fail_0;
+
+		new_node = grub_realloc (node, sz);
 		if (!new_node)
 		  {
+ fail_0:
 		    if (ctx.filename_alloc)
 		      grub_free (ctx.filename);
 		    grub_free (node);
@@ -799,14 +816,18 @@ grub_iso9660_iterate_dir (grub_fshelp_node_t dir,
 		* sizeof (node->dirents[0]) < grub_strlen (ctx.symlink) + 1)
 	      {
 		struct grub_fshelp_node *new_node;
-		new_node = grub_realloc (node,
-					 sizeof (struct grub_fshelp_node)
-					 + ((node->alloc_dirents
-					     - ARRAY_SIZE (node->dirents))
-					    * sizeof (node->dirents[0]))
-					 + grub_strlen (ctx.symlink) + 1);
+		grub_size_t sz;
+
+		if (grub_sub (node->alloc_dirents, ARRAY_SIZE (node->dirents), &sz) ||
+		    grub_mul (sz, sizeof (node->dirents[0]), &sz) ||
+		    grub_add (sz, sizeof (struct grub_fshelp_node) + 1, &sz) ||
+		    grub_add (sz, grub_strlen (ctx.symlink), &sz))
+		  goto fail_1;
+
+		new_node = grub_realloc (node, sz);
 		if (!new_node)
 		  {
+ fail_1:
 		    if (ctx.filename_alloc)
 		      grub_free (ctx.filename);
 		    grub_free (node);
diff --git a/grub-core/fs/sfs.c b/grub-core/fs/sfs.c
index 90f7fb379..de2b107a4 100644
--- a/grub-core/fs/sfs.c
+++ b/grub-core/fs/sfs.c
@@ -26,6 +26,7 @@
 #include <grub/types.h>
 #include <grub/fshelp.h>
 #include <grub/charset.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -307,10 +308,15 @@ grub_sfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
       if (node->cache && node->cache_size >= node->cache_allocated)
 	{
 	  struct cache_entry *e = node->cache;
-	  e = grub_realloc (node->cache,node->cache_allocated * 2
-			    * sizeof (e[0]));
+	  grub_size_t sz;
+
+	  if (grub_mul (node->cache_allocated, 2 * sizeof (e[0]), &sz))
+	    goto fail;
+
+	  e = grub_realloc (node->cache, sz);
 	  if (!e)
 	    {
+ fail:
 	      grub_errno = 0;
 	      grub_free (node->cache);
 	      node->cache = 0;
@@ -477,10 +483,16 @@ grub_sfs_create_node (struct grub_fshelp_node **node,
   grub_size_t len = grub_strlen (name);
   grub_uint8_t *name_u8;
   int ret;
+  grub_size_t sz;
+
+  if (grub_mul (len, GRUB_MAX_UTF8_PER_LATIN1, &sz) ||
+      grub_add (sz, 1, &sz))
+    return 1;
+
   *node = grub_malloc (sizeof (**node));
   if (!*node)
     return 1;
-  name_u8 = grub_malloc (len * GRUB_MAX_UTF8_PER_LATIN1 + 1);
+  name_u8 = grub_malloc (sz);
   if (!name_u8)
     {
       grub_free (*node);
@@ -724,8 +736,13 @@ grub_sfs_label (grub_device_t device, char **label)
   data = grub_sfs_mount (disk);
   if (data)
     {
-      grub_size_t len = grub_strlen (data->label);
-      *label = grub_malloc (len * GRUB_MAX_UTF8_PER_LATIN1 + 1);
+      grub_size_t sz, len = grub_strlen (data->label);
+
+      if (grub_mul (len, GRUB_MAX_UTF8_PER_LATIN1, &sz) ||
+	  grub_add (sz, 1, &sz))
+	return GRUB_ERR_OUT_OF_RANGE;
+
+      *label = grub_malloc (sz);
       if (*label)
 	*grub_latin1_to_utf8 ((grub_uint8_t *) *label,
 			      (const grub_uint8_t *) data->label,
diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c
index 82704f966..a5f35c10e 100644
--- a/grub-core/fs/squash4.c
+++ b/grub-core/fs/squash4.c
@@ -26,6 +26,7 @@
 #include <grub/types.h>
 #include <grub/fshelp.h>
 #include <grub/deflate.h>
+#include <grub/safemath.h>
 #include <minilzo.h>
 
 #include "xz.h"
@@ -459,7 +460,17 @@ grub_squash_read_symlink (grub_fshelp_node_t node)
 {
   char *ret;
   grub_err_t err;
-  ret = grub_malloc (grub_le_to_cpu32 (node->ino.symlink.namelen) + 1);
+  grub_size_t sz;
+
+  if (grub_add (grub_le_to_cpu32 (node->ino.symlink.namelen), 1, &sz))
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
+      return NULL;
+    }
+
+  ret = grub_malloc (sz);
+  if (!ret)
+    return NULL;
 
   err = read_chunk (node->data, ret,
 		    grub_le_to_cpu32 (node->ino.symlink.namelen),
@@ -506,11 +517,16 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir,
 
   {
     grub_fshelp_node_t node;
-    node = grub_malloc (sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
+    grub_size_t sz;
+
+    if (grub_mul (dir->stsize, sizeof (dir->stack[0]), &sz) ||
+	grub_add (sz, sizeof (*node), &sz))
+      return 0;
+
+    node = grub_malloc (sz);
     if (!node)
       return 0;
-    grub_memcpy (node, dir,
-		 sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
+    grub_memcpy (node, dir, sz);
     if (hook (".", GRUB_FSHELP_DIR, node, hook_data))
       return 1;
 
@@ -518,12 +534,15 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir,
       {
 	grub_err_t err;
 
-	node = grub_malloc (sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
+	if (grub_mul (dir->stsize, sizeof (dir->stack[0]), &sz) ||
+	    grub_add (sz, sizeof (*node), &sz))
+	  return 0;
+
+	node = grub_malloc (sz);
 	if (!node)
 	  return 0;
 
-	grub_memcpy (node, dir,
-		     sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
+	grub_memcpy (node, dir, sz);
 
 	node->stsize--;
 	err = read_chunk (dir->data, &node->ino, sizeof (node->ino),
@@ -557,6 +576,7 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir,
 	  enum grub_fshelp_filetype filetype = GRUB_FSHELP_REG;
 	  struct grub_squash_dirent di;
 	  struct grub_squash_inode ino;
+	  grub_size_t sz;
 
 	  err = read_chunk (dir->data, &di, sizeof (di),
 			    grub_le_to_cpu64 (dir->data->sb.diroffset)
@@ -589,13 +609,16 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir,
 	  if (grub_le_to_cpu16 (di.type) == SQUASH_TYPE_SYMLINK)
 	    filetype = GRUB_FSHELP_SYMLINK;
 
-	  node = grub_malloc (sizeof (*node)
-			      + (dir->stsize + 1) * sizeof (dir->stack[0]));
+	  if (grub_add (dir->stsize, 1, &sz) ||
+	      grub_mul (sz, sizeof (dir->stack[0]), &sz) ||
+	      grub_add (sz, sizeof (*node), &sz))
+	    return 0;
+
+	  node = grub_malloc (sz);
 	  if (! node)
 	    return 0;
 
-	  grub_memcpy (node, dir,
-		       sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
+	  grub_memcpy (node, dir, sz - sizeof(dir->stack[0]));
 
 	  node->ino = ino;
 	  node->stack[node->stsize].ino_chunk = grub_le_to_cpu32 (dh.ino_chunk);
diff --git a/grub-core/fs/udf.c b/grub-core/fs/udf.c
index a83761674..21ac7f446 100644
--- a/grub-core/fs/udf.c
+++ b/grub-core/fs/udf.c
@@ -28,6 +28,7 @@
 #include <grub/charset.h>
 #include <grub/datetime.h>
 #include <grub/udf.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -890,9 +891,19 @@ read_string (const grub_uint8_t *raw, grub_size_t sz, char *outbuf)
 	utf16[i] = (raw[2 * i + 1] << 8) | raw[2*i + 2];
     }
   if (!outbuf)
-    outbuf = grub_malloc (utf16len * GRUB_MAX_UTF8_PER_UTF16 + 1);
+    {
+      grub_size_t size;
+
+      if (grub_mul (utf16len, GRUB_MAX_UTF8_PER_UTF16, &size) ||
+	  grub_add (size, 1, &size))
+	goto fail;
+
+      outbuf = grub_malloc (size);
+    }
   if (outbuf)
     *grub_utf16_to_utf8 ((grub_uint8_t *) outbuf, utf16, utf16len) = '\0';
+
+ fail:
   grub_free (utf16);
   return outbuf;
 }
@@ -1005,7 +1016,7 @@ grub_udf_read_symlink (grub_fshelp_node_t node)
   grub_size_t sz = U64 (node->block.fe.file_size);
   grub_uint8_t *raw;
   const grub_uint8_t *ptr;
-  char *out, *optr;
+  char *out = NULL, *optr;
 
   if (sz < 4)
     return NULL;
@@ -1013,14 +1024,16 @@ grub_udf_read_symlink (grub_fshelp_node_t node)
   if (!raw)
     return NULL;
   if (grub_udf_read_file (node, NULL, NULL, 0, sz, (char *) raw) < 0)
-    {
-      grub_free (raw);
-      return NULL;
-    }
+    goto fail_1;
 
-  out = grub_malloc (sz * 2 + 1);
+  if (grub_mul (sz, 2, &sz) ||
+      grub_add (sz, 1, &sz))
+    goto fail_0;
+
+  out = grub_malloc (sz);
   if (!out)
     {
+ fail_0:
       grub_free (raw);
       return NULL;
     }
@@ -1031,17 +1044,17 @@ grub_udf_read_symlink (grub_fshelp_node_t node)
     {
       grub_size_t s;
       if ((grub_size_t) (ptr - raw + 4) > sz)
-	goto fail;
+	goto fail_1;
       if (!(ptr[2] == 0 && ptr[3] == 0))
-	goto fail;
+	goto fail_1;
       s = 4 + ptr[1];
       if ((grub_size_t) (ptr - raw + s) > sz)
-	goto fail;
+	goto fail_1;
       switch (*ptr)
 	{
 	case 1:
 	  if (ptr[1])
-	    goto fail;
+	    goto fail_1;
 	  /* Fallthrough.  */
 	case 2:
 	  /* in 4 bytes. out: 1 byte.  */
@@ -1066,11 +1079,11 @@ grub_udf_read_symlink (grub_fshelp_node_t node)
 	  if (optr != out)
 	    *optr++ = '/';
 	  if (!read_string (ptr + 4, s - 4, optr))
-	    goto fail;
+	    goto fail_1;
 	  optr += grub_strlen (optr);
 	  break;
 	default:
-	  goto fail;
+	  goto fail_1;
 	}
       ptr += s;
     }
@@ -1078,7 +1091,7 @@ grub_udf_read_symlink (grub_fshelp_node_t node)
   grub_free (raw);
   return out;
 
- fail:
+ fail_1:
   grub_free (raw);
   grub_free (out);
   grub_error (GRUB_ERR_BAD_FS, "invalid symlink");
diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c
index 96ffecbfc..ea6590290 100644
--- a/grub-core/fs/xfs.c
+++ b/grub-core/fs/xfs.c
@@ -25,6 +25,7 @@
 #include <grub/dl.h>
 #include <grub/types.h>
 #include <grub/fshelp.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -899,6 +900,7 @@ static struct grub_xfs_data *
 grub_xfs_mount (grub_disk_t disk)
 {
   struct grub_xfs_data *data = 0;
+  grub_size_t sz;
 
   data = grub_zalloc (sizeof (struct grub_xfs_data));
   if (!data)
@@ -913,10 +915,11 @@ grub_xfs_mount (grub_disk_t disk)
   if (!grub_xfs_sb_valid(data))
     goto fail;
 
-  data = grub_realloc (data,
-		       sizeof (struct grub_xfs_data)
-		       - sizeof (struct grub_xfs_inode)
-		       + grub_xfs_inode_size(data) + 1);
+  if (grub_add (grub_xfs_inode_size (data),
+      sizeof (struct grub_xfs_data) - sizeof (struct grub_xfs_inode) + 1, &sz))
+    goto fail;
+
+  data = grub_realloc (data, sz);
 
   if (! data)
     goto fail;
diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c
index 0c676ac41..41ef0ff57 100644
--- a/grub-core/fs/zfs/zfs.c
+++ b/grub-core/fs/zfs/zfs.c
@@ -55,6 +55,7 @@
 #include <grub/deflate.h>
 #include <grub/crypto.h>
 #include <grub/i18n.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -776,11 +777,14 @@ fill_vdev_info (struct grub_zfs_data *data,
   if (data->n_devices_attached > data->n_devices_allocated)
     {
       void *tmp;
-      data->n_devices_allocated = 2 * data->n_devices_attached + 1;
-      data->devices_attached
-	= grub_realloc (tmp = data->devices_attached,
-			data->n_devices_allocated
-			* sizeof (data->devices_attached[0]));
+      grub_size_t sz;
+
+      if (grub_mul (data->n_devices_attached, 2, &data->n_devices_allocated) ||
+	  grub_add (data->n_devices_allocated, 1, &data->n_devices_allocated) ||
+	  grub_mul (data->n_devices_allocated, sizeof (data->devices_attached[0]), &sz))
+	return GRUB_ERR_OUT_OF_RANGE;
+
+      data->devices_attached = grub_realloc (tmp = data->devices_attached, sz);
       if (!data->devices_attached)
 	{
 	  data->devices_attached = tmp;
@@ -3471,14 +3475,18 @@ grub_zfs_nvlist_lookup_nvlist (const char *nvlist, const char *name)
 {
   char *nvpair;
   char *ret;
-  grub_size_t size;
+  grub_size_t size, sz;
   int found;
 
   found = nvlist_find_value (nvlist, name, DATA_TYPE_NVLIST, &nvpair,
 			     &size, 0);
   if (!found)
     return 0;
-  ret = grub_zalloc (size + 3 * sizeof (grub_uint32_t));
+
+  if (grub_add (size, 3 * sizeof (grub_uint32_t), &sz))
+      return 0;
+
+  ret = grub_zalloc (sz);
   if (!ret)
     return 0;
   grub_memcpy (ret, nvlist, sizeof (grub_uint32_t));
diff --git a/grub-core/fs/zfs/zfscrypt.c b/grub-core/fs/zfs/zfscrypt.c
index 1402e0bc2..de3b015f5 100644
--- a/grub-core/fs/zfs/zfscrypt.c
+++ b/grub-core/fs/zfs/zfscrypt.c
@@ -22,6 +22,7 @@
 #include <grub/misc.h>
 #include <grub/disk.h>
 #include <grub/partition.h>
+#include <grub/safemath.h>
 #include <grub/dl.h>
 #include <grub/types.h>
 #include <grub/zfs/zfs.h>
@@ -82,9 +83,13 @@ grub_zfs_add_key (grub_uint8_t *key_in,
 		  int passphrase)
 {
   struct grub_zfs_wrap_key *key;
+  grub_size_t sz;
+
   if (!passphrase && keylen > 32)
     keylen = 32;
-  key = grub_malloc (sizeof (*key) + keylen);
+  if (grub_add (sizeof (*key), keylen, &sz))
+    return GRUB_ERR_OUT_OF_RANGE;
+  key = grub_malloc (sz);
   if (!key)
     return grub_errno;
   key->is_passphrase = passphrase;
diff --git a/grub-core/lib/arg.c b/grub-core/lib/arg.c
index ccc185017..8439a0062 100644
--- a/grub-core/lib/arg.c
+++ b/grub-core/lib/arg.c
@@ -23,6 +23,7 @@
 #include <grub/term.h>
 #include <grub/extcmd.h>
 #include <grub/i18n.h>
+#include <grub/safemath.h>
 
 /* Built-in parser for default options.  */
 static const struct grub_arg_option help_options[] =
@@ -216,7 +217,13 @@ static inline grub_err_t
 add_arg (char ***argl, int *num, char *s)
 {
   char **p = *argl;
-  *argl = grub_realloc (*argl, (++(*num) + 1) * sizeof (char *));
+  grub_size_t sz;
+
+  if (grub_add (++(*num), 1, &sz) ||
+      grub_mul (sz, sizeof (char *), &sz))
+    return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
+
+  *argl = grub_realloc (*argl, sz);
   if (! *argl)
     {
       grub_free (p);
@@ -431,6 +438,7 @@ grub_arg_list_alloc(grub_extcmd_t extcmd, int argc,
   grub_size_t argcnt;
   struct grub_arg_list *list;
   const struct grub_arg_option *options;
+  grub_size_t sz0, sz1;
 
   options = extcmd->options;
   if (! options)
@@ -443,7 +451,15 @@ grub_arg_list_alloc(grub_extcmd_t extcmd, int argc,
 	argcnt += ((grub_size_t) argc + 1) / 2 + 1; /* max possible for any option */
     }
 
-  list = grub_zalloc (sizeof (*list) * i + sizeof (char*) * argcnt);
+  if (grub_mul (sizeof (*list), i, &sz0) ||
+      grub_mul (sizeof (char *), argcnt, &sz1) ||
+      grub_add (sz0, sz1, &sz0))
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
+      return 0;
+    }
+
+  list = grub_zalloc (sz0);
   if (! list)
     return 0;
 
diff --git a/grub-core/loader/i386/bsd.c b/grub-core/loader/i386/bsd.c
index eb82391db..f5bf7f89e 100644
--- a/grub-core/loader/i386/bsd.c
+++ b/grub-core/loader/i386/bsd.c
@@ -35,6 +35,7 @@
 #include <grub/ns8250.h>
 #include <grub/bsdlabel.h>
 #include <grub/crypto.h>
+#include <grub/safemath.h>
 #include <grub/verify.h>
 #ifdef GRUB_MACHINE_PCBIOS
 #include <grub/machine/int.h>
@@ -1012,11 +1013,16 @@ grub_netbsd_add_modules (void)
   struct grub_netbsd_btinfo_modules *mods;
   unsigned i;
   grub_err_t err;
+  grub_size_t sz;
 
   for (mod = netbsd_mods; mod; mod = mod->next)
     modcnt++;
 
-  mods = grub_malloc (sizeof (*mods) + sizeof (mods->mods[0]) * modcnt);
+  if (grub_mul (modcnt, sizeof (mods->mods[0]), &sz) ||
+      grub_add (sz, sizeof (*mods), &sz))
+    return GRUB_ERR_OUT_OF_RANGE;
+
+  mods = grub_malloc (sz);
   if (!mods)
     return grub_errno;
 
diff --git a/grub-core/net/dns.c b/grub-core/net/dns.c
index e332d5eb4..906ec7d67 100644
--- a/grub-core/net/dns.c
+++ b/grub-core/net/dns.c
@@ -22,6 +22,7 @@
 #include <grub/i18n.h>
 #include <grub/err.h>
 #include <grub/time.h>
+#include <grub/safemath.h>
 
 struct dns_cache_element
 {
@@ -51,9 +52,15 @@ grub_net_add_dns_server (const struct grub_net_network_level_address *s)
     {
       int na = dns_servers_alloc * 2;
       struct grub_net_network_level_address *ns;
+      grub_size_t sz;
+
       if (na < 8)
 	na = 8;
-      ns = grub_realloc (dns_servers, na * sizeof (ns[0]));
+
+      if (grub_mul (na, sizeof (ns[0]), &sz))
+	return GRUB_ERR_OUT_OF_RANGE;
+
+      ns = grub_realloc (dns_servers, sz);
       if (!ns)
 	return grub_errno;
       dns_servers_alloc = na;
diff --git a/grub-core/normal/charset.c b/grub-core/normal/charset.c
index d57fb72fa..4dfcc3107 100644
--- a/grub-core/normal/charset.c
+++ b/grub-core/normal/charset.c
@@ -48,6 +48,7 @@
 #include <grub/unicode.h>
 #include <grub/term.h>
 #include <grub/normal.h>
+#include <grub/safemath.h>
 
 #if HAVE_FONT_SOURCE
 #include "widthspec.h"
@@ -464,6 +465,7 @@ grub_unicode_aglomerate_comb (const grub_uint32_t *in, grub_size_t inlen,
 	{
 	  struct grub_unicode_combining *n;
 	  unsigned j;
+	  grub_size_t sz;
 
 	  if (!haveout)
 	    continue;
@@ -477,10 +479,14 @@ grub_unicode_aglomerate_comb (const grub_uint32_t *in, grub_size_t inlen,
 	    n = out->combining_inline;
 	  else if (out->ncomb > (int) ARRAY_SIZE (out->combining_inline))
 	    {
-	      n = grub_realloc (out->combining_ptr,
-				sizeof (n[0]) * (out->ncomb + 1));
+	      if (grub_add (out->ncomb, 1, &sz) ||
+		  grub_mul (sz, sizeof (n[0]), &sz))
+		goto fail;
+
+	      n = grub_realloc (out->combining_ptr, sz);
 	      if (!n)
 		{
+ fail:
 		  grub_errno = GRUB_ERR_NONE;
 		  continue;
 		}
diff --git a/grub-core/normal/cmdline.c b/grub-core/normal/cmdline.c
index c57242e2e..de03fe63b 100644
--- a/grub-core/normal/cmdline.c
+++ b/grub-core/normal/cmdline.c
@@ -28,6 +28,7 @@
 #include <grub/env.h>
 #include <grub/i18n.h>
 #include <grub/charset.h>
+#include <grub/safemath.h>
 
 static grub_uint32_t *kill_buf;
 
@@ -307,12 +308,21 @@ cl_insert (struct cmdline_term *cl_terms, unsigned nterms,
   if (len + (*llen) >= (*max_len))
     {
       grub_uint32_t *nbuf;
-      (*max_len) *= 2;
-      nbuf = grub_realloc ((*buf), sizeof (grub_uint32_t) * (*max_len));
+      grub_size_t sz;
+
+      if (grub_mul (*max_len, 2, max_len) ||
+	  grub_mul (*max_len, sizeof (grub_uint32_t), &sz))
+	{
+	  grub_errno = GRUB_ERR_OUT_OF_RANGE;
+	  goto fail;
+	}
+
+      nbuf = grub_realloc ((*buf), sz);
       if (nbuf)
 	(*buf) = nbuf;
       else
 	{
+ fail:
 	  grub_print_error ();
 	  grub_errno = GRUB_ERR_NONE;
 	  (*max_len) /= 2;
diff --git a/grub-core/normal/menu_entry.c b/grub-core/normal/menu_entry.c
index 1993995be..50eef918c 100644
--- a/grub-core/normal/menu_entry.c
+++ b/grub-core/normal/menu_entry.c
@@ -27,6 +27,7 @@
 #include <grub/auth.h>
 #include <grub/i18n.h>
 #include <grub/charset.h>
+#include <grub/safemath.h>
 
 enum update_mode
   {
@@ -113,10 +114,18 @@ ensure_space (struct line *linep, int extra)
 {
   if (linep->max_len < linep->len + extra)
     {
-      linep->max_len = 2 * (linep->len + extra);
-      linep->buf = grub_realloc (linep->buf, (linep->max_len + 1) * sizeof (linep->buf[0]));
+      grub_size_t sz0, sz1;
+
+      if (grub_add (linep->len, extra, &sz0) ||
+	  grub_mul (sz0, 2, &sz0) ||
+	  grub_add (sz0, 1, &sz1) ||
+	  grub_mul (sz1, sizeof (linep->buf[0]), &sz1))
+	return 0;
+
+      linep->buf = grub_realloc (linep->buf, sz1);
       if (! linep->buf)
 	return 0;
+      linep->max_len = sz0;
     }
 
   return 1;
diff --git a/grub-core/script/argv.c b/grub-core/script/argv.c
index 217ec5d1e..5751fdd57 100644
--- a/grub-core/script/argv.c
+++ b/grub-core/script/argv.c
@@ -20,6 +20,7 @@
 #include <grub/mm.h>
 #include <grub/misc.h>
 #include <grub/script_sh.h>
+#include <grub/safemath.h>
 
 /* Return nearest power of two that is >= v.  */
 static unsigned
@@ -81,11 +82,16 @@ int
 grub_script_argv_next (struct grub_script_argv *argv)
 {
   char **p = argv->args;
+  grub_size_t sz;
 
   if (argv->args && argv->argc && argv->args[argv->argc - 1] == 0)
     return 0;
 
-  p = grub_realloc (p, round_up_exp ((argv->argc + 2) * sizeof (char *)));
+  if (grub_add (argv->argc, 2, &sz) ||
+      grub_mul (sz, sizeof (char *), &sz))
+    return 1;
+
+  p = grub_realloc (p, round_up_exp (sz));
   if (! p)
     return 1;
 
@@ -105,13 +111,19 @@ grub_script_argv_append (struct grub_script_argv *argv, const char *s,
 {
   grub_size_t a;
   char *p = argv->args[argv->argc - 1];
+  grub_size_t sz;
 
   if (! s)
     return 0;
 
   a = p ? grub_strlen (p) : 0;
 
-  p = grub_realloc (p, round_up_exp ((a + slen + 1) * sizeof (char)));
+  if (grub_add (a, slen, &sz) ||
+      grub_add (sz, 1, &sz) ||
+      grub_mul (sz, sizeof (char), &sz))
+    return 1;
+
+  p = grub_realloc (p, round_up_exp (sz));
   if (! p)
     return 1;
 
diff --git a/grub-core/script/lexer.c b/grub-core/script/lexer.c
index c6bd3172f..5fb0cbd0b 100644
--- a/grub-core/script/lexer.c
+++ b/grub-core/script/lexer.c
@@ -24,6 +24,7 @@
 #include <grub/mm.h>
 #include <grub/script_sh.h>
 #include <grub/i18n.h>
+#include <grub/safemath.h>
 
 #define yytext_ptr char *
 #include "grub_script.tab.h"
@@ -110,10 +111,14 @@ grub_script_lexer_record (struct grub_parser_param *parser, char *str)
       old = lexer->recording;
       if (lexer->recordlen < len)
 	lexer->recordlen = len;
-      lexer->recordlen *= 2;
+
+      if (grub_mul (lexer->recordlen, 2, &lexer->recordlen))
+	goto fail;
+
       lexer->recording = grub_realloc (lexer->recording, lexer->recordlen);
       if (!lexer->recording)
 	{
+ fail:
 	  grub_free (old);
 	  lexer->recordpos = 0;
 	  lexer->recordlen = 0;
@@ -130,7 +135,7 @@ int
 grub_script_lexer_yywrap (struct grub_parser_param *parserstate,
 			  const char *input)
 {
-  grub_size_t len = 0;
+  grub_size_t len = 0, sz;
   char *p = 0;
   char *line = 0;
   YY_BUFFER_STATE buffer;
@@ -168,12 +173,22 @@ grub_script_lexer_yywrap (struct grub_parser_param *parserstate,
     }
   else if (len && line[len - 1] != '\n')
     {
-      p = grub_realloc (line, len + 2);
+      if (grub_add (len, 2, &sz))
+	{
+	  grub_free (line);
+	  grub_script_yyerror (parserstate, N_("overflow is detected"));
+	  return 1;
+	}
+
+      p = grub_realloc (line, sz);
       if (p)
 	{
 	  p[len++] = '\n';
 	  p[len] = '\0';
 	}
+      else
+	grub_free (line);
+
       line = p;
     }
 
diff --git a/grub-core/video/bitmap.c b/grub-core/video/bitmap.c
index b2e031566..6256e209a 100644
--- a/grub-core/video/bitmap.c
+++ b/grub-core/video/bitmap.c
@@ -23,6 +23,7 @@
 #include <grub/mm.h>
 #include <grub/misc.h>
 #include <grub/i18n.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -58,7 +59,7 @@ grub_video_bitmap_create (struct grub_video_bitmap **bitmap,
                           enum grub_video_blit_format blit_format)
 {
   struct grub_video_mode_info *mode_info;
-  unsigned int size;
+  grub_size_t size;
 
   if (!bitmap)
     return grub_error (GRUB_ERR_BUG, "invalid argument");
@@ -137,19 +138,25 @@ grub_video_bitmap_create (struct grub_video_bitmap **bitmap,
 
   mode_info->pitch = width * mode_info->bytes_per_pixel;
 
-  /* Calculate size needed for the data.  */
-  size = (width * mode_info->bytes_per_pixel) * height;
+  /* Calculate size needed for the data. */
+  if (grub_mul (width, mode_info->bytes_per_pixel, &size) ||
+      grub_mul (size, height, &size))
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
+      goto fail;
+    }
 
   (*bitmap)->data = grub_zalloc (size);
   if (! (*bitmap)->data)
-    {
-      grub_free (*bitmap);
-      *bitmap = 0;
-
-      return grub_errno;
-    }
+    goto fail;
 
   return GRUB_ERR_NONE;
+
+ fail:
+  grub_free (*bitmap);
+  *bitmap = NULL;
+
+  return grub_errno;
 }
 
 /* Frees all resources allocated by bitmap.  */
diff --git a/grub-core/video/readers/png.c b/grub-core/video/readers/png.c
index 61bd64537..0157ff742 100644
--- a/grub-core/video/readers/png.c
+++ b/grub-core/video/readers/png.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+");
 
@@ -301,9 +302,17 @@ grub_png_decode_image_header (struct grub_png_data *data)
       data->bpp <<= 1;
 
   data->color_bits = color_bits;
-  data->row_bytes = data->image_width * data->bpp;
+
+  if (grub_mul (data->image_width, data->bpp, &data->row_bytes))
+    return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
+
   if (data->color_bits <= 4)
-    data->row_bytes = (data->image_width * data->color_bits + 7) / 8;
+    {
+      if (grub_mul (data->image_width, data->color_bits + 7, &data->row_bytes))
+	return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
+
+      data->row_bytes >>= 3;
+    }
 
 #ifndef GRUB_CPU_WORDS_BIGENDIAN
   if (data->is_16bit || data->is_gray || data->is_palette)
-- 
2.11.0



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

* [SECURITY PATCH 06/28] iso9660: Don't leak memory on realloc() failures
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (4 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 05/28] malloc: Use overflow checking primitives where we do complex allocations Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 07/28] font: Do not load more than one NAME section Daniel Kiper
                   ` (22 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Peter Jones <pjones@redhat.com>

Signed-off-by: Peter Jones <pjones@redhat.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/fs/iso9660.c | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/grub-core/fs/iso9660.c b/grub-core/fs/iso9660.c
index 7ba5b300b..5ec4433b8 100644
--- a/grub-core/fs/iso9660.c
+++ b/grub-core/fs/iso9660.c
@@ -533,14 +533,20 @@ add_part (struct iterate_dir_ctx *ctx,
 {
   int size = ctx->symlink ? grub_strlen (ctx->symlink) : 0;
   grub_size_t sz;
+  char *new;
 
   if (grub_add (size, len2, &sz) ||
       grub_add (sz, 1, &sz))
     return;
 
-  ctx->symlink = grub_realloc (ctx->symlink, sz);
-  if (! ctx->symlink)
-    return;
+  new = grub_realloc (ctx->symlink, sz);
+  if (!new)
+    {
+      grub_free (ctx->symlink);
+      ctx->symlink = NULL;
+      return;
+    }
+  ctx->symlink = new;
 
   grub_memcpy (ctx->symlink + size, part, len2);
   ctx->symlink[size + len2] = 0;  
@@ -634,7 +640,12 @@ susp_iterate_dir (struct grub_iso9660_susp_entry *entry,
 		   is the length.  Both are part of the `Component
 		   Record'.  */
 		if (ctx->symlink && !ctx->was_continue)
-		  add_part (ctx, "/", 1);
+		  {
+		    add_part (ctx, "/", 1);
+		    if (grub_errno)
+		      return grub_errno;
+		  }
+
 		add_part (ctx, (char *) &entry->data[pos + 2],
 			  entry->data[pos + 1]);
 		ctx->was_continue = (entry->data[pos] & 1);
@@ -653,6 +664,11 @@ susp_iterate_dir (struct grub_iso9660_susp_entry *entry,
 	      add_part (ctx, "/", 1);
 	      break;
 	    }
+
+	  /* Check if grub_realloc() failed in add_part(). */
+	  if (grub_errno)
+	    return grub_errno;
+
 	  /* In pos + 1 the length of the `Component Record' is
 	     stored.  */
 	  pos += entry->data[pos + 1] + 2;
-- 
2.11.0



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

* [SECURITY PATCH 07/28] font: Do not load more than one NAME section
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (5 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 06/28] iso9660: Don't leak memory on realloc() failures Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 08/28] gfxmenu: Fix double free in load_image() Daniel Kiper
                   ` (21 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

The GRUB font file can have one NAME section only. Though if somebody
crafts a broken font file with many NAME sections and loads it then the
GRUB leaks memory. So, prevent against that by loading first NAME
section and failing in controlled way on following one.

Reported-by: Chris Coulson <chris.coulson@canonical.com>
Signed-off-by: Daniel Kiper <daniel.kiper@oracle.com>
Reviewed-by: Jan Setje-Eilers <jan.setjeeilers@oracle.com>
---
 grub-core/font/font.c | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/grub-core/font/font.c b/grub-core/font/font.c
index 5edb477ac..d09bb38d8 100644
--- a/grub-core/font/font.c
+++ b/grub-core/font/font.c
@@ -532,6 +532,12 @@ grub_font_load (const char *filename)
       if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_FONT_NAME,
 		       sizeof (FONT_FORMAT_SECTION_NAMES_FONT_NAME) - 1) == 0)
 	{
+	  if (font->name != NULL)
+	    {
+	      grub_error (GRUB_ERR_BAD_FONT, "invalid font file: too many NAME sections");
+	      goto fail;
+	    }
+
 	  font->name = read_section_as_string (&section);
 	  if (!font->name)
 	    goto fail;
-- 
2.11.0



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

* [SECURITY PATCH 08/28] gfxmenu: Fix double free in load_image()
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (6 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 07/28] font: Do not load more than one NAME section Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 09/28] xnu: Fix double free in grub_xnu_devprop_add_property() Daniel Kiper
                   ` (20 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Alexey Makhalov <amakhalov@vmware.com>

self->bitmap should be zeroed after free. Otherwise, there is a chance
to double free (USE_AFTER_FREE) it later in rescale_image().

Fixes: CID 292472

Signed-off-by: Alexey Makhalov <amakhalov@vmware.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/gfxmenu/gui_image.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/grub-core/gfxmenu/gui_image.c b/grub-core/gfxmenu/gui_image.c
index 29784ed2d..6b2e976f1 100644
--- a/grub-core/gfxmenu/gui_image.c
+++ b/grub-core/gfxmenu/gui_image.c
@@ -195,7 +195,10 @@ load_image (grub_gui_image_t self, const char *path)
     return grub_errno;
 
   if (self->bitmap && (self->bitmap != self->raw_bitmap))
-    grub_video_bitmap_destroy (self->bitmap);
+    {
+      grub_video_bitmap_destroy (self->bitmap);
+      self->bitmap = 0;
+    }
   if (self->raw_bitmap)
     grub_video_bitmap_destroy (self->raw_bitmap);
 
-- 
2.11.0



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

* [SECURITY PATCH 09/28] xnu: Fix double free in grub_xnu_devprop_add_property()
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (7 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 08/28] gfxmenu: Fix double free in load_image() Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 10/28] json: Avoid a double-free when parsing fails Daniel Kiper
                   ` (19 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Alexey Makhalov <amakhalov@vmware.com>

grub_xnu_devprop_add_property() should not free utf8 and utf16 as it get
allocated and freed in the caller.

Minor improvement: do prop fields initialization after memory allocations.

Fixes: CID 292442, CID 292457, CID 292460, CID 292466

Signed-off-by: Alexey Makhalov <amakhalov@vmware.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/loader/i386/xnu.c | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

diff --git a/grub-core/loader/i386/xnu.c b/grub-core/loader/i386/xnu.c
index b7d176b5d..e9e119259 100644
--- a/grub-core/loader/i386/xnu.c
+++ b/grub-core/loader/i386/xnu.c
@@ -262,20 +262,19 @@ grub_xnu_devprop_add_property (struct grub_xnu_devprop_device_descriptor *dev,
   if (!prop)
     return grub_errno;
 
+  prop->data = grub_malloc (datalen);
+  if (!prop->data)
+    {
+      grub_free (prop);
+      return grub_errno;
+    }
+  grub_memcpy (prop->data, data, datalen);
+
   prop->name = utf8;
   prop->name16 = utf16;
   prop->name16len = utf16len;
-
   prop->length = datalen;
-  prop->data = grub_malloc (prop->length);
-  if (!prop->data)
-    {
-      grub_free (prop->name);
-      grub_free (prop->name16);
-      grub_free (prop);
-      return grub_errno;
-    }
-  grub_memcpy (prop->data, data, prop->length);
+
   grub_list_push (GRUB_AS_LIST_P (&dev->properties),
 		  GRUB_AS_LIST (prop));
   return GRUB_ERR_NONE;
-- 
2.11.0



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

* [SECURITY PATCH 10/28] json: Avoid a double-free when parsing fails.
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (8 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 09/28] xnu: Fix double free in grub_xnu_devprop_add_property() Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 11/28] lzma: Make sure we don't dereference past array Daniel Kiper
                   ` (18 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

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

When grub_json_parse() succeeds, it returns the root object which
contains a pointer to the provided JSON string. Callers are
responsible for ensuring that this string outlives the root
object and for freeing its memory when it's no longer needed.

If grub_json_parse() fails to parse the provided JSON string,
it frees the string before returning an error. This results
in a double free in luks2_recover_key(), which also frees the
same string after grub_json_parse() returns an error.

This changes grub_json_parse() to never free the JSON string
passed to it, and updates the documentation for it to make it
clear that callers are responsible for ensuring that the string
outlives the root JSON object.

Fixes: CID 292465

Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/lib/json/json.c | 9 +++------
 grub-core/lib/json/json.h | 5 ++++-
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/grub-core/lib/json/json.c b/grub-core/lib/json/json.c
index 913b8f404..1c20c75ea 100644
--- a/grub-core/lib/json/json.c
+++ b/grub-core/lib/json/json.c
@@ -71,12 +71,9 @@ grub_json_parse (grub_json_t **out, char *string, grub_size_t string_len)
   *out = json;
 
  err:
-  if (ret && json)
-    {
-      grub_free (json->string);
-      grub_free (json->tokens);
-      grub_free (json);
-    }
+  if (ret)
+    grub_json_free (json);
+
   return ret;
 }
 
diff --git a/grub-core/lib/json/json.h b/grub-core/lib/json/json.h
index d9f99454d..01614f6df 100644
--- a/grub-core/lib/json/json.h
+++ b/grub-core/lib/json/json.h
@@ -50,7 +50,10 @@ typedef struct grub_json grub_json_t;
  * Parse a JSON-encoded string. Note that the string passed to
  * this function will get modified on subsequent calls to
  * grub_json_get*(). Returns the root object of the parsed JSON
- * object, which needs to be free'd via grub_json_free().
+ * object, which needs to be free'd via grub_json_free(). Callers
+ * must ensure that the string outlives the returned root object,
+ * and that child objects must not be used after the root object
+ * has been free'd.
  */
 extern grub_err_t EXPORT_FUNC(grub_json_parse) (grub_json_t **out,
 					        char *string,
-- 
2.11.0



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

* [SECURITY PATCH 11/28] lzma: Make sure we don't dereference past array
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (9 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 10/28] json: Avoid a double-free when parsing fails Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 12/28] term: Fix overflow on user inputs Daniel Kiper
                   ` (17 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>

The two dimensional array p->posSlotEncoder[4][64] is being dereferenced
using the GetLenToPosState() macro which checks if len is less than 5,
and if so subtracts 2 from it. If len = 0, that is 0 - 2 = 4294967294.
Obviously we don't want to dereference that far out so we check if the
position found is greater or equal kNumLenToPosStates (4) and bail out.

N.B.: Upstream LZMA 18.05 and later has this function completely rewritten
without any history.

Fixes: CID 51526

Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/lib/LzmaEnc.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/grub-core/lib/LzmaEnc.c b/grub-core/lib/LzmaEnc.c
index f2ec04a8c..753e56a95 100644
--- a/grub-core/lib/LzmaEnc.c
+++ b/grub-core/lib/LzmaEnc.c
@@ -1877,13 +1877,19 @@ static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize
       }
       else
       {
-        UInt32 posSlot;
+        UInt32 posSlot, lenToPosState;
         RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0);
         p->state = kMatchNextStates[p->state];
         LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices);
         pos -= LZMA_NUM_REPS;
         GetPosSlot(pos, posSlot);
-        RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot);
+        lenToPosState = GetLenToPosState(len);
+        if (lenToPosState >= kNumLenToPosStates)
+        {
+          p->result = SZ_ERROR_DATA;
+          return CheckErrors(p);
+        }
+        RcTree_Encode(&p->rc, p->posSlotEncoder[lenToPosState], kNumPosSlotBits, posSlot);
 
         if (posSlot >= kStartPosModelIndex)
         {
-- 
2.11.0



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

* [SECURITY PATCH 12/28] term: Fix overflow on user inputs
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (10 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 11/28] lzma: Make sure we don't dereference past array Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 13/28] udf: Fix memory leak Daniel Kiper
                   ` (16 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>

This requires a very weird input from the serial interface but can cause
an overflow in input_buf (keys) overwriting the next variable (npending)
with the user choice:

(pahole output)

struct grub_terminfo_input_state {
        int                        input_buf[6];         /*     0    24 */
        int                        npending;             /*    24     4 */ <- CORRUPT
        ...snip...

The magic string requires causing this is "ESC,O,],0,1,2,q" and we overflow
npending with "q" (aka increase npending to 161). The simplest fix is to
just to disallow overwrites input_buf, which exactly what this patch does.

Fixes: CID 292449

Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/term/terminfo.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/grub-core/term/terminfo.c b/grub-core/term/terminfo.c
index 0e9de7f8f..cd7200803 100644
--- a/grub-core/term/terminfo.c
+++ b/grub-core/term/terminfo.c
@@ -398,7 +398,7 @@ grub_terminfo_getwh (struct grub_term_output *term)
 }
 
 static void
-grub_terminfo_readkey (struct grub_term_input *term, int *keys, int *len,
+grub_terminfo_readkey (struct grub_term_input *term, int *keys, int *len, int max_len,
 		       int (*readkey) (struct grub_term_input *term))
 {
   int c;
@@ -414,6 +414,9 @@ grub_terminfo_readkey (struct grub_term_input *term, int *keys, int *len,
     if (c == -1)						\
       return;							\
 								\
+    if (*len >= max_len)                                       \
+      return;                                                   \
+                                                                \
     keys[*len] = c;						\
     (*len)++;							\
   }
@@ -602,8 +605,8 @@ grub_terminfo_getkey (struct grub_term_input *termi)
       return ret;
     }
 
-  grub_terminfo_readkey (termi, data->input_buf,
-			 &data->npending, data->readkey);
+  grub_terminfo_readkey (termi, data->input_buf, &data->npending,
+			 GRUB_TERMINFO_READKEY_MAX_LEN, data->readkey);
 
 #if defined(__powerpc__) && defined(GRUB_MACHINE_IEEE1275)
   if (data->npending == 1 && data->input_buf[0] == GRUB_TERM_ESC
-- 
2.11.0



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

* [SECURITY PATCH 13/28] udf: Fix memory leak
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (11 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 12/28] term: Fix overflow on user inputs Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 14/28] multiboot2: Fix memory leak if grub_create_loader_cmdline() fails Daniel Kiper
                   ` (15 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>

Fixes: CID 73796

Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
Reviewed-by: Jan Setje-Eilers <jan.setjeeilers@oracle.com>
---
 grub-core/fs/udf.c | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/grub-core/fs/udf.c b/grub-core/fs/udf.c
index 21ac7f446..2ac5c1d00 100644
--- a/grub-core/fs/udf.c
+++ b/grub-core/fs/udf.c
@@ -965,8 +965,10 @@ grub_udf_iterate_dir (grub_fshelp_node_t dir,
 	    return 0;
 
           if (grub_udf_read_icb (dir->data, &dirent.icb, child))
-	    return 0;
-
+	    {
+	      grub_free (child);
+	      return 0;
+	    }
           if (dirent.characteristics & GRUB_UDF_FID_CHAR_PARENT)
 	    {
 	      /* This is the parent directory.  */
@@ -988,11 +990,18 @@ grub_udf_iterate_dir (grub_fshelp_node_t dir,
 				       dirent.file_ident_length,
 				       (char *) raw))
 		  != dirent.file_ident_length)
-		return 0;
+		{
+		  grub_free (child);
+		  return 0;
+		}
 
 	      filename = read_string (raw, dirent.file_ident_length, 0);
 	      if (!filename)
-		grub_print_error ();
+		{
+		  /* As the hook won't get called. */
+		  grub_free (child);
+		  grub_print_error ();
+		}
 
 	      if (filename && hook (filename, type, child, hook_data))
 		{
-- 
2.11.0



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

* [SECURITY PATCH 14/28] multiboot2: Fix memory leak if grub_create_loader_cmdline() fails
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (12 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 13/28] udf: Fix memory leak Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 15/28] tftp: Do not use priority queue Daniel Kiper
                   ` (14 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>

Fixes: CID 292468

Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/loader/multiboot_mbi2.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c
index 18e766c7b..e88c9f488 100644
--- a/grub-core/loader/multiboot_mbi2.c
+++ b/grub-core/loader/multiboot_mbi2.c
@@ -1076,7 +1076,11 @@ grub_multiboot2_add_module (grub_addr_t start, grub_size_t size,
   err = grub_create_loader_cmdline (argc, argv, newmod->cmdline,
 				    newmod->cmdline_size, GRUB_VERIFY_MODULE_CMDLINE);
   if (err)
-    return err;
+    {
+      grub_free (newmod->cmdline);
+      grub_free (newmod);
+      return err;
+    }
 
   if (modules_last)
     modules_last->next = newmod;
-- 
2.11.0



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

* [SECURITY PATCH 15/28] tftp: Do not use priority queue
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (13 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 14/28] multiboot2: Fix memory leak if grub_create_loader_cmdline() fails Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 16/28] relocator: Protect grub_relocator_alloc_chunk_addr() input args against integer underflow/overflow Daniel Kiper
                   ` (13 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Alexey Makhalov <amakhalov@vmware.com>

There is not need to reassemble the order of blocks. Per RFC 1350,
server must wait for the ACK, before sending next block. Data packets
can be served immediately without putting them to priority queue.

Logic to handle incoming packet is this:
  - if packet block id equal to expected block id, then
    process the packet,
  - if packet block id is less than expected - this is retransmit
    of old packet, then ACK it and drop the packet,
  - if packet block id is more than expected - that shouldn't
    happen, just drop the packet.

It makes the tftp receive path code simpler, smaller and faster.
As a benefit, this change fixes CID# 73624 and CID# 96690, caused
by following while loop:

  while (cmp_block (grub_be_to_cpu16 (tftph->u.data.block), data->block + 1) == 0)

where tftph pointer is not moving from one iteration to another, causing
to serve same packet again. Luckily, double serving didn't happen due to
data->block++ during the first iteration.

Fixes: CID 73624, CID 96690

Signed-off-by: Alexey Makhalov <amakhalov@vmware.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/net/tftp.c | 162 ++++++++++++++++-----------------------------------
 1 file changed, 50 insertions(+), 112 deletions(-)

diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c
index b26d2ed7a..644135caf 100644
--- a/grub-core/net/tftp.c
+++ b/grub-core/net/tftp.c
@@ -25,7 +25,6 @@
 #include <grub/mm.h>
 #include <grub/dl.h>
 #include <grub/file.h>
-#include <grub/priority_queue.h>
 #include <grub/i18n.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
@@ -106,31 +105,8 @@ typedef struct tftp_data
   int have_oack;
   struct grub_error_saved save_err;
   grub_net_udp_socket_t sock;
-  grub_priority_queue_t pq;
 } *tftp_data_t;
 
-static int
-cmp_block (grub_uint16_t a, grub_uint16_t b)
-{
-  grub_int16_t i = (grub_int16_t) (a - b);
-  if (i > 0)
-    return +1;
-  if (i < 0)
-    return -1;
-  return 0;
-}
-
-static int
-cmp (const void *a__, const void *b__)
-{
-  struct grub_net_buff *a_ = *(struct grub_net_buff **) a__;
-  struct grub_net_buff *b_ = *(struct grub_net_buff **) b__;
-  struct tftphdr *a = (struct tftphdr *) a_->data;
-  struct tftphdr *b = (struct tftphdr *) b_->data;
-  /* We want the first elements to be on top.  */
-  return -cmp_block (grub_be_to_cpu16 (a->u.data.block), grub_be_to_cpu16 (b->u.data.block));
-}
-
 static grub_err_t
 ack (tftp_data_t data, grub_uint64_t block)
 {
@@ -207,73 +183,60 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)),
 	  return GRUB_ERR_NONE;
 	}
 
-      err = grub_priority_queue_push (data->pq, &nb);
-      if (err)
-	return err;
+      /* Ack old/retransmitted block. */
+      if (grub_be_to_cpu16 (tftph->u.data.block) < data->block + 1)
+	ack (data, grub_be_to_cpu16 (tftph->u.data.block));
+      /* Ignore unexpected block. */
+      else if (grub_be_to_cpu16 (tftph->u.data.block) > data->block + 1)
+	grub_dprintf ("tftp", "TFTP unexpected block # %d\n", tftph->u.data.block);
+      else
+	{
+	  unsigned size;
 
-      {
-	struct grub_net_buff **nb_top_p, *nb_top;
-	while (1)
-	  {
-	    nb_top_p = grub_priority_queue_top (data->pq);
-	    if (!nb_top_p)
-	      return GRUB_ERR_NONE;
-	    nb_top = *nb_top_p;
-	    tftph = (struct tftphdr *) nb_top->data;
-	    if (cmp_block (grub_be_to_cpu16 (tftph->u.data.block), data->block + 1) >= 0)
-	      break;
-	    ack (data, grub_be_to_cpu16 (tftph->u.data.block));
-	    grub_netbuff_free (nb_top);
-	    grub_priority_queue_pop (data->pq);
-	  }
-	while (cmp_block (grub_be_to_cpu16 (tftph->u.data.block), data->block + 1) == 0)
-	  {
-	    unsigned size;
-
-	    grub_priority_queue_pop (data->pq);
-
-	    if (file->device->net->packs.count < 50)
+	  if (file->device->net->packs.count < 50)
+	    {
 	      err = ack (data, data->block + 1);
-	    else
-	      {
-		file->device->net->stall = 1;
-		err = 0;
-	      }
-	    if (err)
-	      return err;
+	      if (err)
+		return err;
+	    }
+	  else
+	    file->device->net->stall = 1;
 
-	    err = grub_netbuff_pull (nb_top, sizeof (tftph->opcode) +
-				     sizeof (tftph->u.data.block));
-	    if (err)
-	      return err;
-	    size = nb_top->tail - nb_top->data;
+	  err = grub_netbuff_pull (nb, sizeof (tftph->opcode) +
+				   sizeof (tftph->u.data.block));
+	  if (err)
+	    return err;
+	  size = nb->tail - nb->data;
 
-	    data->block++;
-	    if (size < data->block_size)
-	      {
-		if (data->ack_sent < data->block)
-		  ack (data, data->block);
-		file->device->net->eof = 1;
-		file->device->net->stall = 1;
-		grub_net_udp_close (data->sock);
-		data->sock = NULL;
-	      }
-	    /* Prevent garbage in broken cards. Is it still necessary
-	       given that IP implementation has been fixed?
-	     */
-	    if (size > data->block_size)
-	      {
-		err = grub_netbuff_unput (nb_top, size - data->block_size);
-		if (err)
-		  return err;
-	      }
-	    /* If there is data, puts packet in socket list. */
-	    if ((nb_top->tail - nb_top->data) > 0)
-	      grub_net_put_packet (&file->device->net->packs, nb_top);
-	    else
-	      grub_netbuff_free (nb_top);
-	  }
-      }
+	  data->block++;
+	  if (size < data->block_size)
+	    {
+	      if (data->ack_sent < data->block)
+		ack (data, data->block);
+	      file->device->net->eof = 1;
+	      file->device->net->stall = 1;
+	      grub_net_udp_close (data->sock);
+	      data->sock = NULL;
+	    }
+	  /*
+	   * Prevent garbage in broken cards. Is it still necessary
+	   * given that IP implementation has been fixed?
+	   */
+	  if (size > data->block_size)
+	    {
+	      err = grub_netbuff_unput (nb, size - data->block_size);
+	      if (err)
+		return err;
+	    }
+	  /* If there is data, puts packet in socket list. */
+	  if ((nb->tail - nb->data) > 0)
+	    {
+	      grub_net_put_packet (&file->device->net->packs, nb);
+	      /* Do not free nb. */
+	      return GRUB_ERR_NONE;
+	    }
+	}
+      grub_netbuff_free (nb);
       return GRUB_ERR_NONE;
     case TFTP_ERROR:
       data->have_oack = 1;
@@ -287,19 +250,6 @@ tftp_receive (grub_net_udp_socket_t sock __attribute__ ((unused)),
     }
 }
 
-static void
-destroy_pq (tftp_data_t data)
-{
-  struct grub_net_buff **nb_p;
-  while ((nb_p = grub_priority_queue_top (data->pq)))
-    {
-      grub_netbuff_free (*nb_p);
-      grub_priority_queue_pop (data->pq);
-    }
-
-  grub_priority_queue_destroy (data->pq);
-}
-
 /*
  * Create a normalized copy of the filename. Compress any string of consecutive
  * forward slashes to a single forward slash.
@@ -397,17 +347,9 @@ tftp_open (struct grub_file *file, const char *filename)
   file->not_easily_seekable = 1;
   file->data = data;
 
-  data->pq = grub_priority_queue_new (sizeof (struct grub_net_buff *), cmp);
-  if (!data->pq)
-    {
-      grub_free (data);
-      return grub_errno;
-    }
-
   err = grub_net_resolve_address (file->device->net->server, &addr);
   if (err)
     {
-      destroy_pq (data);
       grub_free (data);
       return err;
     }
@@ -417,7 +359,6 @@ tftp_open (struct grub_file *file, const char *filename)
 				  file);
   if (!data->sock)
     {
-      destroy_pq (data);
       grub_free (data);
       return grub_errno;
     }
@@ -431,7 +372,6 @@ tftp_open (struct grub_file *file, const char *filename)
       if (err)
 	{
 	  grub_net_udp_close (data->sock);
-	  destroy_pq (data);
 	  grub_free (data);
 	  return err;
 	}
@@ -448,7 +388,6 @@ tftp_open (struct grub_file *file, const char *filename)
   if (grub_errno)
     {
       grub_net_udp_close (data->sock);
-      destroy_pq (data);
       grub_free (data);
       return grub_errno;
     }
@@ -491,7 +430,6 @@ tftp_close (struct grub_file *file)
 	grub_print_error ();
       grub_net_udp_close (data->sock);
     }
-  destroy_pq (data);
   grub_free (data);
   return GRUB_ERR_NONE;
 }
-- 
2.11.0



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

* [SECURITY PATCH 16/28] relocator: Protect grub_relocator_alloc_chunk_addr() input args against integer underflow/overflow
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (14 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 15/28] tftp: Do not use priority queue Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 17/28] relocator: Protect grub_relocator_alloc_chunk_align() max_addr against integer underflow Daniel Kiper
                   ` (12 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Alexey Makhalov <amakhalov@vmware.com>

Use arithmetic macros from safemath.h to accomplish it. In this commit,
I didn't want to be too paranoid to check every possible math equation
for overflow/underflow. Only obvious places (with non zero chance of
overflow/underflow) were refactored.

Signed-off-by: Alexey Makhalov <amakhalov@vmware.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/loader/i386/linux.c    |  9 +++++++--
 grub-core/loader/i386/pc/linux.c |  9 +++++++--
 grub-core/loader/i386/xen.c      | 12 ++++++++++--
 grub-core/loader/xnu.c           | 11 +++++++----
 4 files changed, 31 insertions(+), 10 deletions(-)

diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
index ac1fae72e..938ba536e 100644
--- a/grub-core/loader/i386/linux.c
+++ b/grub-core/loader/i386/linux.c
@@ -36,6 +36,7 @@
 #include <grub/lib/cmdline.h>
 #include <grub/linux.h>
 #include <grub/machine/kernel.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -547,9 +548,13 @@ grub_linux_boot (void)
 
   {
     grub_relocator_chunk_t ch;
+    grub_size_t sz;
+
+    if (grub_add (ctx.real_size, efi_mmap_size, &sz))
+      return GRUB_ERR_OUT_OF_RANGE;
+
     err = grub_relocator_alloc_chunk_addr (relocator, &ch,
-					   ctx.real_mode_target,
-					   (ctx.real_size + efi_mmap_size));
+					   ctx.real_mode_target, sz);
     if (err)
      return err;
     real_mode_mem = get_virtual_current_address (ch);
diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c
index f56af7c50..8be763d19 100644
--- a/grub-core/loader/i386/pc/linux.c
+++ b/grub-core/loader/i386/pc/linux.c
@@ -35,6 +35,7 @@
 #include <grub/i386/floppy.h>
 #include <grub/lib/cmdline.h>
 #include <grub/linux.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -218,8 +219,12 @@ grub_cmd_linux (grub_command_t cmd __attribute__ ((unused)),
     setup_sects = GRUB_LINUX_DEFAULT_SETUP_SECTS;
 
   real_size = setup_sects << GRUB_DISK_SECTOR_BITS;
-  grub_linux16_prot_size = grub_file_size (file)
-    - real_size - GRUB_DISK_SECTOR_SIZE;
+  if (grub_sub (grub_file_size (file), real_size, &grub_linux16_prot_size) ||
+      grub_sub (grub_linux16_prot_size, GRUB_DISK_SECTOR_SIZE, &grub_linux16_prot_size))
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
+      goto fail;
+    }
 
   if (! grub_linux_is_bzimage
       && GRUB_LINUX_ZIMAGE_ADDR + grub_linux16_prot_size
diff --git a/grub-core/loader/i386/xen.c b/grub-core/loader/i386/xen.c
index 8f662c8ac..cd24874ca 100644
--- a/grub-core/loader/i386/xen.c
+++ b/grub-core/loader/i386/xen.c
@@ -41,6 +41,7 @@
 #include <grub/linux.h>
 #include <grub/i386/memory.h>
 #include <grub/verify.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -636,6 +637,7 @@ grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)),
   grub_relocator_chunk_t ch;
   grub_addr_t kern_start;
   grub_addr_t kern_end;
+  grub_size_t sz;
 
   if (argc == 0)
     return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
@@ -703,8 +705,14 @@ grub_cmd_xen (grub_command_t cmd __attribute__ ((unused)),
 
   xen_state.max_addr = ALIGN_UP (kern_end, PAGE_SIZE);
 
-  err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch, kern_start,
-					 kern_end - kern_start);
+
+  if (grub_sub (kern_end, kern_start, &sz))
+    {
+      err = GRUB_ERR_OUT_OF_RANGE;
+      goto fail;
+    }
+
+  err = grub_relocator_alloc_chunk_addr (xen_state.relocator, &ch, kern_start, sz);
   if (err)
     goto fail;
   kern_chunk_src = get_virtual_current_address (ch);
diff --git a/grub-core/loader/xnu.c b/grub-core/loader/xnu.c
index 77d7060e1..9ae4ceb35 100644
--- a/grub-core/loader/xnu.c
+++ b/grub-core/loader/xnu.c
@@ -34,6 +34,7 @@
 #include <grub/env.h>
 #include <grub/i18n.h>
 #include <grub/verify.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -59,15 +60,17 @@ grub_xnu_heap_malloc (int size, void **src, grub_addr_t *target)
 {
   grub_err_t err;
   grub_relocator_chunk_t ch;
+  grub_addr_t tgt;
+
+  if (grub_add (grub_xnu_heap_target_start, grub_xnu_heap_size, &tgt))
+    return GRUB_ERR_OUT_OF_RANGE;
   
-  err = grub_relocator_alloc_chunk_addr (grub_xnu_relocator, &ch,
-					 grub_xnu_heap_target_start
-					 + grub_xnu_heap_size, size);
+  err = grub_relocator_alloc_chunk_addr (grub_xnu_relocator, &ch, tgt, size);
   if (err)
     return err;
 
   *src = get_virtual_current_address (ch);
-  *target = grub_xnu_heap_target_start + grub_xnu_heap_size;
+  *target = tgt;
   grub_xnu_heap_size += size;
   grub_dprintf ("xnu", "val=%p\n", *src);
   return GRUB_ERR_NONE;
-- 
2.11.0



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

* [SECURITY PATCH 17/28] relocator: Protect grub_relocator_alloc_chunk_align() max_addr against integer underflow
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (15 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 16/28] relocator: Protect grub_relocator_alloc_chunk_addr() input args against integer underflow/overflow Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 18/28] script: Remove unused fields from grub_script_function struct Daniel Kiper
                   ` (11 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Alexey Makhalov <amakhalov@vmware.com>

This commit introduces integer underflow mitigation in max_addr calculation
in grub_relocator_alloc_chunk_align() invocation.

It consists of 2 fixes:
  1. Introduced grub_relocator_alloc_chunk_align_safe() wrapper function to perform
     sanity check for min/max and size values, and to make safe invocation of
     grub_relocator_alloc_chunk_align() with validated max_addr value. Replace all
     invocations such as grub_relocator_alloc_chunk_align(..., min_addr, max_addr - size, size, ...)
     by grub_relocator_alloc_chunk_align_safe(..., min_addr, max_addr, size, ...).
  2. Introduced UP_TO_TOP32(s) macro for the cases where max_addr is 32-bit top
     address (0xffffffff - size + 1) or similar.

Signed-off-by: Alexey Makhalov <amakhalov@vmware.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/lib/i386/relocator.c        | 28 +++++++++++-----------------
 grub-core/lib/mips/relocator.c        |  6 ++----
 grub-core/lib/powerpc/relocator.c     |  6 ++----
 grub-core/lib/x86_64/efi/relocator.c  |  7 +++----
 grub-core/loader/i386/linux.c         |  5 ++---
 grub-core/loader/i386/multiboot_mbi.c |  7 +++----
 grub-core/loader/i386/pc/linux.c      |  6 ++----
 grub-core/loader/mips/linux.c         |  9 +++------
 grub-core/loader/multiboot.c          |  2 +-
 grub-core/loader/multiboot_elfxx.c    | 10 +++++-----
 grub-core/loader/multiboot_mbi2.c     | 10 +++++-----
 grub-core/loader/xnu_resume.c         |  2 +-
 include/grub/relocator.h              | 29 +++++++++++++++++++++++++++++
 13 files changed, 69 insertions(+), 58 deletions(-)

diff --git a/grub-core/lib/i386/relocator.c b/grub-core/lib/i386/relocator.c
index 71dd4f0ab..34cbe834f 100644
--- a/grub-core/lib/i386/relocator.c
+++ b/grub-core/lib/i386/relocator.c
@@ -83,11 +83,10 @@ grub_relocator32_boot (struct grub_relocator *rel,
   /* Specific memory range due to Global Descriptor Table for use by payload
      that we will store in returned chunk.  The address range and preference
      are based on "THE LINUX/x86 BOOT PROTOCOL" specification.  */
-  err = grub_relocator_alloc_chunk_align (rel, &ch, 0x1000,
-					  0x9a000 - RELOCATOR_SIZEOF (32),
-					  RELOCATOR_SIZEOF (32), 16,
-					  GRUB_RELOCATOR_PREFERENCE_LOW,
-					  avoid_efi_bootservices);
+  err = grub_relocator_alloc_chunk_align_safe (rel, &ch, 0x1000, 0x9a000,
+					       RELOCATOR_SIZEOF (32), 16,
+					       GRUB_RELOCATOR_PREFERENCE_LOW,
+					       avoid_efi_bootservices);
   if (err)
     return err;
 
@@ -125,13 +124,10 @@ grub_relocator16_boot (struct grub_relocator *rel,
   grub_relocator_chunk_t ch;
 
   /* Put it higher than the byte it checks for A20 check.  */
-  err = grub_relocator_alloc_chunk_align (rel, &ch, 0x8010,
-					  0xa0000 - RELOCATOR_SIZEOF (16)
-					  - GRUB_RELOCATOR16_STACK_SIZE,
-					  RELOCATOR_SIZEOF (16)
-					  + GRUB_RELOCATOR16_STACK_SIZE, 16,
-					  GRUB_RELOCATOR_PREFERENCE_NONE,
-					  0);
+  err = grub_relocator_alloc_chunk_align_safe (rel, &ch, 0x8010, 0xa0000,
+					       RELOCATOR_SIZEOF (16) +
+					       GRUB_RELOCATOR16_STACK_SIZE, 16,
+					       GRUB_RELOCATOR_PREFERENCE_NONE, 0);
   if (err)
     return err;
 
@@ -183,11 +179,9 @@ grub_relocator64_boot (struct grub_relocator *rel,
   void *relst;
   grub_relocator_chunk_t ch;
 
-  err = grub_relocator_alloc_chunk_align (rel, &ch, min_addr,
-					  max_addr - RELOCATOR_SIZEOF (64),
-					  RELOCATOR_SIZEOF (64), 16,
-					  GRUB_RELOCATOR_PREFERENCE_NONE,
-					  0);
+  err = grub_relocator_alloc_chunk_align_safe (rel, &ch, min_addr, max_addr,
+					       RELOCATOR_SIZEOF (64), 16,
+					       GRUB_RELOCATOR_PREFERENCE_NONE, 0);
   if (err)
     return err;
 
diff --git a/grub-core/lib/mips/relocator.c b/grub-core/lib/mips/relocator.c
index 9d5f49cb9..743b213e6 100644
--- a/grub-core/lib/mips/relocator.c
+++ b/grub-core/lib/mips/relocator.c
@@ -120,10 +120,8 @@ grub_relocator32_boot (struct grub_relocator *rel,
   unsigned i;
   grub_addr_t vtarget;
 
-  err = grub_relocator_alloc_chunk_align (rel, &ch, 0,
-					  (0xffffffff - stateset_size)
-					  + 1, stateset_size,
-					  sizeof (grub_uint32_t),
+  err = grub_relocator_alloc_chunk_align (rel, &ch, 0, UP_TO_TOP32 (stateset_size),
+					  stateset_size, sizeof (grub_uint32_t),
 					  GRUB_RELOCATOR_PREFERENCE_NONE, 0);
   if (err)
     return err;
diff --git a/grub-core/lib/powerpc/relocator.c b/grub-core/lib/powerpc/relocator.c
index bdf2b111b..8ffb8b686 100644
--- a/grub-core/lib/powerpc/relocator.c
+++ b/grub-core/lib/powerpc/relocator.c
@@ -115,10 +115,8 @@ grub_relocator32_boot (struct grub_relocator *rel,
   unsigned i;
   grub_relocator_chunk_t ch;
 
-  err = grub_relocator_alloc_chunk_align (rel, &ch, 0,
-					  (0xffffffff - stateset_size)
-					  + 1, stateset_size,
-					  sizeof (grub_uint32_t),
+  err = grub_relocator_alloc_chunk_align (rel, &ch, 0, UP_TO_TOP32 (stateset_size),
+					  stateset_size, sizeof (grub_uint32_t),
 					  GRUB_RELOCATOR_PREFERENCE_NONE, 0);
   if (err)
     return err;
diff --git a/grub-core/lib/x86_64/efi/relocator.c b/grub-core/lib/x86_64/efi/relocator.c
index 3caef7a40..7d200a125 100644
--- a/grub-core/lib/x86_64/efi/relocator.c
+++ b/grub-core/lib/x86_64/efi/relocator.c
@@ -50,10 +50,9 @@ grub_relocator64_efi_boot (struct grub_relocator *rel,
    * 64-bit relocator code may live above 4 GiB quite well.
    * However, I do not want ask for problems. Just in case.
    */
-  err = grub_relocator_alloc_chunk_align (rel, &ch, 0,
-					  0x100000000 - RELOCATOR_SIZEOF (64_efi),
-					  RELOCATOR_SIZEOF (64_efi), 16,
-					  GRUB_RELOCATOR_PREFERENCE_NONE, 1);
+  err = grub_relocator_alloc_chunk_align_safe (rel, &ch, 0, 0x100000000,
+					       RELOCATOR_SIZEOF (64_efi), 16,
+					       GRUB_RELOCATOR_PREFERENCE_NONE, 1);
   if (err)
     return err;
 
diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
index 938ba536e..976af3fae 100644
--- a/grub-core/loader/i386/linux.c
+++ b/grub-core/loader/i386/linux.c
@@ -181,9 +181,8 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align,
 	for (; err && *align + 1 > min_align; (*align)--)
 	  {
 	    grub_errno = GRUB_ERR_NONE;
-	    err = grub_relocator_alloc_chunk_align (relocator, &ch,
-						    0x1000000,
-						    0xffffffff & ~prot_size,
+	    err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000000,
+						    UP_TO_TOP32 (prot_size),
 						    prot_size, 1 << *align,
 						    GRUB_RELOCATOR_PREFERENCE_LOW,
 						    1);
diff --git a/grub-core/loader/i386/multiboot_mbi.c b/grub-core/loader/i386/multiboot_mbi.c
index ad3cc292f..a67d9d0a8 100644
--- a/grub-core/loader/i386/multiboot_mbi.c
+++ b/grub-core/loader/i386/multiboot_mbi.c
@@ -466,10 +466,9 @@ grub_multiboot_make_mbi (grub_uint32_t *target)
 
   bufsize = grub_multiboot_get_mbi_size ();
 
-  err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch,
-					  0x10000, 0xa0000 - bufsize,
-					  bufsize, 4,
-					  GRUB_RELOCATOR_PREFERENCE_NONE, 0);
+  err = grub_relocator_alloc_chunk_align_safe (grub_multiboot_relocator, &ch,
+					       0x10000, 0xa0000, bufsize, 4,
+					       GRUB_RELOCATOR_PREFERENCE_NONE, 0);
   if (err)
     return err;
   ptrorig = get_virtual_current_address (ch);
diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c
index 8be763d19..814988ab9 100644
--- a/grub-core/loader/i386/pc/linux.c
+++ b/grub-core/loader/i386/pc/linux.c
@@ -453,10 +453,8 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
 
   {
     grub_relocator_chunk_t ch;
-    err = grub_relocator_alloc_chunk_align (relocator, &ch,
-					    addr_min, addr_max - size,
-					    size, 0x1000,
-					    GRUB_RELOCATOR_PREFERENCE_HIGH, 0);
+    err = grub_relocator_alloc_chunk_align_safe (relocator, &ch, addr_min, addr_max, size,
+						 0x1000, GRUB_RELOCATOR_PREFERENCE_HIGH, 0);
     if (err)
       return err;
     initrd_chunk = get_virtual_current_address (ch);
diff --git a/grub-core/loader/mips/linux.c b/grub-core/loader/mips/linux.c
index 7b723bf18..e4ed95921 100644
--- a/grub-core/loader/mips/linux.c
+++ b/grub-core/loader/mips/linux.c
@@ -442,12 +442,9 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
   {
     grub_relocator_chunk_t ch;
 
-    err = grub_relocator_alloc_chunk_align (relocator, &ch,
-					    (target_addr & 0x1fffffff)
-					    + linux_size + 0x10000,
-					    (0x10000000 - size),
-					    size, 0x10000,
-					    GRUB_RELOCATOR_PREFERENCE_NONE, 0);
+    err = grub_relocator_alloc_chunk_align_safe (relocator, &ch, (target_addr & 0x1fffffff) +
+						 linux_size + 0x10000, 0x10000000, size,
+						 0x10000, GRUB_RELOCATOR_PREFERENCE_NONE, 0);
 
     if (err)
       goto fail;
diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c
index 4a98d7082..facb13f3d 100644
--- a/grub-core/loader/multiboot.c
+++ b/grub-core/loader/multiboot.c
@@ -403,7 +403,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)),
   {
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch,
-					    lowest_addr, (0xffffffff - size) + 1,
+					    lowest_addr, UP_TO_TOP32 (size),
 					    size, MULTIBOOT_MOD_ALIGN,
 					    GRUB_RELOCATOR_PREFERENCE_NONE, 1);
     if (err)
diff --git a/grub-core/loader/multiboot_elfxx.c b/grub-core/loader/multiboot_elfxx.c
index cc6853692..f2318e0d1 100644
--- a/grub-core/loader/multiboot_elfxx.c
+++ b/grub-core/loader/multiboot_elfxx.c
@@ -109,10 +109,10 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
       if (load_size > mld->max_addr || mld->min_addr > mld->max_addr - load_size)
 	return grub_error (GRUB_ERR_BAD_OS, "invalid min/max address and/or load size");
 
-      err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch,
-					      mld->min_addr, mld->max_addr - load_size,
-					      load_size, mld->align ? mld->align : 1,
-					      mld->preference, mld->avoid_efi_boot_services);
+      err = grub_relocator_alloc_chunk_align_safe (GRUB_MULTIBOOT (relocator), &ch,
+						   mld->min_addr, mld->max_addr,
+						   load_size, mld->align ? mld->align : 1,
+						   mld->preference, mld->avoid_efi_boot_services);
 
       if (err)
         {
@@ -256,7 +256,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
 	    continue;
 
 	  err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch, 0,
-						  (0xffffffff - sh->sh_size) + 1,
+						  UP_TO_TOP32 (sh->sh_size),
 						  sh->sh_size, sh->sh_addralign,
 						  GRUB_RELOCATOR_PREFERENCE_NONE,
 						  mld->avoid_efi_boot_services);
diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c
index e88c9f488..9a943d7bd 100644
--- a/grub-core/loader/multiboot_mbi2.c
+++ b/grub-core/loader/multiboot_mbi2.c
@@ -301,10 +301,10 @@ grub_multiboot2_load (grub_file_t file, const char *filename)
 	      return grub_error (GRUB_ERR_BAD_OS, "invalid min/max address and/or load size");
 	    }
 
-	  err = grub_relocator_alloc_chunk_align (grub_multiboot2_relocator, &ch,
-						  mld.min_addr, mld.max_addr - code_size,
-						  code_size, mld.align ? mld.align : 1,
-						  mld.preference, keep_bs);
+	  err = grub_relocator_alloc_chunk_align_safe (grub_multiboot2_relocator, &ch,
+						       mld.min_addr, mld.max_addr,
+						       code_size, mld.align ? mld.align : 1,
+						       mld.preference, keep_bs);
 	}
       else
 	err = grub_relocator_alloc_chunk_addr (grub_multiboot2_relocator,
@@ -714,7 +714,7 @@ grub_multiboot2_make_mbi (grub_uint32_t *target)
   COMPILE_TIME_ASSERT (MULTIBOOT_TAG_ALIGN % sizeof (grub_properly_aligned_t) == 0);
 
   err = grub_relocator_alloc_chunk_align (grub_multiboot2_relocator, &ch,
-					  MBI_MIN_ADDR, 0xffffffff - bufsize,
+					  MBI_MIN_ADDR, UP_TO_TOP32 (bufsize),
 					  bufsize, MULTIBOOT_TAG_ALIGN,
 					  GRUB_RELOCATOR_PREFERENCE_NONE, 1);
   if (err)
diff --git a/grub-core/loader/xnu_resume.c b/grub-core/loader/xnu_resume.c
index 8089804d4..d648ef0cd 100644
--- a/grub-core/loader/xnu_resume.c
+++ b/grub-core/loader/xnu_resume.c
@@ -129,7 +129,7 @@ grub_xnu_resume (char *imagename)
   {
     grub_relocator_chunk_t ch;
     err = grub_relocator_alloc_chunk_align (grub_xnu_relocator, &ch, 0,
-					    (0xffffffff - hibhead.image_size) + 1,
+					    UP_TO_TOP32 (hibhead.image_size),
 					    hibhead.image_size,
 					    GRUB_XNU_PAGESIZE,
 					    GRUB_RELOCATOR_PREFERENCE_NONE, 0);
diff --git a/include/grub/relocator.h b/include/grub/relocator.h
index 24d8672d2..1b3bdd92a 100644
--- a/include/grub/relocator.h
+++ b/include/grub/relocator.h
@@ -49,6 +49,35 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel,
 				  int preference,
 				  int avoid_efi_boot_services);
 
+/*
+ * Wrapper for grub_relocator_alloc_chunk_align() with purpose of
+ * protecting against integer underflow.
+ *
+ * Compare to its callee, max_addr has different meaning here.
+ * It covers entire chunk and not just start address of the chunk.
+ */
+static inline grub_err_t
+grub_relocator_alloc_chunk_align_safe (struct grub_relocator *rel,
+				       grub_relocator_chunk_t *out,
+				       grub_phys_addr_t min_addr,
+				       grub_phys_addr_t max_addr,
+				       grub_size_t size, grub_size_t align,
+				       int preference,
+				       int avoid_efi_boot_services)
+{
+  /* Sanity check and ensure following equation (max_addr - size) is safe. */
+  if (max_addr < size || (max_addr - size) < min_addr)
+    return GRUB_ERR_OUT_OF_RANGE;
+
+  return grub_relocator_alloc_chunk_align (rel, out, min_addr,
+					   max_addr - size,
+					   size, align, preference,
+					   avoid_efi_boot_services);
+}
+
+/* Top 32-bit address minus s bytes and plus 1 byte. */
+#define UP_TO_TOP32(s)	((~(s) & 0xffffffff) + 1)
+
 #define GRUB_RELOCATOR_PREFERENCE_NONE 0
 #define GRUB_RELOCATOR_PREFERENCE_LOW 1
 #define GRUB_RELOCATOR_PREFERENCE_HIGH 2
-- 
2.11.0



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

* [SECURITY PATCH 18/28] script: Remove unused fields from grub_script_function struct
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (16 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 17/28] relocator: Protect grub_relocator_alloc_chunk_align() max_addr against integer underflow Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 19/28] script: Avoid a use-after-free when redefining a function during execution Daniel Kiper
                   ` (10 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

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

Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 include/grub/script_sh.h | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/include/grub/script_sh.h b/include/grub/script_sh.h
index 360c2be1f..b382bcf09 100644
--- a/include/grub/script_sh.h
+++ b/include/grub/script_sh.h
@@ -359,13 +359,8 @@ struct grub_script_function
   /* The script function.  */
   struct grub_script *func;
 
-  /* The flags.  */
-  unsigned flags;
-
   /* The next element.  */
   struct grub_script_function *next;
-
-  int references;
 };
 typedef struct grub_script_function *grub_script_function_t;
 
-- 
2.11.0



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

* [SECURITY PATCH 19/28] script: Avoid a use-after-free when redefining a function during execution
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (17 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 18/28] script: Remove unused fields from grub_script_function struct Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 20/28] relocator: Fix grub_relocator_alloc_chunk_align() top memory allocation Daniel Kiper
                   ` (9 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

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

Defining a new function with the same name as a previously defined
function causes the grub_script and associated resources for the
previous function to be freed. If the previous function is currently
executing when a function with the same name is defined, this results
in use-after-frees when processing subsequent commands in the original
function.

Instead, reject a new function definition if it has the same name as
a previously defined function, and that function is currently being
executed. Although a behavioural change, this should be backwards
compatible with existing configurations because they can't be
dependent on the current behaviour without being broken.

Fixes: CVE-2020-15706

Signed-off-by: Chris Coulson <chris.coulson@canonical.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/script/execute.c  |  2 ++
 grub-core/script/function.c | 16 +++++++++++++---
 grub-core/script/parser.y   |  3 ++-
 include/grub/script_sh.h    |  2 ++
 4 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/grub-core/script/execute.c b/grub-core/script/execute.c
index 8a9161cc8..ce83edd4b 100644
--- a/grub-core/script/execute.c
+++ b/grub-core/script/execute.c
@@ -838,7 +838,9 @@ grub_script_function_call (grub_script_function_t func, int argc, char **args)
   old_scope = scope;
   scope = &new_scope;
 
+  func->executing++;
   ret = grub_script_execute (func->func);
+  func->executing--;
 
   function_return = 0;
   active_loops = loops;
diff --git a/grub-core/script/function.c b/grub-core/script/function.c
index d36655e51..3aad04bf9 100644
--- a/grub-core/script/function.c
+++ b/grub-core/script/function.c
@@ -34,6 +34,7 @@ grub_script_function_create (struct grub_script_arg *functionname_arg,
   func = (grub_script_function_t) grub_malloc (sizeof (*func));
   if (! func)
     return 0;
+  func->executing = 0;
 
   func->name = grub_strdup (functionname_arg->str);
   if (! func->name)
@@ -60,10 +61,19 @@ grub_script_function_create (struct grub_script_arg *functionname_arg,
       grub_script_function_t q;
 
       q = *p;
-      grub_script_free (q->func);
-      q->func = cmd;
       grub_free (func);
-      func = q;
+      if (q->executing > 0)
+        {
+          grub_error (GRUB_ERR_BAD_ARGUMENT,
+		      N_("attempt to redefine a function being executed"));
+          func = NULL;
+        }
+      else
+        {
+          grub_script_free (q->func);
+          q->func = cmd;
+          func = q;
+        }
     }
   else
     {
diff --git a/grub-core/script/parser.y b/grub-core/script/parser.y
index 4f0ab8319..f80b86b6f 100644
--- a/grub-core/script/parser.y
+++ b/grub-core/script/parser.y
@@ -289,7 +289,8 @@ function: "function" "name"
 	      grub_script_mem_free (state->func_mem);
 	    else {
 	      script->children = state->scripts;
-	      grub_script_function_create ($2, script);
+	      if (!grub_script_function_create ($2, script))
+		grub_script_free (script);
 	    }
 
 	    state->scripts = $<scripts>3;
diff --git a/include/grub/script_sh.h b/include/grub/script_sh.h
index b382bcf09..6c48e0751 100644
--- a/include/grub/script_sh.h
+++ b/include/grub/script_sh.h
@@ -361,6 +361,8 @@ struct grub_script_function
 
   /* The next element.  */
   struct grub_script_function *next;
+
+  unsigned executing;
 };
 typedef struct grub_script_function *grub_script_function_t;
 
-- 
2.11.0



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

* [SECURITY PATCH 20/28] relocator: Fix grub_relocator_alloc_chunk_align() top memory allocation
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (18 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 19/28] script: Avoid a use-after-free when redefining a function during execution Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 21/28] hfsplus: Fix two more overflows Daniel Kiper
                   ` (8 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Alexey Makhalov <amakhalov@vmware.com>

Current implementation of grub_relocator_alloc_chunk_align()
does not allow allocation of the top byte.

Assuming input args are:
  max_addr = 0xfffff000;
  size = 0x1000;

And this is valid. But following overflow protection will
unnecessarily move max_addr one byte down (to 0xffffefff):
  if (max_addr > ~size)
    max_addr = ~size;

~size + 1 will fix the situation. In addition, check size
for non zero to do not zero max_addr.

Signed-off-by: Alexey Makhalov <amakhalov@vmware.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/lib/relocator.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/grub-core/lib/relocator.c b/grub-core/lib/relocator.c
index 5847aac36..f2c1944c2 100644
--- a/grub-core/lib/relocator.c
+++ b/grub-core/lib/relocator.c
@@ -1386,8 +1386,8 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel,
   };
   grub_addr_t min_addr2 = 0, max_addr2;
 
-  if (max_addr > ~size)
-    max_addr = ~size;
+  if (size && (max_addr > ~size))
+    max_addr = ~size + 1;
 
 #ifdef GRUB_MACHINE_PCBIOS
   if (min_addr < 0x1000)
-- 
2.11.0



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

* [SECURITY PATCH 21/28] hfsplus: Fix two more overflows
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (19 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 20/28] relocator: Fix grub_relocator_alloc_chunk_align() top memory allocation Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 22/28] lvm: Fix two more potential data-dependent alloc overflows Daniel Kiper
                   ` (7 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Peter Jones <pjones@redhat.com>

Both node->size and node->namelen come from the supplied filesystem,
which may be user-supplied. We can't trust them for the math unless we
know they don't overflow. Making sure they go through grub_add() or
grub_calloc() first will give us that.

Signed-off-by: Peter Jones <pjones@redhat.com>
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/fs/hfsplus.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/grub-core/fs/hfsplus.c b/grub-core/fs/hfsplus.c
index dae43becc..9c4e4c88c 100644
--- a/grub-core/fs/hfsplus.c
+++ b/grub-core/fs/hfsplus.c
@@ -31,6 +31,7 @@
 #include <grub/hfs.h>
 #include <grub/charset.h>
 #include <grub/hfsplus.h>
+#include <grub/safemath.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -475,8 +476,12 @@ grub_hfsplus_read_symlink (grub_fshelp_node_t node)
 {
   char *symlink;
   grub_ssize_t numread;
+  grub_size_t sz = node->size;
 
-  symlink = grub_malloc (node->size + 1);
+  if (grub_add (sz, 1, &sz))
+    return NULL;
+
+  symlink = grub_malloc (sz);
   if (!symlink)
     return 0;
 
@@ -715,8 +720,8 @@ list_nodes (void *record, void *hook_arg)
   if (type == GRUB_FSHELP_UNKNOWN)
     return 0;
 
-  filename = grub_malloc (grub_be_to_cpu16 (catkey->namelen)
-			  * GRUB_MAX_UTF8_PER_UTF16 + 1);
+  filename = grub_calloc (grub_be_to_cpu16 (catkey->namelen),
+			  GRUB_MAX_UTF8_PER_UTF16 + 1);
   if (! filename)
     return 0;
 
-- 
2.11.0



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

* [SECURITY PATCH 22/28] lvm: Fix two more potential data-dependent alloc overflows
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (20 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 21/28] hfsplus: Fix two more overflows Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 23/28] emu: Make grub_free(NULL) safe Daniel Kiper
                   ` (6 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Peter Jones <pjones@redhat.com>

It appears to be possible to make a (possibly invalid) lvm PV with
a metadata size field that overflows our type when adding it to the
address we've allocated. Even if it doesn't, it may be possible to do so
with the math using the outcome of that as an operand. Check them both.

Signed-off-by: Peter Jones <pjones@redhat.com>
Signed-off-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/disk/lvm.c | 48 ++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 40 insertions(+), 8 deletions(-)

diff --git a/grub-core/disk/lvm.c b/grub-core/disk/lvm.c
index 48e36b4f3..753545146 100644
--- a/grub-core/disk/lvm.c
+++ b/grub-core/disk/lvm.c
@@ -25,6 +25,7 @@
 #include <grub/lvm.h>
 #include <grub/partition.h>
 #include <grub/i18n.h>
+#include <grub/safemath.h>
 
 #ifdef GRUB_UTIL
 #include <grub/emu/misc.h>
@@ -138,10 +139,11 @@ grub_lvm_detect (grub_disk_t disk,
 {
   grub_err_t err;
   grub_uint64_t mda_offset, mda_size;
+  grub_size_t ptr;
   char buf[GRUB_LVM_LABEL_SIZE];
   char vg_id[GRUB_LVM_ID_STRLEN+1];
   char pv_id[GRUB_LVM_ID_STRLEN+1];
-  char *metadatabuf, *vgname;
+  char *metadatabuf, *mda_end, *vgname;
   const char *p, *q;
   struct grub_lvm_label_header *lh = (struct grub_lvm_label_header *) buf;
   struct grub_lvm_pv_header *pvh;
@@ -242,19 +244,31 @@ grub_lvm_detect (grub_disk_t disk,
 		   grub_le_to_cpu64 (rlocn->size) -
 		   grub_le_to_cpu64 (mdah->size));
     }
-  p = q = metadatabuf + grub_le_to_cpu64 (rlocn->offset);
 
-  while (*q != ' ' && q < metadatabuf + mda_size)
-    q++;
-
-  if (q == metadatabuf + mda_size)
+  if (grub_add ((grub_size_t)metadatabuf,
+		(grub_size_t)grub_le_to_cpu64 (rlocn->offset),
+		&ptr))
     {
+ error_parsing_metadata:
 #ifdef GRUB_UTIL
       grub_util_info ("error parsing metadata");
 #endif
       goto fail2;
     }
 
+  p = q = (char *)ptr;
+
+  if (grub_add ((grub_size_t)metadatabuf, (grub_size_t)mda_size, &ptr))
+    goto error_parsing_metadata;
+
+  mda_end = (char *)ptr;
+
+  while (*q != ' ' && q < mda_end)
+    q++;
+
+  if (q == mda_end)
+    goto error_parsing_metadata;
+
   vgname_len = q - p;
   vgname = grub_malloc (vgname_len + 1);
   if (!vgname)
@@ -406,8 +420,26 @@ grub_lvm_detect (grub_disk_t disk,
 	      {
 		const char *iptr;
 		char *optr;
-		lv->fullname = grub_malloc (sizeof ("lvm/") - 1 + 2 * vgname_len
-					    + 1 + 2 * s + 1);
+
+		/*
+		 * This is kind of hard to read with our safe (but rather
+		 * baroque) math primatives, but it boils down to:
+		 *
+		 *   sz0 = vgname_len * 2 + 1 +
+		 *         s * 2 + 1 +
+		 *         sizeof ("lvm/") - 1;
+		 */
+		grub_size_t sz0 = vgname_len, sz1 = s;
+
+		if (grub_mul (sz0, 2, &sz0) ||
+		    grub_add (sz0, 1, &sz0) ||
+		    grub_mul (sz1, 2, &sz1) ||
+		    grub_add (sz1, 1, &sz1) ||
+		    grub_add (sz0, sz1, &sz0) ||
+		    grub_add (sz0, sizeof ("lvm/") - 1, &sz0))
+		  goto lvs_fail;
+
+		lv->fullname = grub_malloc (sz0);
 		if (!lv->fullname)
 		  goto lvs_fail;
 
-- 
2.11.0



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

* [SECURITY PATCH 23/28] emu: Make grub_free(NULL) safe
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (21 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 22/28] lvm: Fix two more potential data-dependent alloc overflows Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 24/28] efi: Fix some malformed device path arithmetic errors Daniel Kiper
                   ` (5 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Peter Jones <pjones@redhat.com>

The grub_free() implementation in grub-core/kern/mm.c safely handles
NULL pointers, and code at many places depends on this. We don't know
that the same is true on all host OSes, so we need to handle the same
behavior in grub-emu's implementation.

Signed-off-by: Peter Jones <pjones@redhat.com>
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/kern/emu/mm.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/grub-core/kern/emu/mm.c b/grub-core/kern/emu/mm.c
index 145b01d37..4d1046a21 100644
--- a/grub-core/kern/emu/mm.c
+++ b/grub-core/kern/emu/mm.c
@@ -60,7 +60,8 @@ grub_zalloc (grub_size_t size)
 void
 grub_free (void *ptr)
 {
-  free (ptr);
+  if (ptr)
+    free (ptr);
 }
 
 void *
-- 
2.11.0



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

* [SECURITY PATCH 24/28] efi: Fix some malformed device path arithmetic errors
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (22 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 23/28] emu: Make grub_free(NULL) safe Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 25/28] efi/chainloader: Propagate errors from copy_file_path() Daniel Kiper
                   ` (4 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Peter Jones <pjones@redhat.com>

Several places we take the length of a device path and subtract 4 from
it, without ever checking that it's >= 4. There are also cases where
this kind of malformation will result in unpredictable iteration,
including treating the length from one dp node as the type in the next
node. These are all errors, no matter where the data comes from.

This patch adds a checking macro, GRUB_EFI_DEVICE_PATH_VALID(), which
can be used in several places, and makes GRUB_EFI_NEXT_DEVICE_PATH()
return NULL and GRUB_EFI_END_ENTIRE_DEVICE_PATH() evaluate as true when
the length is too small. Additionally, it makes several places in the
code check for and return errors in these cases.

Signed-off-by: Peter Jones <pjones@redhat.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/kern/efi/efi.c           | 64 ++++++++++++++++++++++++++++++++------
 grub-core/loader/efi/chainloader.c | 13 ++++++--
 grub-core/loader/i386/xnu.c        |  9 +++---
 include/grub/efi/api.h             | 14 ++++++---
 4 files changed, 79 insertions(+), 21 deletions(-)

diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
index b2d07a3fa..535e29424 100644
--- a/grub-core/kern/efi/efi.c
+++ b/grub-core/kern/efi/efi.c
@@ -342,7 +342,7 @@ grub_efi_get_filename (grub_efi_device_path_t *dp0)
 
   dp = dp0;
 
-  while (1)
+  while (dp)
     {
       grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
       grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
@@ -352,9 +352,15 @@ grub_efi_get_filename (grub_efi_device_path_t *dp0)
       if (type == GRUB_EFI_MEDIA_DEVICE_PATH_TYPE
 	       && subtype == GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE)
 	{
-	  grub_efi_uint16_t len;
-	  len = ((GRUB_EFI_DEVICE_PATH_LENGTH (dp) - 4)
-		 / sizeof (grub_efi_char16_t));
+	  grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp);
+
+	  if (len < 4)
+	    {
+	      grub_error (GRUB_ERR_OUT_OF_RANGE,
+			  "malformed EFI Device Path node has length=%d", len);
+	      return NULL;
+	    }
+	  len = (len - 4) / sizeof (grub_efi_char16_t);
 	  filesize += GRUB_MAX_UTF8_PER_UTF16 * len + 2;
 	}
 
@@ -370,7 +376,7 @@ grub_efi_get_filename (grub_efi_device_path_t *dp0)
   if (!name)
     return NULL;
 
-  while (1)
+  while (dp)
     {
       grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
       grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
@@ -386,8 +392,15 @@ grub_efi_get_filename (grub_efi_device_path_t *dp0)
 
 	  *p++ = '/';
 
-	  len = ((GRUB_EFI_DEVICE_PATH_LENGTH (dp) - 4)
-		 / sizeof (grub_efi_char16_t));
+	  len = GRUB_EFI_DEVICE_PATH_LENGTH (dp);
+	  if (len < 4)
+	    {
+	      grub_error (GRUB_ERR_OUT_OF_RANGE,
+			  "malformed EFI Device Path node has length=%d", len);
+	      return NULL;
+	    }
+
+	  len = (len - 4) / sizeof (grub_efi_char16_t);
 	  fp = (grub_efi_file_path_device_path_t *) dp;
 	  /* According to EFI spec Path Name is NULL terminated */
 	  while (len > 0 && fp->path_name[len - 1] == 0)
@@ -462,7 +475,26 @@ grub_efi_duplicate_device_path (const grub_efi_device_path_t *dp)
        ;
        p = GRUB_EFI_NEXT_DEVICE_PATH (p))
     {
-      total_size += GRUB_EFI_DEVICE_PATH_LENGTH (p);
+      grub_size_t len = GRUB_EFI_DEVICE_PATH_LENGTH (p);
+
+      /*
+       * In the event that we find a node that's completely garbage, for
+       * example if we get to 0x7f 0x01 0x02 0x00 ... (EndInstance with a size
+       * of 2), GRUB_EFI_END_ENTIRE_DEVICE_PATH() will be true and
+       * GRUB_EFI_NEXT_DEVICE_PATH() will return NULL, so we won't continue,
+       * and neither should our consumers, but there won't be any error raised
+       * even though the device path is junk.
+       *
+       * This keeps us from passing junk down back to our caller.
+       */
+      if (len < 4)
+	{
+	  grub_error (GRUB_ERR_OUT_OF_RANGE,
+		      "malformed EFI Device Path node has length=%d", len);
+	  return NULL;
+	}
+
+      total_size += len;
       if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (p))
 	break;
     }
@@ -507,7 +539,7 @@ dump_vendor_path (const char *type, grub_efi_vendor_device_path_t *vendor)
 void
 grub_efi_print_device_path (grub_efi_device_path_t *dp)
 {
-  while (1)
+  while (GRUB_EFI_DEVICE_PATH_VALID (dp))
     {
       grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
       grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
@@ -919,7 +951,10 @@ grub_efi_compare_device_paths (const grub_efi_device_path_t *dp1,
     /* Return non-zero.  */
     return 1;
 
-  while (1)
+  if (dp1 == dp2)
+    return 0;
+
+  while (GRUB_EFI_DEVICE_PATH_VALID (dp1) && GRUB_EFI_DEVICE_PATH_VALID (dp2))
     {
       grub_efi_uint8_t type1, type2;
       grub_efi_uint8_t subtype1, subtype2;
@@ -955,5 +990,14 @@ grub_efi_compare_device_paths (const grub_efi_device_path_t *dp1,
       dp2 = (grub_efi_device_path_t *) ((char *) dp2 + len2);
     }
 
+  /*
+   * There's no "right" answer here, but we probably don't want to call a valid
+   * dp and an invalid dp equal, so pick one way or the other.
+   */
+  if (GRUB_EFI_DEVICE_PATH_VALID (dp1) && !GRUB_EFI_DEVICE_PATH_VALID (dp2))
+    return 1;
+  else if (!GRUB_EFI_DEVICE_PATH_VALID (dp1) && GRUB_EFI_DEVICE_PATH_VALID (dp2))
+    return -1;
+
   return 0;
 }
diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
index daf8c6b54..a8d7b9155 100644
--- a/grub-core/loader/efi/chainloader.c
+++ b/grub-core/loader/efi/chainloader.c
@@ -156,9 +156,18 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename)
 
   size = 0;
   d = dp;
-  while (1)
+  while (d)
     {
-      size += GRUB_EFI_DEVICE_PATH_LENGTH (d);
+      grub_size_t len = GRUB_EFI_DEVICE_PATH_LENGTH (d);
+
+      if (len < 4)
+	{
+	  grub_error (GRUB_ERR_OUT_OF_RANGE,
+		      "malformed EFI Device Path node has length=%d", len);
+	  return NULL;
+	}
+
+      size += len;
       if ((GRUB_EFI_END_ENTIRE_DEVICE_PATH (d)))
 	break;
       d = GRUB_EFI_NEXT_DEVICE_PATH (d);
diff --git a/grub-core/loader/i386/xnu.c b/grub-core/loader/i386/xnu.c
index e9e119259..a70093607 100644
--- a/grub-core/loader/i386/xnu.c
+++ b/grub-core/loader/i386/xnu.c
@@ -515,14 +515,15 @@ grub_cmd_devprop_load (grub_command_t cmd __attribute__ ((unused)),
 
       devhead = buf;
       buf = devhead + 1;
-      dpstart = buf;
+      dp = dpstart = buf;
 
-      do
+      while (GRUB_EFI_DEVICE_PATH_VALID (dp) && buf < bufend)
 	{
-	  dp = buf;
 	  buf = (char *) buf + GRUB_EFI_DEVICE_PATH_LENGTH (dp);
+	  if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp))
+	    break;
+	  dp = buf;
 	}
-      while (!GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp) && buf < bufend);
 
       dev = grub_xnu_devprop_add_device (dpstart, (char *) buf
 					 - (char *) dpstart);
diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
index 937058d68..1dcaa12f5 100644
--- a/include/grub/efi/api.h
+++ b/include/grub/efi/api.h
@@ -630,6 +630,7 @@ typedef struct grub_efi_device_path grub_efi_device_path_protocol_t;
 #define GRUB_EFI_DEVICE_PATH_TYPE(dp)		((dp)->type & 0x7f)
 #define GRUB_EFI_DEVICE_PATH_SUBTYPE(dp)	((dp)->subtype)
 #define GRUB_EFI_DEVICE_PATH_LENGTH(dp)		((dp)->length)
+#define GRUB_EFI_DEVICE_PATH_VALID(dp)		((dp) != NULL && GRUB_EFI_DEVICE_PATH_LENGTH (dp) >= 4)
 
 /* The End of Device Path nodes.  */
 #define GRUB_EFI_END_DEVICE_PATH_TYPE			(0xff & 0x7f)
@@ -638,13 +639,16 @@ typedef struct grub_efi_device_path grub_efi_device_path_protocol_t;
 #define GRUB_EFI_END_THIS_DEVICE_PATH_SUBTYPE		0x01
 
 #define GRUB_EFI_END_ENTIRE_DEVICE_PATH(dp)	\
-  (GRUB_EFI_DEVICE_PATH_TYPE (dp) == GRUB_EFI_END_DEVICE_PATH_TYPE \
-   && (GRUB_EFI_DEVICE_PATH_SUBTYPE (dp) \
-       == GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE))
+  (!GRUB_EFI_DEVICE_PATH_VALID (dp) || \
+   (GRUB_EFI_DEVICE_PATH_TYPE (dp) == GRUB_EFI_END_DEVICE_PATH_TYPE \
+    && (GRUB_EFI_DEVICE_PATH_SUBTYPE (dp) \
+	== GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE)))
 
 #define GRUB_EFI_NEXT_DEVICE_PATH(dp)	\
-  ((grub_efi_device_path_t *) ((char *) (dp) \
-                               + GRUB_EFI_DEVICE_PATH_LENGTH (dp)))
+  (GRUB_EFI_DEVICE_PATH_VALID (dp) \
+   ? ((grub_efi_device_path_t *) \
+      ((char *) (dp) + GRUB_EFI_DEVICE_PATH_LENGTH (dp))) \
+   : NULL)
 
 /* Hardware Device Path.  */
 #define GRUB_EFI_HARDWARE_DEVICE_PATH_TYPE		1
-- 
2.11.0



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

* [SECURITY PATCH 25/28] efi/chainloader: Propagate errors from copy_file_path()
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (23 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 24/28] efi: Fix some malformed device path arithmetic errors Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 26/28] efi: Fix use-after-free in halt/reboot path Daniel Kiper
                   ` (3 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

Without any error propagated to the caller, make_file_path()
would then try to advance the invalid device path node with
GRUB_EFI_NEXT_DEVICE_PATH(), which would fail, returning a NULL
pointer that would subsequently be dereferenced. Hence, propagate
errors from copy_file_path().

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

diff --git a/grub-core/loader/efi/chainloader.c b/grub-core/loader/efi/chainloader.c
index a8d7b9155..7b31c3fb9 100644
--- a/grub-core/loader/efi/chainloader.c
+++ b/grub-core/loader/efi/chainloader.c
@@ -106,7 +106,7 @@ grub_chainloader_boot (void)
   return grub_errno;
 }
 
-static void
+static grub_err_t
 copy_file_path (grub_efi_file_path_device_path_t *fp,
 		const char *str, grub_efi_uint16_t len)
 {
@@ -118,7 +118,7 @@ copy_file_path (grub_efi_file_path_device_path_t *fp,
 
   path_name = grub_calloc (len, GRUB_MAX_UTF16_PER_UTF8 * sizeof (*path_name));
   if (!path_name)
-    return;
+    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "failed to allocate path buffer");
 
   size = grub_utf8_to_utf16 (path_name, len * GRUB_MAX_UTF16_PER_UTF8,
 			     (const grub_uint8_t *) str, len, 0);
@@ -131,6 +131,7 @@ copy_file_path (grub_efi_file_path_device_path_t *fp,
   fp->path_name[size++] = '\0';
   fp->header.length = size * sizeof (grub_efi_char16_t) + sizeof (*fp);
   grub_free (path_name);
+  return GRUB_ERR_NONE;
 }
 
 static grub_efi_device_path_t *
@@ -189,13 +190,19 @@ make_file_path (grub_efi_device_path_t *dp, const char *filename)
   d = (grub_efi_device_path_t *) ((char *) file_path
 				  + ((char *) d - (char *) dp));
   grub_efi_print_device_path (d);
-  copy_file_path ((grub_efi_file_path_device_path_t *) d,
-		  dir_start, dir_end - dir_start);
+  if (copy_file_path ((grub_efi_file_path_device_path_t *) d,
+		      dir_start, dir_end - dir_start) != GRUB_ERR_NONE)
+    {
+ fail:
+      grub_free (file_path);
+      return 0;
+    }
 
   /* Fill the file path for the file.  */
   d = GRUB_EFI_NEXT_DEVICE_PATH (d);
-  copy_file_path ((grub_efi_file_path_device_path_t *) d,
-		  dir_end + 1, grub_strlen (dir_end + 1));
+  if (copy_file_path ((grub_efi_file_path_device_path_t *) d,
+		      dir_end + 1, grub_strlen (dir_end + 1)) != GRUB_ERR_NONE)
+    goto fail;
 
   /* Fill the end of device path nodes.  */
   d = GRUB_EFI_NEXT_DEVICE_PATH (d);
-- 
2.11.0



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

* [SECURITY PATCH 26/28] efi: Fix use-after-free in halt/reboot path
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (24 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 25/28] efi/chainloader: Propagate errors from copy_file_path() Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 27/28] loader/linux: Avoid overflow on initrd size calculation Daniel Kiper
                   ` (2 subsequent siblings)
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Alexey Makhalov <amakhalov@vmware.com>

commit 92bfc33db984 ("efi: Free malloc regions on exit")
introduced memory freeing in grub_efi_fini(), which is
used not only by exit path but by halt/reboot one as well.
As result of memory freeing, code and data regions used by
modules, such as halt, reboot, acpi (used by halt) also got
freed. After return to module code, CPU executes, filled
by UEFI firmware (tested with edk2), 0xAFAFAFAF pattern as
a code. Which leads to #UD exception later.

grub> halt
!!!! X64 Exception Type - 06(#UD - Invalid Opcode)  CPU Apic ID - 00000000 !!!!
RIP  - 0000000003F4EC28, CS  - 0000000000000038, RFLAGS - 0000000000200246
RAX  - 0000000000000000, RCX - 00000000061DA188, RDX - 0A74C0854DC35D41
RBX  - 0000000003E10E08, RSP - 0000000007F0F860, RBP - 0000000000000000
RSI  - 00000000064DB768, RDI - 000000000832C5C3
R8   - 0000000000000002, R9  - 0000000000000000, R10 - 00000000061E2E52
R11  - 0000000000000020, R12 - 0000000003EE5C1F, R13 - 00000000061E0FF4
R14  - 0000000003E10D80, R15 - 00000000061E2F60
DS   - 0000000000000030, ES  - 0000000000000030, FS  - 0000000000000030
GS   - 0000000000000030, SS  - 0000000000000030
CR0  - 0000000080010033, CR2 - 0000000000000000, CR3 - 0000000007C01000
CR4  - 0000000000000668, CR8 - 0000000000000000
DR0  - 0000000000000000, DR1 - 0000000000000000, DR2 - 0000000000000000
DR3  - 0000000000000000, DR6 - 00000000FFFF0FF0, DR7 - 0000000000000400
GDTR - 00000000079EEA98 0000000000000047, LDTR - 0000000000000000
IDTR - 0000000007598018 0000000000000FFF,   TR - 0000000000000000
FXSAVE_STATE - 0000000007F0F4C0

Proposal here is to continue to free allocated memory for
exit boot services path but keep it for halt/reboot path
as it won't be much security concern here.
Introduced GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY
loader flag to be used by efi halt/reboot path.

Signed-off-by: Alexey Makhalov <amakhalov@vmware.com>
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/kern/arm/efi/init.c   | 3 +++
 grub-core/kern/arm64/efi/init.c | 3 +++
 grub-core/kern/efi/efi.c        | 3 ++-
 grub-core/kern/efi/init.c       | 1 -
 grub-core/kern/i386/efi/init.c  | 9 +++++++--
 grub-core/kern/ia64/efi/init.c  | 9 +++++++--
 grub-core/kern/riscv/efi/init.c | 3 +++
 grub-core/lib/efi/halt.c        | 3 ++-
 include/grub/loader.h           | 1 +
 9 files changed, 28 insertions(+), 7 deletions(-)

diff --git a/grub-core/kern/arm/efi/init.c b/grub-core/kern/arm/efi/init.c
index 06df60e2f..40c3b467f 100644
--- a/grub-core/kern/arm/efi/init.c
+++ b/grub-core/kern/arm/efi/init.c
@@ -71,4 +71,7 @@ grub_machine_fini (int flags)
   efi_call_1 (b->close_event, tmr_evt);
 
   grub_efi_fini ();
+
+  if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY))
+    grub_efi_memory_fini ();
 }
diff --git a/grub-core/kern/arm64/efi/init.c b/grub-core/kern/arm64/efi/init.c
index 6224999ec..5010caefd 100644
--- a/grub-core/kern/arm64/efi/init.c
+++ b/grub-core/kern/arm64/efi/init.c
@@ -57,4 +57,7 @@ grub_machine_fini (int flags)
     return;
 
   grub_efi_fini ();
+
+  if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY))
+    grub_efi_memory_fini ();
 }
diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
index 535e29424..e0165e74c 100644
--- a/grub-core/kern/efi/efi.c
+++ b/grub-core/kern/efi/efi.c
@@ -157,7 +157,8 @@ grub_efi_get_loaded_image (grub_efi_handle_t image_handle)
 void
 grub_reboot (void)
 {
-  grub_machine_fini (GRUB_LOADER_FLAG_NORETURN);
+  grub_machine_fini (GRUB_LOADER_FLAG_NORETURN |
+		     GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY);
   efi_call_4 (grub_efi_system_table->runtime_services->reset_system,
               GRUB_EFI_RESET_COLD, GRUB_EFI_SUCCESS, 0, NULL);
   for (;;) ;
diff --git a/grub-core/kern/efi/init.c b/grub-core/kern/efi/init.c
index 3dfdf2d22..2c31847bf 100644
--- a/grub-core/kern/efi/init.c
+++ b/grub-core/kern/efi/init.c
@@ -80,5 +80,4 @@ grub_efi_fini (void)
 {
   grub_efidisk_fini ();
   grub_console_fini ();
-  grub_efi_memory_fini ();
 }
diff --git a/grub-core/kern/i386/efi/init.c b/grub-core/kern/i386/efi/init.c
index da499aba0..deb2eacd8 100644
--- a/grub-core/kern/i386/efi/init.c
+++ b/grub-core/kern/i386/efi/init.c
@@ -39,6 +39,11 @@ grub_machine_init (void)
 void
 grub_machine_fini (int flags)
 {
-  if (flags & GRUB_LOADER_FLAG_NORETURN)
-    grub_efi_fini ();
+  if (!(flags & GRUB_LOADER_FLAG_NORETURN))
+    return;
+
+  grub_efi_fini ();
+
+  if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY))
+    grub_efi_memory_fini ();
 }
diff --git a/grub-core/kern/ia64/efi/init.c b/grub-core/kern/ia64/efi/init.c
index b5ecbd091..f1965571b 100644
--- a/grub-core/kern/ia64/efi/init.c
+++ b/grub-core/kern/ia64/efi/init.c
@@ -70,6 +70,11 @@ grub_machine_init (void)
 void
 grub_machine_fini (int flags)
 {
-  if (flags & GRUB_LOADER_FLAG_NORETURN)
-    grub_efi_fini ();
+  if (!(flags & GRUB_LOADER_FLAG_NORETURN))
+    return;
+
+  grub_efi_fini ();
+
+  if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY))
+    grub_efi_memory_fini ();
 }
diff --git a/grub-core/kern/riscv/efi/init.c b/grub-core/kern/riscv/efi/init.c
index 7eb1969d0..38795fe67 100644
--- a/grub-core/kern/riscv/efi/init.c
+++ b/grub-core/kern/riscv/efi/init.c
@@ -73,4 +73,7 @@ grub_machine_fini (int flags)
     return;
 
   grub_efi_fini ();
+
+  if (!(flags & GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY))
+    grub_efi_memory_fini ();
 }
diff --git a/grub-core/lib/efi/halt.c b/grub-core/lib/efi/halt.c
index 5859f0498..29d413641 100644
--- a/grub-core/lib/efi/halt.c
+++ b/grub-core/lib/efi/halt.c
@@ -28,7 +28,8 @@
 void
 grub_halt (void)
 {
-  grub_machine_fini (GRUB_LOADER_FLAG_NORETURN);
+  grub_machine_fini (GRUB_LOADER_FLAG_NORETURN |
+		     GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY);
 #if !defined(__ia64__) && !defined(__arm__) && !defined(__aarch64__) && \
     !defined(__riscv)
   grub_acpi_halt ();
diff --git a/include/grub/loader.h b/include/grub/loader.h
index 7f82a499f..b20864282 100644
--- a/include/grub/loader.h
+++ b/include/grub/loader.h
@@ -33,6 +33,7 @@ enum
 {
   GRUB_LOADER_FLAG_NORETURN = 1,
   GRUB_LOADER_FLAG_PXE_NOT_UNLOAD = 2,
+  GRUB_LOADER_FLAG_EFI_KEEP_ALLOCATED_MEMORY = 4,
 };
 
 void EXPORT_FUNC (grub_loader_set) (grub_err_t (*boot) (void),
-- 
2.11.0



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

* [SECURITY PATCH 27/28] loader/linux: Avoid overflow on initrd size calculation
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (25 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 26/28] efi: Fix use-after-free in halt/reboot path Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 17:00 ` [SECURITY PATCH 28/28] linux: Fix integer overflows in initrd size handling Daniel Kiper
  2020-07-29 20:12 ` [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Christian Hesse
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Peter Jones <pjones@redhat.com>

Signed-off-by: Peter Jones <pjones@redhat.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/loader/linux.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/grub-core/loader/linux.c b/grub-core/loader/linux.c
index 471b214d6..4cd8c20c7 100644
--- a/grub-core/loader/linux.c
+++ b/grub-core/loader/linux.c
@@ -151,8 +151,7 @@ grub_initrd_init (int argc, char *argv[],
   initrd_ctx->nfiles = 0;
   initrd_ctx->components = 0;
 
-  initrd_ctx->components = grub_zalloc (argc
-					* sizeof (initrd_ctx->components[0]));
+  initrd_ctx->components = grub_calloc (argc, sizeof (initrd_ctx->components[0]));
   if (!initrd_ctx->components)
     return grub_errno;
 
-- 
2.11.0



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

* [SECURITY PATCH 28/28] linux: Fix integer overflows in initrd size handling
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (26 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 27/28] loader/linux: Avoid overflow on initrd size calculation Daniel Kiper
@ 2020-07-29 17:00 ` Daniel Kiper
  2020-07-29 20:12 ` [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Christian Hesse
  28 siblings, 0 replies; 34+ messages in thread
From: Daniel Kiper @ 2020-07-29 17:00 UTC (permalink / raw)
  To: grub-devel
  Cc: 93sam, alexander.burmashev, amakhalov, chris.coulson, cjwatson,
	cperry, darren.kenny, darren.moffat, dave.miner, degranit,
	eric.snowberg, ilya.okomin, jan.setjeeilers, jerecox, jesse,
	john.haxby, kanth.ghatraju, konrad.wilk, mbenatto, mickey,
	msrc57813grub, phcoder, pjones, sajacobu, todd.vierling, xnox

From: Colin Watson <cjwatson@debian.org>

These could be triggered by a crafted filesystem with very large files.

Fixes: CVE-2020-15707

Signed-off-by: Colin Watson <cjwatson@debian.org>
Reviewed-by: Jan Setje-Eilers <jan.setjeeilers@oracle.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/loader/linux.c | 74 +++++++++++++++++++++++++++++++++++-------------
 1 file changed, 54 insertions(+), 20 deletions(-)

diff --git a/grub-core/loader/linux.c b/grub-core/loader/linux.c
index 4cd8c20c7..3fe390f17 100644
--- a/grub-core/loader/linux.c
+++ b/grub-core/loader/linux.c
@@ -4,6 +4,7 @@
 #include <grub/misc.h>
 #include <grub/file.h>
 #include <grub/mm.h>
+#include <grub/safemath.h>
 
 struct newc_head
 {
@@ -98,13 +99,13 @@ free_dir (struct dir *root)
   grub_free (root);
 }
 
-static grub_size_t
+static grub_err_t
 insert_dir (const char *name, struct dir **root,
-	    grub_uint8_t *ptr)
+	    grub_uint8_t *ptr, grub_size_t *size)
 {
   struct dir *cur, **head = root;
   const char *cb, *ce = name;
-  grub_size_t size = 0;
+  *size = 0;
   while (1)
     {
       for (cb = ce; *cb == '/'; cb++);
@@ -130,14 +131,22 @@ insert_dir (const char *name, struct dir **root,
 	      ptr = make_header (ptr, name, ce - name,
 				 040777, 0);
 	    }
-	  size += ALIGN_UP ((ce - (char *) name)
-			    + sizeof (struct newc_head), 4);
+	  if (grub_add (*size,
+		        ALIGN_UP ((ce - (char *) name)
+				  + sizeof (struct newc_head), 4),
+			size))
+	    {
+	      grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
+	      grub_free (n->name);
+	      grub_free (n);
+	      return grub_errno;
+	    }
 	  *head = n;
 	  cur = n;
 	}
       root = &cur->next;
     }
-  return size;
+  return GRUB_ERR_NONE;
 }
 
 grub_err_t
@@ -172,26 +181,33 @@ grub_initrd_init (int argc, char *argv[],
 	  eptr = grub_strchr (ptr, ':');
 	  if (eptr)
 	    {
+	      grub_size_t dir_size, name_len;
+
 	      initrd_ctx->components[i].newc_name = grub_strndup (ptr, eptr - ptr);
-	      if (!initrd_ctx->components[i].newc_name)
+	      if (!initrd_ctx->components[i].newc_name ||
+		  insert_dir (initrd_ctx->components[i].newc_name, &root, 0,
+			      &dir_size))
 		{
 		  grub_initrd_close (initrd_ctx);
 		  return grub_errno;
 		}
-	      initrd_ctx->size
-		+= ALIGN_UP (sizeof (struct newc_head)
-			    + grub_strlen (initrd_ctx->components[i].newc_name),
-			     4);
-	      initrd_ctx->size += insert_dir (initrd_ctx->components[i].newc_name,
-					      &root, 0);
+	      name_len = grub_strlen (initrd_ctx->components[i].newc_name);
+	      if (grub_add (initrd_ctx->size,
+			    ALIGN_UP (sizeof (struct newc_head) + name_len, 4),
+			    &initrd_ctx->size) ||
+		  grub_add (initrd_ctx->size, dir_size, &initrd_ctx->size))
+		goto overflow;
 	      newc = 1;
 	      fname = eptr + 1;
 	    }
 	}
       else if (newc)
 	{
-	  initrd_ctx->size += ALIGN_UP (sizeof (struct newc_head)
-					+ sizeof ("TRAILER!!!") - 1, 4);
+	  if (grub_add (initrd_ctx->size,
+			ALIGN_UP (sizeof (struct newc_head)
+				  + sizeof ("TRAILER!!!") - 1, 4),
+			&initrd_ctx->size))
+	    goto overflow;
 	  free_dir (root);
 	  root = 0;
 	  newc = 0;
@@ -207,19 +223,29 @@ grub_initrd_init (int argc, char *argv[],
       initrd_ctx->nfiles++;
       initrd_ctx->components[i].size
 	= grub_file_size (initrd_ctx->components[i].file);
-      initrd_ctx->size += initrd_ctx->components[i].size;
+      if (grub_add (initrd_ctx->size, initrd_ctx->components[i].size,
+		    &initrd_ctx->size))
+	goto overflow;
     }
 
   if (newc)
     {
       initrd_ctx->size = ALIGN_UP (initrd_ctx->size, 4);
-      initrd_ctx->size += ALIGN_UP (sizeof (struct newc_head)
-				    + sizeof ("TRAILER!!!") - 1, 4);
+      if (grub_add (initrd_ctx->size,
+		    ALIGN_UP (sizeof (struct newc_head)
+			      + sizeof ("TRAILER!!!") - 1, 4),
+		    &initrd_ctx->size))
+	goto overflow;
       free_dir (root);
       root = 0;
     }
   
   return GRUB_ERR_NONE;
+
+ overflow:
+  free_dir (root);
+  grub_initrd_close (initrd_ctx);
+  return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
 }
 
 grub_size_t
@@ -260,8 +286,16 @@ grub_initrd_load (struct grub_linux_initrd_context *initrd_ctx,
 
       if (initrd_ctx->components[i].newc_name)
 	{
-	  ptr += insert_dir (initrd_ctx->components[i].newc_name,
-			     &root, ptr);
+	  grub_size_t dir_size;
+
+	  if (insert_dir (initrd_ctx->components[i].newc_name, &root, ptr,
+			  &dir_size))
+	    {
+	      free_dir (root);
+	      grub_initrd_close (initrd_ctx);
+	      return grub_errno;
+	    }
+	  ptr += dir_size;
 	  ptr = make_header (ptr, initrd_ctx->components[i].newc_name,
 			     grub_strlen (initrd_ctx->components[i].newc_name),
 			     0100777,
-- 
2.11.0



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

* Re: [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole
  2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
                   ` (27 preceding siblings ...)
  2020-07-29 17:00 ` [SECURITY PATCH 28/28] linux: Fix integer overflows in initrd size handling Daniel Kiper
@ 2020-07-29 20:12 ` Christian Hesse
  2020-07-29 20:20   ` John Paul Adrian Glaubitz
  28 siblings, 1 reply; 34+ messages in thread
From: Christian Hesse @ 2020-07-29 20:12 UTC (permalink / raw)
  To: Daniel Kiper; +Cc: The development of GNU GRUB

[-- Attachment #1: Type: text/plain, Size: 846 bytes --]

Daniel Kiper <daniel.kiper@oracle.com> on Wed, 2020/07/29 19:00:
> I am posting all the GRUB2 upstream patches which fixes 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 [5] too.

This does not apply on top of grub 2.04. Will downstream maintainers have to
do their cherry-picking on its own or will a maintenance branch on top of
grub-2.04 (or what ever) be available?
I would like to push updates to the Arch Linux repositories.
Thanks!
-- 
main(a){char*c=/*    Schoene Gruesse                         */"B?IJj;MEH"
"CX:;",b;for(a/*    Best regards             my address:    */=0;b=c[a++];)
putchar(b-1/(/*    Chris            cc -ox -xc - && ./x    */b/42*2-3)*42);}

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

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

* Re: [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole
  2020-07-29 20:12 ` [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Christian Hesse
@ 2020-07-29 20:20   ` John Paul Adrian Glaubitz
  2020-07-29 21:20     ` Dimitri John Ledkov
  0 siblings, 1 reply; 34+ messages in thread
From: John Paul Adrian Glaubitz @ 2020-07-29 20:20 UTC (permalink / raw)
  To: The development of GNU GRUB, Christian Hesse, Daniel Kiper

On 7/29/20 10:12 PM, Christian Hesse wrote:
> This does not apply on top of grub 2.04. Will downstream maintainers have to
> do their cherry-picking on its own or will a maintenance branch on top of
> grub-2.04 (or what ever) be available?
> I would like to push updates to the Arch Linux repositories.

You may want to look at the Debian package which already has the patches
applied in Debian unstable [1].

I'm surprised that Arch did not receive a disclosure of the vulnerabilities
under NDA since Debian and the various enterprise distributions have it
already.

Adrian

> [1] https://salsa.debian.org/grub-team/grub/-/tree/master/debian/patches

-- 
 .''`.  John Paul Adrian Glaubitz
: :' :  Debian Developer - glaubitz@debian.org
`. `'   Freie Universitaet Berlin - glaubitz@physik.fu-berlin.de
  `-    GPG: 62FF 8A75 84E0 2956 9546  0006 7426 3B37 F5B5 F913


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

* Re: [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole
  2020-07-29 20:20   ` John Paul Adrian Glaubitz
@ 2020-07-29 21:20     ` Dimitri John Ledkov
  2020-07-29 21:33       ` John Paul Adrian Glaubitz
  0 siblings, 1 reply; 34+ messages in thread
From: Dimitri John Ledkov @ 2020-07-29 21:20 UTC (permalink / raw)
  To: The development of GNU GRUB; +Cc: Christian Hesse, Daniel Kiper

On Wed, 29 Jul 2020 at 21:20, John Paul Adrian Glaubitz
<glaubitz@physik.fu-berlin.de> wrote:
>
> On 7/29/20 10:12 PM, Christian Hesse wrote:
> > This does not apply on top of grub 2.04. Will downstream maintainers have to
> > do their cherry-picking on its own or will a maintenance branch on top of
> > grub-2.04 (or what ever) be available?
> > I would like to push updates to the Arch Linux repositories.
>
> You may want to look at the Debian package which already has the patches
> applied in Debian unstable [1].
>
> I'm surprised that Arch did not receive a disclosure of the vulnerabilities
> under NDA since Debian and the various enterprise distributions have it
> already.

Disclosures were done to a subset of binary distributions that have a
trust path to shims signed with Microsoft UEFI CA 2011 db key. Arch
Linux does not provide shim-signed with keys controlled by Arch Linux
and it doesn't provide pre-signed secureboot kernels.

Reading Arch Linux documentation it seems that Fedora's shim is used
together with self-signed Mok Keys.

Mitigation strategy for Arch Linux will then be quite different to
everyone else:

1) Update to new shim from fedora when available, as previous ones are
going to be revoked by the dbxupdate from uefi.org
2) Patch Archlinux grub
3) Patch Archilinux kernel for lockdown bypass
4) Generate new MOK key, enroll it into MOK
5) Sign patched grub/kernel with the new MOK key
6) Provide instructions for users to revoke their old key via MOKX,
i.e. use mokutil --mokx --import existing cert; or for example delete
the old key from MOK with --delete old-cert.der

This is just a rough guideline, please analyze how signing keys are
controlled and used on typical Arch Linux deployment and adjust things
to taste.

The key point is to rotate the signing key used for
shim/grub/kernel/fwupd, only use the new key to sign fixed things, and
ensure that old key is no longer trusted (removed from MOK, or added
to MOKX).

-- 
Regards,

Dimitri.


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

* Re: [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole
  2020-07-29 21:20     ` Dimitri John Ledkov
@ 2020-07-29 21:33       ` John Paul Adrian Glaubitz
  0 siblings, 0 replies; 34+ messages in thread
From: John Paul Adrian Glaubitz @ 2020-07-29 21:33 UTC (permalink / raw)
  To: The development of GNU GRUB, Dimitri John Ledkov
  Cc: Christian Hesse, Daniel Kiper

Hi Dimitri!

On 7/29/20 11:20 PM, Dimitri John Ledkov wrote:
> Disclosures were done to a subset of binary distributions that have a
> trust path to shims signed with Microsoft UEFI CA 2011 db key. Arch
> Linux does not provide shim-signed with keys controlled by Arch Linux
> and it doesn't provide pre-signed secureboot kernels.
> 
> Reading Arch Linux documentation it seems that Fedora's shim is used
> together with self-signed Mok Keys.
> 
> Mitigation strategy for Arch Linux will then be quite different to
> everyone else:
> 
> 1) Update to new shim from fedora when available, as previous ones are
> going to be revoked by the dbxupdate from uefi.org
> 2) Patch Archlinux grub
> 3) Patch Archilinux kernel for lockdown bypass
> 4) Generate new MOK key, enroll it into MOK
> 5) Sign patched grub/kernel with the new MOK key
> 6) Provide instructions for users to revoke their old key via MOKX,
> i.e. use mokutil --mokx --import existing cert; or for example delete
> the old key from MOK with --delete old-cert.der
> 
> This is just a rough guideline, please analyze how signing keys are
> controlled and used on typical Arch Linux deployment and adjust things
> to taste.
> 
> The key point is to rotate the signing key used for
> shim/grub/kernel/fwupd, only use the new key to sign fixed things, and
> ensure that old key is no longer trusted (removed from MOK, or added
> to MOKX).

Thanks for describing the detailed procedure, very informative.

Adrian

-- 
 .''`.  John Paul Adrian Glaubitz
: :' :  Debian Developer - glaubitz@debian.org
`. `'   Freie Universitaet Berlin - glaubitz@physik.fu-berlin.de
  `-    GPG: 62FF 8A75 84E0 2956 9546  0006 7426 3B37 F5B5 F913


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

* Re: [SECURITY PATCH 05/28] malloc: Use overflow checking primitives where we do complex allocations
  2020-07-29 17:00 ` [SECURITY PATCH 05/28] malloc: Use overflow checking primitives where we do complex allocations Daniel Kiper
@ 2021-09-10 16:10   ` Glenn Washburn
  0 siblings, 0 replies; 34+ messages in thread
From: Glenn Washburn @ 2021-09-10 16:10 UTC (permalink / raw)
  To: Daniel Kiper
  Cc: The development of GNU GRUB, 93sam, alexander.burmashev,
	amakhalov, chris.coulson, cjwatson, cperry, darren.kenny,
	darren.moffat, dave.miner, degranit, eric.snowberg, ilya.okomin,
	jan.setjeeilers, jerecox, jesse, john.haxby, kanth.ghatraju,
	konrad.wilk, mbenatto, mickey, msrc57813grub, phcoder, pjones,
	sajacobu, todd.vierling, xnox

On Wed, 29 Jul 2020 19:00:18 +0200
Daniel Kiper <daniel.kiper@oracle.com> wrote:

> From: Peter Jones <pjones@redhat.com>
> 
> This attempts to fix the places where we do the following where
> arithmetic_expr may include unvalidated data:
> 
>   X = grub_malloc(arithmetic_expr);
> 
> It accomplishes this by doing the arithmetic ahead of time using grub_add(),
> grub_sub(), grub_mul() and testing for overflow before proceeding.

...

> diff --git a/grub-core/fs/udf.c b/grub-core/fs/udf.c
> index a83761674..21ac7f446 100644
> --- a/grub-core/fs/udf.c
> +++ b/grub-core/fs/udf.c
> @@ -28,6 +28,7 @@
>  #include <grub/charset.h>
>  #include <grub/datetime.h>
>  #include <grub/udf.h>
> +#include <grub/safemath.h>
>  
>  GRUB_MOD_LICENSE ("GPLv3+");
>  
> @@ -890,9 +891,19 @@ read_string (const grub_uint8_t *raw, grub_size_t sz, char *outbuf)
>  	utf16[i] = (raw[2 * i + 1] << 8) | raw[2*i + 2];
>      }
>    if (!outbuf)
> -    outbuf = grub_malloc (utf16len * GRUB_MAX_UTF8_PER_UTF16 + 1);
> +    {
> +      grub_size_t size;
> +
> +      if (grub_mul (utf16len, GRUB_MAX_UTF8_PER_UTF16, &size) ||
> +	  grub_add (size, 1, &size))
> +	goto fail;
> +
> +      outbuf = grub_malloc (size);
> +    }
>    if (outbuf)
>      *grub_utf16_to_utf8 ((grub_uint8_t *) outbuf, utf16, utf16len) = '\0';
> +
> + fail:
>    grub_free (utf16);
>    return outbuf;
>  }
> @@ -1005,7 +1016,7 @@ grub_udf_read_symlink (grub_fshelp_node_t node)
>    grub_size_t sz = U64 (node->block.fe.file_size);
>    grub_uint8_t *raw;
>    const grub_uint8_t *ptr;
> -  char *out, *optr;
> +  char *out = NULL, *optr;
>  
>    if (sz < 4)
>      return NULL;
> @@ -1013,14 +1024,16 @@ grub_udf_read_symlink (grub_fshelp_node_t node)
>    if (!raw)
>      return NULL;
>    if (grub_udf_read_file (node, NULL, NULL, 0, sz, (char *) raw) < 0)
> -    {
> -      grub_free (raw);
> -      return NULL;
> -    }
> +    goto fail_1;
>  
> -  out = grub_malloc (sz * 2 + 1);
> +  if (grub_mul (sz, 2, &sz) ||
> +      grub_add (sz, 1, &sz))

The old code did not change the value of sz, but the new code does.
This is a problem because sz is used further down. This is causing udf
symlinks to not be accessible via grub and is causing the udf tests to
fail on the symlink test.

> +    goto fail_0;
> +
> +  out = grub_malloc (sz);
>    if (!out)
>      {
> + fail_0:
>        grub_free (raw);
>        return NULL;
>      }
> @@ -1031,17 +1044,17 @@ grub_udf_read_symlink (grub_fshelp_node_t node)
>      {
>        grub_size_t s;
>        if ((grub_size_t) (ptr - raw + 4) > sz)
> -	goto fail;
> +	goto fail_1;
>        if (!(ptr[2] == 0 && ptr[3] == 0))
> -	goto fail;
> +	goto fail_1;
>        s = 4 + ptr[1];
>        if ((grub_size_t) (ptr - raw + s) > sz)
> -	goto fail;
> +	goto fail_1;
>        switch (*ptr)
>  	{
>  	case 1:
>  	  if (ptr[1])
> -	    goto fail;
> +	    goto fail_1;
>  	  /* Fallthrough.  */
>  	case 2:
>  	  /* in 4 bytes. out: 1 byte.  */
> @@ -1066,11 +1079,11 @@ grub_udf_read_symlink (grub_fshelp_node_t node)
>  	  if (optr != out)
>  	    *optr++ = '/';
>  	  if (!read_string (ptr + 4, s - 4, optr))
> -	    goto fail;
> +	    goto fail_1;
>  	  optr += grub_strlen (optr);
>  	  break;
>  	default:
> -	  goto fail;
> +	  goto fail_1;
>  	}
>        ptr += s;
>      }
> @@ -1078,7 +1091,7 @@ grub_udf_read_symlink (grub_fshelp_node_t node)
>    grub_free (raw);
>    return out;
>  
> - fail:
> + fail_1:
>    grub_free (raw);
>    grub_free (out);
>    grub_error (GRUB_ERR_BAD_FS, "invalid symlink");

I actually noticed in my CI tests that the UDF tests were failing before
the 2.06 release, but I assumed it was something long standing and
didn't look into what was causing it. Here's another example of how my
CI and testing improvements could've made a more solid release.

Glenn


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

end of thread, other threads:[~2021-09-10 16:10 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-29 17:00 [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 01/28] yylex: Make lexer fatal errors actually be fatal Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 02/28] safemath: Add some arithmetic primitives that check for overflow Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 03/28] calloc: Make sure we always have an overflow-checking calloc() available Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 04/28] calloc: Use calloc() at most places Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 05/28] malloc: Use overflow checking primitives where we do complex allocations Daniel Kiper
2021-09-10 16:10   ` Glenn Washburn
2020-07-29 17:00 ` [SECURITY PATCH 06/28] iso9660: Don't leak memory on realloc() failures Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 07/28] font: Do not load more than one NAME section Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 08/28] gfxmenu: Fix double free in load_image() Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 09/28] xnu: Fix double free in grub_xnu_devprop_add_property() Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 10/28] json: Avoid a double-free when parsing fails Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 11/28] lzma: Make sure we don't dereference past array Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 12/28] term: Fix overflow on user inputs Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 13/28] udf: Fix memory leak Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 14/28] multiboot2: Fix memory leak if grub_create_loader_cmdline() fails Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 15/28] tftp: Do not use priority queue Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 16/28] relocator: Protect grub_relocator_alloc_chunk_addr() input args against integer underflow/overflow Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 17/28] relocator: Protect grub_relocator_alloc_chunk_align() max_addr against integer underflow Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 18/28] script: Remove unused fields from grub_script_function struct Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 19/28] script: Avoid a use-after-free when redefining a function during execution Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 20/28] relocator: Fix grub_relocator_alloc_chunk_align() top memory allocation Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 21/28] hfsplus: Fix two more overflows Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 22/28] lvm: Fix two more potential data-dependent alloc overflows Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 23/28] emu: Make grub_free(NULL) safe Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 24/28] efi: Fix some malformed device path arithmetic errors Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 25/28] efi/chainloader: Propagate errors from copy_file_path() Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 26/28] efi: Fix use-after-free in halt/reboot path Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 27/28] loader/linux: Avoid overflow on initrd size calculation Daniel Kiper
2020-07-29 17:00 ` [SECURITY PATCH 28/28] linux: Fix integer overflows in initrd size handling Daniel Kiper
2020-07-29 20:12 ` [SECURITY PATCH 00/28] Multiple GRUB2 vulnerabilities - BootHole Christian Hesse
2020-07-29 20:20   ` John Paul Adrian Glaubitz
2020-07-29 21:20     ` Dimitri John Ledkov
2020-07-29 21:33       ` John Paul Adrian Glaubitz

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.