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

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