grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v2] multiboot: Add support for program headers at "strange" locat


From: Jacob Paul
Subject: [PATCH v2] multiboot: Add support for program headers at "strange" locations
Date: Mon, 25 May 2020 13:06:33 +0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Thunderbird/68.8.1

Currently, grub_multiboot_load_elf() will throw an error if
the program header is at a too high offset (since it would be outside
the allocated buffer space of MULTIBOOT_SEARCH bytes). It is understandable
why it does this, since 99.99% of all cases e_phoff has a value of 0x34 or 0x40. So, to be able to support a (uncommon) circumstance like this, the program header
can be loaded into a separate buffer if needed, and then freed at the end.
The only downside to this is that grub_multiboot_load_elf() must at every return
statement check if phdr_base is a separate buffer and if so de-allocate it.

Signed-off-by: Jacob Paul <address@hidden>
---

Changes in v2:
- Add line breaks to commit message as suggested by John Paul Adrian Glaubitz (for some reason the line breaks were removed by my MUA).
- Removed whitespace.

 grub-core/loader/multiboot_elfxx.c | 86 +++++++++++++++++++++++++-----
 1 file changed, 74 insertions(+), 12 deletions(-)

diff --git a/grub-core/loader/multiboot_elfxx.c b/grub-core/loader/multiboot_elfxx.c
index 70cd1db51..f6aa156a0 100644
--- a/grub-core/loader/multiboot_elfxx.c
+++ b/grub-core/loader/multiboot_elfxx.c
@@ -75,12 +75,37 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
   if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
return grub_error (GRUB_ERR_UNKNOWN_OS, N_("this ELF file is not of the right type"));

-  /* FIXME: Should we support program headers at strange locations?  */
+ /* Program header outside of the allocated buffer space. Weird, but just work around it by allocating a new buffer for it */ if (ehdr->e_phoff + (grub_uint32_t) ehdr->e_phnum * ehdr->e_phentsize > MULTIBOOT_SEARCH) - return grub_error (GRUB_ERR_BAD_OS, "program header at a too high offset");
+    {
+ phdr_base = grub_malloc ((grub_size_t) ehdr->e_phnum * ehdr->e_phentsize);
+      if (!phdr_base)
+    return grub_errno;
+
+ if (grub_file_seek (mld->file, (grub_off_t) ehdr->e_phoff) == (grub_off_t) -1)
+        {
+          grub_free(phdr_base);
+ return grub_error (GRUB_ERR_BAD_OS, "invalid program header offset");
+        }
+
+ if (grub_file_read (mld->file, phdr_base, (grub_size_t) ehdr->e_phnum * ehdr->e_phentsize)
+      != (grub_ssize_t) ehdr->e_phnum * ehdr->e_phentsize)
+        {
+          grub_free (phdr_base);
+          if (!grub_errno)
+ grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"), mld->filename);
+          return grub_errno;
+        }
+    }
+  else
+    {
+      phdr_base = (char *) mld->buffer + ehdr->e_phoff;
+    }

-  phdr_base = (char *) mld->buffer + ehdr->e_phoff;
 #define phdr(i)                        ((Elf_Phdr *) (phdr_base + (i) * 
ehdr->e_phentsize))
+/* FIXME: find a better way to de-allocate phdr_base if an error occurs.
+ Putting this in front of every exit is probably not the prettiest solution */ +#define phdr_free() if ((grub_size_t) phdr_base != (grub_size_t) mld->buffer + ehdr->e_phoff) grub_free(phdr_base);

   mld->link_base_addr = ~0;

@@ -94,7 +119,10 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)

 #ifdef MULTIBOOT_LOAD_ELF64
   if (highest_load >= 0x100000000)
-    return grub_error (GRUB_ERR_BAD_OS, "segment crosses 4 GiB border");
+    {
+      phdr_free();
+      return grub_error (GRUB_ERR_BAD_OS, "segment crosses 4 GiB border");
+    }
 #endif

   if (mld->relocatable)
@@ -107,7 +135,10 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
                    mld->avoid_efi_boot_services);

if (load_size > mld->max_addr || mld->min_addr > mld->max_addr - load_size) - return grub_error (GRUB_ERR_BAD_OS, "invalid min/max address and/or load size");
+        {
+          phdr_free();
+ return grub_error (GRUB_ERR_BAD_OS, "invalid min/max address and/or load size");
+        }

err = grub_relocator_alloc_chunk_align (GRUB_MULTIBOOT (relocator), &ch,
                                              mld->min_addr, mld->max_addr - 
load_size,
@@ -117,6 +148,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
       if (err)
         {
grub_dprintf ("multiboot_loader", "Cannot allocate memory for OS image\n");
+          phdr_free();
           return err;
         }

@@ -152,6 +184,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
              if (err)
                {
grub_dprintf ("multiboot_loader", "Cannot allocate memory for OS image\n");
+      phdr_free();
                  return err;
                }

@@ -162,7 +195,10 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
            {
              if (grub_file_seek (mld->file, (grub_off_t) phdr(i)->p_offset)
                  == (grub_off_t) -1)
-               return grub_errno;
+    {
+      phdr_free();
+                 return grub_errno;
+    }

if (grub_file_read (mld->file, (grub_uint8_t *) source + load_offset, phdr(i)->p_filesz)
                  != (grub_ssize_t) phdr(i)->p_filesz)
@@ -170,6 +206,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
                  if (!grub_errno)
grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file %s"),
                                mld->filename);
+      phdr_free();
                  return grub_errno;
                }
            }
@@ -191,19 +228,28 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
   /* We still in 32-bit mode.  */
   if ((ehdr->e_entry - phdr(i)->p_vaddr)
       + phdr(i)->p_paddr < 0xffffffff80000000ULL)
-    return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64");
+    {
+      phdr_free();
+      return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64");
+    }
 # else
   /* We still in 32-bit mode.  */
   if ((ehdr->e_entry - phdr(i)->p_vaddr)
       + phdr(i)->p_paddr > 0xffffffff)
-    return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64");
+    {
+      phdr_free();
+      return grub_error (GRUB_ERR_BAD_OS, "invalid entry point for ELF64");
+    }
 # endif
 #endif
        break;
       }

   if (i == ehdr->e_phnum)
-    return grub_error (GRUB_ERR_BAD_OS, "entry point isn't in a segment");
+    {
+      phdr_free();
+ return grub_error (GRUB_ERR_BAD_OS, "entry point isn't in a segment");
+    }

 #if defined (__i386__) || defined (__x86_64__)

@@ -219,11 +265,15 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)

shdr = grub_malloc ((grub_uint32_t) ehdr->e_shnum * ehdr->e_shentsize);
       if (!shdr)
-       return grub_errno;
+  {
+    phdr_free();
+         return grub_errno;
+  }

       if (grub_file_seek (mld->file, ehdr->e_shoff) == (grub_off_t) -1)
        {
          grub_free (shdr);
+    phdr_free();
          return grub_errno;
        }

@@ -233,6 +283,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
          if (!grub_errno)
            grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file 
%s"),
                        mld->filename);
+    phdr_free();
          return grub_errno;
        }

@@ -244,7 +295,10 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
          grub_addr_t target;

if (mld->mbi_ver >= 2 && (sh->sh_type == SHT_REL || sh->sh_type == SHT_RELA)) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "ELF files with relocs are not supported yet");
+      {
+        phdr_free();
+ return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, "ELF files with relocs are not supported yet");
+      }

          /* This section is a loaded section,
             so we don't care.  */
@@ -263,13 +317,17 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
          if (err)
            {
              grub_dprintf ("multiboot_loader", "Error loading shdr %d\n", i);
+        phdr_free();
              return err;
            }
          src = get_virtual_current_address (ch);
          target = get_physical_target_address (ch);

          if (grub_file_seek (mld->file, sh->sh_offset) == (grub_off_t) -1)
-           return grub_errno;
+      {
+        phdr_free();
+             return grub_errno;
+      }

           if (grub_file_read (mld->file, src, sh->sh_size)
               != (grub_ssize_t) sh->sh_size)
@@ -277,6 +335,7 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
              if (!grub_errno)
                grub_error (GRUB_ERR_FILE_READ_ERROR, N_("premature end of file 
%s"),
                            mld->filename);
+        phdr_free();
              return grub_errno;
            }
          sh->sh_addr = target;
@@ -285,7 +344,10 @@ CONCAT(grub_multiboot_load_elf, XX) (mbi_load_data_t *mld)
                                    ehdr->e_shstrndx, shdr);
     }

+  phdr_free();
+
 #undef phdr
+#undef phdr_free

   return grub_errno;
 }
--
2.26.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]