Blame SOURCES/0296-relocator-Protect-grub_relocator_alloc_chunk_align-m.patch

80913e
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
80913e
From: Alexey Makhalov <amakhalov@vmware.com>
80913e
Date: Wed, 8 Jul 2020 01:44:38 +0000
80913e
Subject: [PATCH] relocator: Protect grub_relocator_alloc_chunk_align()
80913e
 max_addr against integer underflow
80913e
80913e
This commit introduces integer underflow mitigation in max_addr calculation
80913e
in grub_relocator_alloc_chunk_align() invocation.
80913e
80913e
It consists of 2 fixes:
80913e
  1. Introduced grub_relocator_alloc_chunk_align_safe() wrapper function to perform
80913e
     sanity check for min/max and size values, and to make safe invocation of
80913e
     grub_relocator_alloc_chunk_align() with validated max_addr value. Replace all
80913e
     invocations such as grub_relocator_alloc_chunk_align(..., min_addr, max_addr - size, size, ...)
80913e
     by grub_relocator_alloc_chunk_align_safe(..., min_addr, max_addr, size, ...).
80913e
  2. Introduced UP_TO_TOP32(s) macro for the cases where max_addr is 32-bit top
80913e
     address (0xffffffff - size + 1) or similar.
80913e
80913e
Signed-off-by: Alexey Makhalov <amakhalov@vmware.com>
80913e
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
80913e
Upstream-commit-id: 10498c8ba17
80913e
---
80913e
 grub-core/lib/i386/relocator.c        | 28 +++++++++++-----------------
80913e
 grub-core/lib/mips/relocator.c        |  6 ++----
80913e
 grub-core/lib/powerpc/relocator.c     |  6 ++----
80913e
 grub-core/lib/x86_64/efi/relocator.c  |  7 +++----
80913e
 grub-core/loader/i386/linux.c         |  5 ++---
80913e
 grub-core/loader/i386/multiboot_mbi.c |  7 +++----
80913e
 grub-core/loader/i386/pc/linux.c      |  6 ++----
80913e
 grub-core/loader/mips/linux.c         |  9 +++------
80913e
 grub-core/loader/multiboot.c          |  2 +-
80913e
 grub-core/loader/multiboot_elfxx.c    | 10 +++++-----
80913e
 grub-core/loader/multiboot_mbi2.c     | 10 +++++-----
80913e
 grub-core/loader/xnu_resume.c         |  2 +-
80913e
 include/grub/relocator.h              | 29 +++++++++++++++++++++++++++++
80913e
 13 files changed, 69 insertions(+), 58 deletions(-)
80913e
80913e
diff --git a/grub-core/lib/i386/relocator.c b/grub-core/lib/i386/relocator.c
b32e65
index 71dd4f0ab..34cbe834f 100644
80913e
--- a/grub-core/lib/i386/relocator.c
80913e
+++ b/grub-core/lib/i386/relocator.c
80913e
@@ -83,11 +83,10 @@ grub_relocator32_boot (struct grub_relocator *rel,
80913e
   /* Specific memory range due to Global Descriptor Table for use by payload
80913e
      that we will store in returned chunk.  The address range and preference
80913e
      are based on "THE LINUX/x86 BOOT PROTOCOL" specification.  */
80913e
-  err = grub_relocator_alloc_chunk_align (rel, &ch, 0x1000,
80913e
-					  0x9a000 - RELOCATOR_SIZEOF (32),
80913e
-					  RELOCATOR_SIZEOF (32), 16,
80913e
-					  GRUB_RELOCATOR_PREFERENCE_LOW,
80913e
-					  avoid_efi_bootservices);
80913e
+  err = grub_relocator_alloc_chunk_align_safe (rel, &ch, 0x1000, 0x9a000,
80913e
+					       RELOCATOR_SIZEOF (32), 16,
80913e
+					       GRUB_RELOCATOR_PREFERENCE_LOW,
80913e
+					       avoid_efi_bootservices);
80913e
   if (err)
80913e
     return err;
80913e
 
80913e
@@ -125,13 +124,10 @@ grub_relocator16_boot (struct grub_relocator *rel,
80913e
   grub_relocator_chunk_t ch;
80913e
 
80913e
   /* Put it higher than the byte it checks for A20 check.  */
80913e
-  err = grub_relocator_alloc_chunk_align (rel, &ch, 0x8010,
80913e
-					  0xa0000 - RELOCATOR_SIZEOF (16)
80913e
-					  - GRUB_RELOCATOR16_STACK_SIZE,
80913e
-					  RELOCATOR_SIZEOF (16)
80913e
-					  + GRUB_RELOCATOR16_STACK_SIZE, 16,
80913e
-					  GRUB_RELOCATOR_PREFERENCE_NONE,
80913e
-					  0);
80913e
+  err = grub_relocator_alloc_chunk_align_safe (rel, &ch, 0x8010, 0xa0000,
80913e
+					       RELOCATOR_SIZEOF (16) +
80913e
+					       GRUB_RELOCATOR16_STACK_SIZE, 16,
80913e
+					       GRUB_RELOCATOR_PREFERENCE_NONE, 0);
80913e
   if (err)
80913e
     return err;
80913e
 
80913e
@@ -183,11 +179,9 @@ grub_relocator64_boot (struct grub_relocator *rel,
80913e
   void *relst;
80913e
   grub_relocator_chunk_t ch;
80913e
 
80913e
-  err = grub_relocator_alloc_chunk_align (rel, &ch, min_addr,
80913e
-					  max_addr - RELOCATOR_SIZEOF (64),
80913e
-					  RELOCATOR_SIZEOF (64), 16,
80913e
-					  GRUB_RELOCATOR_PREFERENCE_NONE,
80913e
-					  0);
80913e
+  err = grub_relocator_alloc_chunk_align_safe (rel, &ch, min_addr, max_addr,
80913e
+					       RELOCATOR_SIZEOF (64), 16,
80913e
+					       GRUB_RELOCATOR_PREFERENCE_NONE, 0);
80913e
   if (err)
80913e
     return err;
80913e
 
80913e
diff --git a/grub-core/lib/mips/relocator.c b/grub-core/lib/mips/relocator.c
b32e65
index 9d5f49cb9..743b213e6 100644
80913e
--- a/grub-core/lib/mips/relocator.c
80913e
+++ b/grub-core/lib/mips/relocator.c
80913e
@@ -120,10 +120,8 @@ grub_relocator32_boot (struct grub_relocator *rel,
80913e
   unsigned i;
80913e
   grub_addr_t vtarget;
80913e
 
80913e
-  err = grub_relocator_alloc_chunk_align (rel, &ch, 0,
80913e
-					  (0xffffffff - stateset_size)
80913e
-					  + 1, stateset_size,
80913e
-					  sizeof (grub_uint32_t),
80913e
+  err = grub_relocator_alloc_chunk_align (rel, &ch, 0, UP_TO_TOP32 (stateset_size),
80913e
+					  stateset_size, sizeof (grub_uint32_t),
80913e
 					  GRUB_RELOCATOR_PREFERENCE_NONE, 0);
80913e
   if (err)
80913e
     return err;
80913e
diff --git a/grub-core/lib/powerpc/relocator.c b/grub-core/lib/powerpc/relocator.c
b32e65
index bdf2b111b..8ffb8b686 100644
80913e
--- a/grub-core/lib/powerpc/relocator.c
80913e
+++ b/grub-core/lib/powerpc/relocator.c
80913e
@@ -115,10 +115,8 @@ grub_relocator32_boot (struct grub_relocator *rel,
80913e
   unsigned i;
80913e
   grub_relocator_chunk_t ch;
80913e
 
80913e
-  err = grub_relocator_alloc_chunk_align (rel, &ch, 0,
80913e
-					  (0xffffffff - stateset_size)
80913e
-					  + 1, stateset_size,
80913e
-					  sizeof (grub_uint32_t),
80913e
+  err = grub_relocator_alloc_chunk_align (rel, &ch, 0, UP_TO_TOP32 (stateset_size),
80913e
+					  stateset_size, sizeof (grub_uint32_t),
80913e
 					  GRUB_RELOCATOR_PREFERENCE_NONE, 0);
80913e
   if (err)
80913e
     return err;
80913e
diff --git a/grub-core/lib/x86_64/efi/relocator.c b/grub-core/lib/x86_64/efi/relocator.c
b32e65
index 3caef7a40..7d200a125 100644
80913e
--- a/grub-core/lib/x86_64/efi/relocator.c
80913e
+++ b/grub-core/lib/x86_64/efi/relocator.c
80913e
@@ -50,10 +50,9 @@ grub_relocator64_efi_boot (struct grub_relocator *rel,
80913e
    * 64-bit relocator code may live above 4 GiB quite well.
80913e
    * However, I do not want ask for problems. Just in case.
80913e
    */
80913e
-  err = grub_relocator_alloc_chunk_align (rel, &ch, 0,
80913e
-					  0x100000000 - RELOCATOR_SIZEOF (64_efi),
80913e
-					  RELOCATOR_SIZEOF (64_efi), 16,
80913e
-					  GRUB_RELOCATOR_PREFERENCE_NONE, 1);
80913e
+  err = grub_relocator_alloc_chunk_align_safe (rel, &ch, 0, 0x100000000,
80913e
+					       RELOCATOR_SIZEOF (64_efi), 16,
80913e
+					       GRUB_RELOCATOR_PREFERENCE_NONE, 1);
80913e
   if (err)
80913e
     return err;
80913e
 
80913e
diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
b32e65
index b4a30f607..191f1631e 100644
80913e
--- a/grub-core/loader/i386/linux.c
80913e
+++ b/grub-core/loader/i386/linux.c
80913e
@@ -231,9 +231,8 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align,
80913e
 	for (; err && *align + 1 > min_align; (*align)--)
80913e
 	  {
80913e
 	    grub_errno = GRUB_ERR_NONE;
80913e
-	    err = grub_relocator_alloc_chunk_align (relocator, &ch,
80913e
-						    0x1000000,
80913e
-						    0xffffffff & ~prot_size,
80913e
+	    err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000000,
80913e
+						    UP_TO_TOP32 (prot_size),
80913e
 						    prot_size, 1 << *align,
80913e
 						    GRUB_RELOCATOR_PREFERENCE_LOW,
80913e
 						    1);
80913e
diff --git a/grub-core/loader/i386/multiboot_mbi.c b/grub-core/loader/i386/multiboot_mbi.c
b32e65
index ca85358f7..9d3466d6a 100644
80913e
--- a/grub-core/loader/i386/multiboot_mbi.c
80913e
+++ b/grub-core/loader/i386/multiboot_mbi.c
80913e
@@ -470,10 +470,9 @@ grub_multiboot_make_mbi (grub_uint32_t *target)
80913e
 
80913e
   bufsize = grub_multiboot_get_mbi_size ();
80913e
 
80913e
-  err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch,
80913e
-					  0x10000, 0xa0000 - bufsize,
80913e
-					  bufsize, 4,
80913e
-					  GRUB_RELOCATOR_PREFERENCE_NONE, 0);
80913e
+  err = grub_relocator_alloc_chunk_align_safe (grub_multiboot_relocator, &ch,
80913e
+					       0x10000, 0xa0000, bufsize, 4,
80913e
+					       GRUB_RELOCATOR_PREFERENCE_NONE, 0);
80913e
   if (err)
80913e
     return err;
80913e
   ptrorig = get_virtual_current_address (ch);
80913e
diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c
b32e65
index 540891371..63736fae9 100644
80913e
--- a/grub-core/loader/i386/pc/linux.c
80913e
+++ b/grub-core/loader/i386/pc/linux.c
80913e
@@ -460,10 +460,8 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
80913e
 
80913e
   {
80913e
     grub_relocator_chunk_t ch;
80913e
-    err = grub_relocator_alloc_chunk_align (relocator, &ch,
80913e
-					    addr_min, addr_max - size,
80913e
-					    size, 0x1000,
80913e
-					    GRUB_RELOCATOR_PREFERENCE_HIGH, 0);
80913e
+    err = grub_relocator_alloc_chunk_align_safe (relocator, &ch, addr_min, addr_max, size,
80913e
+						 0x1000, GRUB_RELOCATOR_PREFERENCE_HIGH, 0);
80913e
     if (err)
80913e
       return err;
80913e
     initrd_chunk = get_virtual_current_address (ch);
80913e
diff --git a/grub-core/loader/mips/linux.c b/grub-core/loader/mips/linux.c
b32e65
index 5f383be3d..27c1db84a 100644
80913e
--- a/grub-core/loader/mips/linux.c
80913e
+++ b/grub-core/loader/mips/linux.c
80913e
@@ -434,12 +434,9 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
80913e
   {
80913e
     grub_relocator_chunk_t ch;
80913e
 
80913e
-    err = grub_relocator_alloc_chunk_align (relocator, &ch,
80913e
-					    (target_addr & 0x1fffffff)
80913e
-					    + linux_size + 0x10000,
80913e
-					    (0x10000000 - size),
80913e
-					    size, 0x10000,
80913e
-					    GRUB_RELOCATOR_PREFERENCE_NONE, 0);
80913e
+    err = grub_relocator_alloc_chunk_align_safe (relocator, &ch, (target_addr & 0x1fffffff) +
80913e
+						 linux_size + 0x10000, 0x10000000, size,
80913e
+						 0x10000, GRUB_RELOCATOR_PREFERENCE_NONE, 0);
80913e
 
80913e
     if (err)
80913e
       goto fail;
80913e
diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c
b32e65
index 9a8dae556..f455e8039 100644
80913e
--- a/grub-core/loader/multiboot.c
80913e
+++ b/grub-core/loader/multiboot.c
80913e
@@ -407,7 +407,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)),
80913e
   {
80913e
     grub_relocator_chunk_t ch;
80913e
     err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch,
80913e
-					    lowest_addr, (0xffffffff - size) + 1,
80913e
+					    lowest_addr, UP_TO_TOP32 (size),
80913e
 					    size, MULTIBOOT_MOD_ALIGN,
80913e
 					    GRUB_RELOCATOR_PREFERENCE_NONE, 1);
80913e
     if (err)
80913e
diff --git a/grub-core/loader/multiboot_elfxx.c b/grub-core/loader/multiboot_elfxx.c
b32e65
index cc6853692..f2318e0d1 100644
80913e
--- a/grub-core/loader/multiboot_elfxx.c
80913e
+++ b/grub-core/loader/multiboot_elfxx.c
80913e
@@ -109,10 +109,10 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
80913e
       if (load_size > mld->max_addr || mld->min_addr > mld->max_addr - load_size)
80913e
 	return grub_error (GRUB_ERR_BAD_OS, "invalid min/max address and/or load size");
80913e
 
80913e
-      err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch,
80913e
-					      mld->min_addr, mld->max_addr - load_size,
80913e
-					      load_size, mld->align ? mld->align : 1,
80913e
-					      mld->preference, mld->avoid_efi_boot_services);
80913e
+      err = grub_relocator_alloc_chunk_align_safe (GRUB_MULTIBOOT (relocator), &ch,
80913e
+						   mld->min_addr, mld->max_addr,
80913e
+						   load_size, mld->align ? mld->align : 1,
80913e
+						   mld->preference, mld->avoid_efi_boot_services);
80913e
 
80913e
       if (err)
80913e
         {
80913e
@@ -256,7 +256,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
80913e
 	    continue;
80913e
 
80913e
 	  err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch, 0,
80913e
-						  (0xffffffff - sh->sh_size) + 1,
80913e
+						  UP_TO_TOP32 (sh->sh_size),
80913e
 						  sh->sh_size, sh->sh_addralign,
80913e
 						  GRUB_RELOCATOR_PREFERENCE_NONE,
80913e
 						  mld->avoid_efi_boot_services);
80913e
diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c
b32e65
index 872dcd42e..3cfb47650 100644
80913e
--- a/grub-core/loader/multiboot_mbi2.c
80913e
+++ b/grub-core/loader/multiboot_mbi2.c
80913e
@@ -298,10 +298,10 @@ grub_multiboot2_load (grub_file_t file, const char *filename)
80913e
 	      return grub_error (GRUB_ERR_BAD_OS, "invalid min/max address and/or load size");
80913e
 	    }
80913e
 
80913e
-	  err = grub_relocator_alloc_chunk_align (grub_multiboot2_relocator, &ch,
80913e
-						  mld.min_addr, mld.max_addr - code_size,
80913e
-						  code_size, mld.align ? mld.align : 1,
80913e
-						  mld.preference, keep_bs);
80913e
+	  err = grub_relocator_alloc_chunk_align_safe (grub_multiboot2_relocator, &ch,
80913e
+						       mld.min_addr, mld.max_addr,
80913e
+						       code_size, mld.align ? mld.align : 1,
80913e
+						       mld.preference, keep_bs);
80913e
 	}
80913e
       else
80913e
 	err = grub_relocator_alloc_chunk_addr (grub_multiboot2_relocator,
80913e
@@ -747,7 +747,7 @@ grub_multiboot2_make_mbi (grub_uint32_t *target)
80913e
   COMPILE_TIME_ASSERT (MULTIBOOT_TAG_ALIGN % sizeof (grub_properly_aligned_t) == 0);
80913e
 
80913e
   err = grub_relocator_alloc_chunk_align (grub_multiboot2_relocator, &ch,
80913e
-					  0, 0xffffffff - bufsize,
80913e
+					  0, UP_TO_TOP32 (bufsize),
80913e
 					  bufsize, MULTIBOOT_TAG_ALIGN,
80913e
 					  GRUB_RELOCATOR_PREFERENCE_NONE, 1);
80913e
   if (err)
80913e
diff --git a/grub-core/loader/xnu_resume.c b/grub-core/loader/xnu_resume.c
b32e65
index 534a74438..99119558d 100644
80913e
--- a/grub-core/loader/xnu_resume.c
80913e
+++ b/grub-core/loader/xnu_resume.c
80913e
@@ -129,7 +129,7 @@ grub_xnu_resume (char *imagename)
80913e
   {
80913e
     grub_relocator_chunk_t ch;
80913e
     err = grub_relocator_alloc_chunk_align (grub_xnu_relocator, &ch, 0,
80913e
-					    (0xffffffff - hibhead.image_size) + 1,
80913e
+					    UP_TO_TOP32 (hibhead.image_size),
80913e
 					    hibhead.image_size,
80913e
 					    GRUB_XNU_PAGESIZE,
80913e
 					    GRUB_RELOCATOR_PREFERENCE_NONE, 0);
80913e
diff --git a/include/grub/relocator.h b/include/grub/relocator.h
b32e65
index 24d8672d2..1b3bdd92a 100644
80913e
--- a/include/grub/relocator.h
80913e
+++ b/include/grub/relocator.h
80913e
@@ -49,6 +49,35 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel,
80913e
 				  int preference,
80913e
 				  int avoid_efi_boot_services);
80913e
 
80913e
+/*
80913e
+ * Wrapper for grub_relocator_alloc_chunk_align() with purpose of
80913e
+ * protecting against integer underflow.
80913e
+ *
80913e
+ * Compare to its callee, max_addr has different meaning here.
80913e
+ * It covers entire chunk and not just start address of the chunk.
80913e
+ */
80913e
+static inline grub_err_t
80913e
+grub_relocator_alloc_chunk_align_safe (struct grub_relocator *rel,
80913e
+				       grub_relocator_chunk_t *out,
80913e
+				       grub_phys_addr_t min_addr,
80913e
+				       grub_phys_addr_t max_addr,
80913e
+				       grub_size_t size, grub_size_t align,
80913e
+				       int preference,
80913e
+				       int avoid_efi_boot_services)
80913e
+{
80913e
+  /* Sanity check and ensure following equation (max_addr - size) is safe. */
80913e
+  if (max_addr < size || (max_addr - size) < min_addr)
80913e
+    return GRUB_ERR_OUT_OF_RANGE;
80913e
+
80913e
+  return grub_relocator_alloc_chunk_align (rel, out, min_addr,
80913e
+					   max_addr - size,
80913e
+					   size, align, preference,
80913e
+					   avoid_efi_boot_services);
80913e
+}
80913e
+
80913e
+/* Top 32-bit address minus s bytes and plus 1 byte. */
80913e
+#define UP_TO_TOP32(s)	((~(s) & 0xffffffff) + 1)
80913e
+
80913e
 #define GRUB_RELOCATOR_PREFERENCE_NONE 0
80913e
 #define GRUB_RELOCATOR_PREFERENCE_LOW 1
80913e
 #define GRUB_RELOCATOR_PREFERENCE_HIGH 2