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

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