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
c294fc
index 71dd4f0ab0c..34cbe834fa3 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
c294fc
index 9d5f49cb93a..743b213e695 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
c294fc
index bdf2b111be7..8ffb8b68683 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
c294fc
index 3caef7a4021..7d200a125ee 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
c294fc
index b4a30f607fa..191f1631e88 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
c294fc
index ca85358f771..9d3466d6ace 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
c294fc
index 540891371f9..63736fae950 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
c294fc
index 5f383be3d07..27c1db84a44 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
c294fc
index 9a8dae5565b..f455e803910 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
c294fc
index cc6853692a8..f2318e0d165 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
c294fc
index 872dcd42e97..3cfb47650a0 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
c294fc
index 534a74438b2..99119558d21 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
c294fc
index 24d8672d22c..1b3bdd92ac6 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