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

c4e390
From 940b7872052d8539c7699e6323fda9467c87f67c Mon Sep 17 00:00:00 2001
c4e390
From: Alexey Makhalov <amakhalov@vmware.com>
c4e390
Date: Wed, 8 Jul 2020 01:44:38 +0000
c4e390
Subject: [PATCH 314/336] relocator: Protect grub_relocator_alloc_chunk_align()
c4e390
 max_addr against integer underflow
c4e390
c4e390
This commit introduces integer underflow mitigation in max_addr calculation
c4e390
in grub_relocator_alloc_chunk_align() invocation.
c4e390
c4e390
It consists of 2 fixes:
c4e390
  1. Introduced grub_relocator_alloc_chunk_align_safe() wrapper function to perform
c4e390
     sanity check for min/max and size values, and to make safe invocation of
c4e390
     grub_relocator_alloc_chunk_align() with validated max_addr value. Replace all
c4e390
     invocations such as grub_relocator_alloc_chunk_align(..., min_addr, max_addr - size, size, ...)
c4e390
     by grub_relocator_alloc_chunk_align_safe(..., min_addr, max_addr, size, ...).
c4e390
  2. Introduced UP_TO_TOP32(s) macro for the cases where max_addr is 32-bit top
c4e390
     address (0xffffffff - size + 1) or similar.
c4e390
c4e390
Signed-off-by: Alexey Makhalov <amakhalov@vmware.com>
c4e390
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
c4e390
Upstream-commit-id: 10498c8ba17
c4e390
---
c4e390
 grub-core/lib/i386/relocator.c        | 28 ++++++++++----------------
c4e390
 grub-core/lib/mips/relocator.c        |  6 ++----
c4e390
 grub-core/lib/powerpc/relocator.c     |  6 ++----
c4e390
 grub-core/loader/i386/linux.c         |  5 ++---
c4e390
 grub-core/loader/i386/multiboot_mbi.c |  7 +++----
c4e390
 grub-core/loader/i386/pc/linux.c      |  6 ++----
c4e390
 grub-core/loader/mips/linux.c         |  9 +++------
c4e390
 grub-core/loader/multiboot.c          |  2 +-
c4e390
 grub-core/loader/multiboot_elfxx.c    |  5 ++---
c4e390
 grub-core/loader/multiboot_mbi2.c     | 11 ++++++----
c4e390
 grub-core/loader/xnu_resume.c         |  2 +-
c4e390
 include/grub/relocator.h              | 29 +++++++++++++++++++++++++++
c4e390
 12 files changed, 65 insertions(+), 51 deletions(-)
c4e390
c4e390
diff --git a/grub-core/lib/i386/relocator.c b/grub-core/lib/i386/relocator.c
c4e390
index ffaf25f088d..08f6821f20c 100644
c4e390
--- a/grub-core/lib/i386/relocator.c
c4e390
+++ b/grub-core/lib/i386/relocator.c
c4e390
@@ -84,11 +84,10 @@ grub_relocator32_boot (struct grub_relocator *rel,
c4e390
   /* Specific memory range due to Global Descriptor Table for use by payload
c4e390
      that we will store in returned chunk.  The address range and preference
c4e390
      are based on "THE LINUX/x86 BOOT PROTOCOL" specification.  */
c4e390
-  err = grub_relocator_alloc_chunk_align (rel, &ch, 0x1000,
c4e390
-					  0x9a000 - RELOCATOR_SIZEOF (32),
c4e390
-					  RELOCATOR_SIZEOF (32), 16,
c4e390
-					  GRUB_RELOCATOR_PREFERENCE_LOW,
c4e390
-					  avoid_efi_bootservices);
c4e390
+  err = grub_relocator_alloc_chunk_align_safe (rel, &ch, 0x1000, 0x9a000,
c4e390
+					       RELOCATOR_SIZEOF (32), 16,
c4e390
+					       GRUB_RELOCATOR_PREFERENCE_LOW,
c4e390
+					       avoid_efi_bootservices);
c4e390
   if (err)
c4e390
     return err;
c4e390
 
c4e390
@@ -126,13 +125,10 @@ grub_relocator16_boot (struct grub_relocator *rel,
c4e390
   grub_relocator_chunk_t ch;
c4e390
 
c4e390
   /* Put it higher than the byte it checks for A20 check.  */
c4e390
-  err = grub_relocator_alloc_chunk_align (rel, &ch, 0x8010,
c4e390
-					  0xa0000 - RELOCATOR_SIZEOF (16)
c4e390
-					  - GRUB_RELOCATOR16_STACK_SIZE,
c4e390
-					  RELOCATOR_SIZEOF (16)
c4e390
-					  + GRUB_RELOCATOR16_STACK_SIZE, 16,
c4e390
-					  GRUB_RELOCATOR_PREFERENCE_NONE,
c4e390
-					  0);
c4e390
+  err = grub_relocator_alloc_chunk_align_safe (rel, &ch, 0x8010, 0xa0000,
c4e390
+					       RELOCATOR_SIZEOF (16) +
c4e390
+					       GRUB_RELOCATOR16_STACK_SIZE, 16,
c4e390
+					       GRUB_RELOCATOR_PREFERENCE_NONE, 0);
c4e390
   if (err)
c4e390
     return err;
c4e390
 
c4e390
@@ -184,11 +180,9 @@ grub_relocator64_boot (struct grub_relocator *rel,
c4e390
   void *relst;
c4e390
   grub_relocator_chunk_t ch;
c4e390
 
c4e390
-  err = grub_relocator_alloc_chunk_align (rel, &ch, min_addr,
c4e390
-					  max_addr - RELOCATOR_SIZEOF (64),
c4e390
-					  RELOCATOR_SIZEOF (64), 16,
c4e390
-					  GRUB_RELOCATOR_PREFERENCE_NONE,
c4e390
-					  0);
c4e390
+  err = grub_relocator_alloc_chunk_align_safe (rel, &ch, min_addr, max_addr,
c4e390
+					       RELOCATOR_SIZEOF (64), 16,
c4e390
+					       GRUB_RELOCATOR_PREFERENCE_NONE, 0);
c4e390
   if (err)
c4e390
     return err;
c4e390
 
c4e390
diff --git a/grub-core/lib/mips/relocator.c b/grub-core/lib/mips/relocator.c
c4e390
index 9d5f49cb93a..743b213e695 100644
c4e390
--- a/grub-core/lib/mips/relocator.c
c4e390
+++ b/grub-core/lib/mips/relocator.c
c4e390
@@ -120,10 +120,8 @@ grub_relocator32_boot (struct grub_relocator *rel,
c4e390
   unsigned i;
c4e390
   grub_addr_t vtarget;
c4e390
 
c4e390
-  err = grub_relocator_alloc_chunk_align (rel, &ch, 0,
c4e390
-					  (0xffffffff - stateset_size)
c4e390
-					  + 1, stateset_size,
c4e390
-					  sizeof (grub_uint32_t),
c4e390
+  err = grub_relocator_alloc_chunk_align (rel, &ch, 0, UP_TO_TOP32 (stateset_size),
c4e390
+					  stateset_size, sizeof (grub_uint32_t),
c4e390
 					  GRUB_RELOCATOR_PREFERENCE_NONE, 0);
c4e390
   if (err)
c4e390
     return err;
c4e390
diff --git a/grub-core/lib/powerpc/relocator.c b/grub-core/lib/powerpc/relocator.c
c4e390
index bdf2b111be7..8ffb8b68683 100644
c4e390
--- a/grub-core/lib/powerpc/relocator.c
c4e390
+++ b/grub-core/lib/powerpc/relocator.c
c4e390
@@ -115,10 +115,8 @@ grub_relocator32_boot (struct grub_relocator *rel,
c4e390
   unsigned i;
c4e390
   grub_relocator_chunk_t ch;
c4e390
 
c4e390
-  err = grub_relocator_alloc_chunk_align (rel, &ch, 0,
c4e390
-					  (0xffffffff - stateset_size)
c4e390
-					  + 1, stateset_size,
c4e390
-					  sizeof (grub_uint32_t),
c4e390
+  err = grub_relocator_alloc_chunk_align (rel, &ch, 0, UP_TO_TOP32 (stateset_size),
c4e390
+					  stateset_size, sizeof (grub_uint32_t),
c4e390
 					  GRUB_RELOCATOR_PREFERENCE_NONE, 0);
c4e390
   if (err)
c4e390
     return err;
c4e390
diff --git a/grub-core/loader/i386/linux.c b/grub-core/loader/i386/linux.c
c4e390
index 1a36979a5ec..8531ca17437 100644
c4e390
--- a/grub-core/loader/i386/linux.c
c4e390
+++ b/grub-core/loader/i386/linux.c
c4e390
@@ -230,9 +230,8 @@ allocate_pages (grub_size_t prot_size, grub_size_t *align,
c4e390
 	for (; err && *align + 1 > min_align; (*align)--)
c4e390
 	  {
c4e390
 	    grub_errno = GRUB_ERR_NONE;
c4e390
-	    err = grub_relocator_alloc_chunk_align (relocator, &ch,
c4e390
-						    0x1000000,
c4e390
-						    0xffffffff & ~prot_size,
c4e390
+	    err = grub_relocator_alloc_chunk_align (relocator, &ch, 0x1000000,
c4e390
+						    UP_TO_TOP32 (prot_size),
c4e390
 						    prot_size, 1 << *align,
c4e390
 						    GRUB_RELOCATOR_PREFERENCE_LOW,
c4e390
 						    1);
c4e390
diff --git a/grub-core/loader/i386/multiboot_mbi.c b/grub-core/loader/i386/multiboot_mbi.c
c4e390
index f10c087f724..adda5fd3a87 100644
c4e390
--- a/grub-core/loader/i386/multiboot_mbi.c
c4e390
+++ b/grub-core/loader/i386/multiboot_mbi.c
c4e390
@@ -445,10 +445,9 @@ grub_multiboot_make_mbi (grub_uint32_t *target)
c4e390
 
c4e390
   bufsize = grub_multiboot_get_mbi_size ();
c4e390
 
c4e390
-  err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch,
c4e390
-					  0x10000, 0xa0000 - bufsize,
c4e390
-					  bufsize, 4,
c4e390
-					  GRUB_RELOCATOR_PREFERENCE_NONE, 0);
c4e390
+  err = grub_relocator_alloc_chunk_align_safe (grub_multiboot_relocator, &ch,
c4e390
+					       0x10000, 0xa0000, bufsize, 4,
c4e390
+					       GRUB_RELOCATOR_PREFERENCE_NONE, 0);
c4e390
   if (err)
c4e390
     return err;
c4e390
   ptrorig = get_virtual_current_address (ch);
c4e390
diff --git a/grub-core/loader/i386/pc/linux.c b/grub-core/loader/i386/pc/linux.c
c4e390
index 4a801a7ab08..f217f5071b6 100644
c4e390
--- a/grub-core/loader/i386/pc/linux.c
c4e390
+++ b/grub-core/loader/i386/pc/linux.c
c4e390
@@ -457,10 +457,8 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
c4e390
 
c4e390
   {
c4e390
     grub_relocator_chunk_t ch;
c4e390
-    err = grub_relocator_alloc_chunk_align (relocator, &ch,
c4e390
-					    addr_min, addr_max - size,
c4e390
-					    size, 0x1000,
c4e390
-					    GRUB_RELOCATOR_PREFERENCE_HIGH, 0);
c4e390
+    err = grub_relocator_alloc_chunk_align_safe (relocator, &ch, addr_min, addr_max, size,
c4e390
+						 0x1000, GRUB_RELOCATOR_PREFERENCE_HIGH, 0);
c4e390
     if (err)
c4e390
       return err;
c4e390
     initrd_chunk = get_virtual_current_address (ch);
c4e390
diff --git a/grub-core/loader/mips/linux.c b/grub-core/loader/mips/linux.c
c4e390
index 4a3e8c5dfa2..f4bfd1d8b20 100644
c4e390
--- a/grub-core/loader/mips/linux.c
c4e390
+++ b/grub-core/loader/mips/linux.c
c4e390
@@ -434,12 +434,9 @@ grub_cmd_initrd (grub_command_t cmd __attribute__ ((unused)),
c4e390
   {
c4e390
     grub_relocator_chunk_t ch;
c4e390
 
c4e390
-    err = grub_relocator_alloc_chunk_align (relocator, &ch,
c4e390
-					    (target_addr & 0x1fffffff)
c4e390
-					    + linux_size + 0x10000,
c4e390
-					    (0x10000000 - size),
c4e390
-					    size, 0x10000,
c4e390
-					    GRUB_RELOCATOR_PREFERENCE_NONE, 0);
c4e390
+    err = grub_relocator_alloc_chunk_align_safe (relocator, &ch, (target_addr & 0x1fffffff) +
c4e390
+						 linux_size + 0x10000, 0x10000000, size,
c4e390
+						 0x10000, GRUB_RELOCATOR_PREFERENCE_NONE, 0);
c4e390
 
c4e390
     if (err)
c4e390
       goto fail;
c4e390
diff --git a/grub-core/loader/multiboot.c b/grub-core/loader/multiboot.c
c4e390
index e4e696e8f89..e2d3a8b5b2e 100644
c4e390
--- a/grub-core/loader/multiboot.c
c4e390
+++ b/grub-core/loader/multiboot.c
c4e390
@@ -343,7 +343,7 @@ grub_cmd_module (grub_command_t cmd __attribute__ ((unused)),
c4e390
   {
c4e390
     grub_relocator_chunk_t ch;
c4e390
     err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch,
c4e390
-					    lowest_addr, (0xffffffff - size) + 1,
c4e390
+					    lowest_addr, UP_TO_TOP32 (size),
c4e390
 					    size, MULTIBOOT_MOD_ALIGN,
c4e390
 					    GRUB_RELOCATOR_PREFERENCE_NONE, 1);
c4e390
     if (err)
c4e390
diff --git a/grub-core/loader/multiboot_elfxx.c b/grub-core/loader/multiboot_elfxx.c
c4e390
index c0ff1239dbf..6d87f08976f 100644
c4e390
--- a/grub-core/loader/multiboot_elfxx.c
c4e390
+++ b/grub-core/loader/multiboot_elfxx.c
c4e390
@@ -201,9 +201,8 @@ CONCAT(grub_multiboot_load_elf, XX) (grub_file_t file, const char *filename, voi
c4e390
 	    grub_relocator_chunk_t ch;
c4e390
 	    err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator,
c4e390
 						    &ch, 0,
c4e390
-						    (0xffffffff - sh->sh_size)
c4e390
-						    + 1, sh->sh_size,
c4e390
-						    sh->sh_addralign,
c4e390
+						    UP_TO_TOP32 (sh->sh_size),
c4e390
+						    sh->sh_size, sh->sh_addralign,
c4e390
 						    GRUB_RELOCATOR_PREFERENCE_NONE,
c4e390
 						    0);
c4e390
 	    if (err)
c4e390
diff --git a/grub-core/loader/multiboot_mbi2.c b/grub-core/loader/multiboot_mbi2.c
c4e390
index 7e01dfb5ee7..e99d3177a4a 100644
c4e390
--- a/grub-core/loader/multiboot_mbi2.c
c4e390
+++ b/grub-core/loader/multiboot_mbi2.c
c4e390
@@ -235,15 +235,18 @@ grub_multiboot_load (grub_file_t file, const char *filename)
c4e390
       grub_size_t code_size;
c4e390
       void *source;
c4e390
       grub_relocator_chunk_t ch;
c4e390
+      int preference = GRUB_RELOCATOR_PREFERENCE_NONE;
c4e390
 
c4e390
       if (addr_tag->bss_end_addr)
c4e390
 	code_size = (addr_tag->bss_end_addr - load_addr);
c4e390
       else
c4e390
 	code_size = load_size;
c4e390
 
c4e390
-      err = grub_relocator_alloc_chunk_addr (grub_multiboot_relocator, 
c4e390
-					     &ch, load_addr,
c4e390
-					     code_size);
c4e390
+      err = grub_relocator_alloc_chunk_align_safe (grub_multiboot_relocator,
c4e390
+						   &ch, load_addr,
c4e390
+						   UP_TO_TOP32(load_addr),
c4e390
+						   code_size, 0, preference,
c4e390
+						   keep_bs);
c4e390
       if (err)
c4e390
 	{
c4e390
 	  grub_dprintf ("multiboot_loader", "Error loading aout kludge\n");
c4e390
@@ -647,7 +650,7 @@ grub_multiboot_make_mbi (grub_uint32_t *target)
c4e390
   COMPILE_TIME_ASSERT (MULTIBOOT_TAG_ALIGN % sizeof (grub_properly_aligned_t) == 0);
c4e390
 
c4e390
   err = grub_relocator_alloc_chunk_align (grub_multiboot_relocator, &ch,
c4e390
-					  0, 0xffffffff - bufsize,
c4e390
+					  0, UP_TO_TOP32 (bufsize),
c4e390
 					  bufsize, MULTIBOOT_TAG_ALIGN,
c4e390
 					  GRUB_RELOCATOR_PREFERENCE_NONE, 1);
c4e390
   if (err)
c4e390
diff --git a/grub-core/loader/xnu_resume.c b/grub-core/loader/xnu_resume.c
c4e390
index 534a74438b2..99119558d21 100644
c4e390
--- a/grub-core/loader/xnu_resume.c
c4e390
+++ b/grub-core/loader/xnu_resume.c
c4e390
@@ -129,7 +129,7 @@ grub_xnu_resume (char *imagename)
c4e390
   {
c4e390
     grub_relocator_chunk_t ch;
c4e390
     err = grub_relocator_alloc_chunk_align (grub_xnu_relocator, &ch, 0,
c4e390
-					    (0xffffffff - hibhead.image_size) + 1,
c4e390
+					    UP_TO_TOP32 (hibhead.image_size),
c4e390
 					    hibhead.image_size,
c4e390
 					    GRUB_XNU_PAGESIZE,
c4e390
 					    GRUB_RELOCATOR_PREFERENCE_NONE, 0);
c4e390
diff --git a/include/grub/relocator.h b/include/grub/relocator.h
c4e390
index 24d8672d22c..1b3bdd92ac6 100644
c4e390
--- a/include/grub/relocator.h
c4e390
+++ b/include/grub/relocator.h
c4e390
@@ -49,6 +49,35 @@ grub_relocator_alloc_chunk_align (struct grub_relocator *rel,
c4e390
 				  int preference,
c4e390
 				  int avoid_efi_boot_services);
c4e390
 
c4e390
+/*
c4e390
+ * Wrapper for grub_relocator_alloc_chunk_align() with purpose of
c4e390
+ * protecting against integer underflow.
c4e390
+ *
c4e390
+ * Compare to its callee, max_addr has different meaning here.
c4e390
+ * It covers entire chunk and not just start address of the chunk.
c4e390
+ */
c4e390
+static inline grub_err_t
c4e390
+grub_relocator_alloc_chunk_align_safe (struct grub_relocator *rel,
c4e390
+				       grub_relocator_chunk_t *out,
c4e390
+				       grub_phys_addr_t min_addr,
c4e390
+				       grub_phys_addr_t max_addr,
c4e390
+				       grub_size_t size, grub_size_t align,
c4e390
+				       int preference,
c4e390
+				       int avoid_efi_boot_services)
c4e390
+{
c4e390
+  /* Sanity check and ensure following equation (max_addr - size) is safe. */
c4e390
+  if (max_addr < size || (max_addr - size) < min_addr)
c4e390
+    return GRUB_ERR_OUT_OF_RANGE;
c4e390
+
c4e390
+  return grub_relocator_alloc_chunk_align (rel, out, min_addr,
c4e390
+					   max_addr - size,
c4e390
+					   size, align, preference,
c4e390
+					   avoid_efi_boot_services);
c4e390
+}
c4e390
+
c4e390
+/* Top 32-bit address minus s bytes and plus 1 byte. */
c4e390
+#define UP_TO_TOP32(s)	((~(s) & 0xffffffff) + 1)
c4e390
+
c4e390
 #define GRUB_RELOCATOR_PREFERENCE_NONE 0
c4e390
 #define GRUB_RELOCATOR_PREFERENCE_LOW 1
c4e390
 #define GRUB_RELOCATOR_PREFERENCE_HIGH 2
c4e390
-- 
c4e390
2.26.2
c4e390