Blame SOURCES/0549-modules-load-module-sections-at-page-aligned-address.patch

b9d01e
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
b9d01e
From: Peter Jones <pjones@redhat.com>
b9d01e
Date: Mon, 21 Mar 2022 17:45:40 -0400
b9d01e
Subject: [PATCH] modules: load module sections at page-aligned addresses
b9d01e
b9d01e
Currently we load module sections at whatever alignment gcc+ld happened
b9d01e
to dump into the ELF section header, which is often pretty useless.  For
b9d01e
example, by default time.mod has these sections on a current x86_64
b9d01e
build:
b9d01e
b9d01e
$ eu-readelf -a grub-core/time.mod |& grep ^Section -A13
b9d01e
Section Headers:
b9d01e
[Nr] Name            Type         Addr  Off      Size     ES Flags Lk Inf Al
b9d01e
[ 0]                 NULL         0     00000000 00000000  0        0   0  0
b9d01e
[ 1] .text           PROGBITS     0     00000040 0000015e  0 AX     0   0  1
b9d01e
[ 2] .rela.text      RELA         0     00000458 000001e0 24 I      8   1  8
b9d01e
[ 3] .rodata.str1.1  PROGBITS     0     0000019e 000000a1  1 AMS    0   0  1
b9d01e
[ 4] .module_license PROGBITS     0     00000240 0000000f  0 A      0   0  8
b9d01e
[ 5] .data           PROGBITS     0     0000024f 00000000  0 WA     0   0  1
b9d01e
[ 6] .bss            NOBITS       0     00000250 00000008  0 WA     0   0  8
b9d01e
[ 7] .modname        PROGBITS     0     00000250 00000005  0        0   0  1
b9d01e
[ 8] .symtab         SYMTAB       0     00000258 00000150 24        9   6  8
b9d01e
[ 9] .strtab         STRTAB       0     000003a8 000000ab  0        0   0  1
b9d01e
[10] .shstrtab       STRTAB       0     00000638 00000059  0        0   0  1
b9d01e
b9d01e
With NX protections being page based, loading sections with either a 1
b9d01e
or 8 *byte* alignment does absolutely nothing to help us out.
b9d01e
b9d01e
This patch switches most EFI platforms to load module sections at 4kB
b9d01e
page-aligned addresses.  To do so, it adds an new per-arch function,
b9d01e
grub_arch_dl_min_alignment(), which returns the alignment needed for
b9d01e
dynamically loaded sections (in bytes).  Currently it sets it to 4096
b9d01e
when GRUB_MACHINE_EFI is true on x86_64, i386, arm, arm64, and emu, and
b9d01e
1-byte alignment on everything else.
b9d01e
b9d01e
It then changes the allocation size computation and the loader code in
b9d01e
grub_dl_load_segments() to align the locations and sizes up to these
b9d01e
boundaries, and fills any added padding with zeros.
b9d01e
b9d01e
All of this happens before relocations are applied, so the relocations
b9d01e
factor that in with no change.
b9d01e
b9d01e
As an aside, initially Daniel Kiper and I thought that it might be a
b9d01e
better idea to split the modules up into top-level sections as
b9d01e
.text.modules, .rodata.modules, .data.modules, etc., so that their page
b9d01e
permissions would get set by the loader that's loading grub itself.
b9d01e
This turns out to have two significant downsides: 1) either in mkimage
b9d01e
or in grub_dl_relocate_symbols(), you wind up having to dynamically
b9d01e
process the relocations to accommodate the moved module sections, and 2)
b9d01e
you then need to change the permissions on the modules and change them
b9d01e
back while relocating them in grub_dl_relocate_symbols(), which means
b9d01e
that any loader that /does/ honor the section flags but does /not/
b9d01e
generally support NX with the memory attributes API will cause grub to
b9d01e
fail.
b9d01e
b9d01e
Signed-off-by: Peter Jones <pjones@redhat.com>
b9d01e
(cherry picked from commit 31d52500b281619d92b03b2c2d30fe15aedaf326)
b9d01e
(cherry picked from commit 04f1df6b665493e38de66018aebe377fdac4ceec)
b9d01e
[rharwood: not risc-v yet]
b9d01e
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
b9d01e
---
b9d01e
 grub-core/kern/arm/dl.c     | 13 +++++++++++++
b9d01e
 grub-core/kern/arm64/dl.c   | 13 +++++++++++++
b9d01e
 grub-core/kern/dl.c         | 29 +++++++++++++++++++++--------
b9d01e
 grub-core/kern/emu/full.c   | 13 +++++++++++++
b9d01e
 grub-core/kern/i386/dl.c    | 13 +++++++++++++
b9d01e
 grub-core/kern/ia64/dl.c    |  9 +++++++++
b9d01e
 grub-core/kern/mips/dl.c    |  8 ++++++++
b9d01e
 grub-core/kern/powerpc/dl.c |  9 +++++++++
b9d01e
 grub-core/kern/sparc64/dl.c |  9 +++++++++
b9d01e
 grub-core/kern/x86_64/dl.c  | 13 +++++++++++++
b9d01e
 include/grub/dl.h           |  2 ++
b9d01e
 docs/grub-dev.texi          |  6 +++---
b9d01e
 12 files changed, 126 insertions(+), 11 deletions(-)
b9d01e
b9d01e
diff --git a/grub-core/kern/arm/dl.c b/grub-core/kern/arm/dl.c
b9d01e
index eab9d17ff2..9260737936 100644
b9d01e
--- a/grub-core/kern/arm/dl.c
b9d01e
+++ b/grub-core/kern/arm/dl.c
b9d01e
@@ -278,3 +278,16 @@ grub_arch_dl_check_header (void *ehdr)
b9d01e
 
b9d01e
   return GRUB_ERR_NONE;
b9d01e
 }
b9d01e
+
b9d01e
+/*
b9d01e
+ * Tell the loader what our minimum section alignment is.
b9d01e
+ */
b9d01e
+grub_size_t
b9d01e
+grub_arch_dl_min_alignment (void)
b9d01e
+{
b9d01e
+#ifdef GRUB_MACHINE_EFI
b9d01e
+  return 4096;
b9d01e
+#else
b9d01e
+  return 1;
b9d01e
+#endif
b9d01e
+}
b9d01e
diff --git a/grub-core/kern/arm64/dl.c b/grub-core/kern/arm64/dl.c
b9d01e
index fb03373190..826f8e721e 100644
b9d01e
--- a/grub-core/kern/arm64/dl.c
b9d01e
+++ b/grub-core/kern/arm64/dl.c
b9d01e
@@ -191,3 +191,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
b9d01e
 
b9d01e
   return GRUB_ERR_NONE;
b9d01e
 }
b9d01e
+
b9d01e
+/*
b9d01e
+ * Tell the loader what our minimum section alignment is.
b9d01e
+ */
b9d01e
+grub_size_t
b9d01e
+grub_arch_dl_min_alignment (void)
b9d01e
+{
b9d01e
+#ifdef GRUB_MACHINE_EFI
b9d01e
+  return 4096;
b9d01e
+#else
b9d01e
+  return 1;
b9d01e
+#endif
b9d01e
+}
b9d01e
diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
b9d01e
index ba1b33c20b..5c2153acf9 100644
b9d01e
--- a/grub-core/kern/dl.c
b9d01e
+++ b/grub-core/kern/dl.c
b9d01e
@@ -278,7 +278,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
b9d01e
 {
b9d01e
   unsigned i;
b9d01e
   const Elf_Shdr *s;
b9d01e
-  grub_size_t tsize = 0, talign = 1;
b9d01e
+  grub_size_t tsize = 0, talign = 1, arch_addralign = 1;
b9d01e
 #if !defined (__i386__) && !defined (__x86_64__)
b9d01e
   grub_size_t tramp;
b9d01e
   grub_size_t got;
b9d01e
@@ -286,16 +286,24 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
b9d01e
 #endif
b9d01e
   char *ptr;
b9d01e
 
b9d01e
+  arch_addralign = grub_arch_dl_min_alignment ();
b9d01e
+
b9d01e
   for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff);
b9d01e
        i < e->e_shnum;
b9d01e
        i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize))
b9d01e
     {
b9d01e
+      grub_size_t sh_addralign;
b9d01e
+      grub_size_t sh_size;
b9d01e
+
b9d01e
       if (s->sh_size == 0 || !(s->sh_flags & SHF_ALLOC))
b9d01e
 	continue;
b9d01e
 
b9d01e
-      tsize = ALIGN_UP (tsize, s->sh_addralign) + s->sh_size;
b9d01e
-      if (talign < s->sh_addralign)
b9d01e
-	talign = s->sh_addralign;
b9d01e
+      sh_addralign = ALIGN_UP(s->sh_addralign, arch_addralign);
b9d01e
+      sh_size = ALIGN_UP(s->sh_size, sh_addralign);
b9d01e
+
b9d01e
+      tsize = ALIGN_UP (tsize, sh_addralign) + sh_size;
b9d01e
+      if (talign < sh_addralign)
b9d01e
+	talign = sh_addralign;
b9d01e
     }
b9d01e
 
b9d01e
 #if !defined (__i386__) && !defined (__x86_64__)
b9d01e
@@ -324,6 +332,9 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
b9d01e
        i < e->e_shnum;
b9d01e
        i++, s = (Elf_Shdr *)((char *) s + e->e_shentsize))
b9d01e
     {
b9d01e
+      grub_size_t sh_addralign = ALIGN_UP(s->sh_addralign, arch_addralign);
b9d01e
+      grub_size_t sh_size = ALIGN_UP(s->sh_size, sh_addralign);
b9d01e
+
b9d01e
       if (s->sh_flags & SHF_ALLOC)
b9d01e
 	{
b9d01e
 	  grub_dl_segment_t seg;
b9d01e
@@ -336,17 +347,19 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
b9d01e
 	    {
b9d01e
 	      void *addr;
b9d01e
 
b9d01e
-	      ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, s->sh_addralign);
b9d01e
+	      ptr = (char *) ALIGN_UP ((grub_addr_t) ptr, sh_addralign);
b9d01e
 	      addr = ptr;
b9d01e
-	      ptr += s->sh_size;
b9d01e
+	      ptr += sh_size;
b9d01e
 
b9d01e
 	      switch (s->sh_type)
b9d01e
 		{
b9d01e
 		case SHT_PROGBITS:
b9d01e
 		  grub_memcpy (addr, (char *) e + s->sh_offset, s->sh_size);
b9d01e
+		  grub_memset ((char *)addr + s->sh_size, 0,
b9d01e
+			       sh_size - s->sh_size);
b9d01e
 		  break;
b9d01e
 		case SHT_NOBITS:
b9d01e
-		  grub_memset (addr, 0, s->sh_size);
b9d01e
+		  grub_memset (addr, 0, sh_size);
b9d01e
 		  break;
b9d01e
 		}
b9d01e
 
b9d01e
@@ -355,7 +368,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
b9d01e
 	  else
b9d01e
 	    seg->addr = 0;
b9d01e
 
b9d01e
-	  seg->size = s->sh_size;
b9d01e
+	  seg->size = sh_size;
b9d01e
 	  seg->section = i;
b9d01e
 	  seg->next = mod->segment;
b9d01e
 	  mod->segment = seg;
b9d01e
diff --git a/grub-core/kern/emu/full.c b/grub-core/kern/emu/full.c
b9d01e
index e8d63b1f5f..1de1c28eb0 100644
b9d01e
--- a/grub-core/kern/emu/full.c
b9d01e
+++ b/grub-core/kern/emu/full.c
b9d01e
@@ -67,3 +67,16 @@ grub_arch_dl_init_linker (void)
b9d01e
 }
b9d01e
 #endif
b9d01e
 
b9d01e
+
b9d01e
+/*
b9d01e
+ * Tell the loader what our minimum section alignment is.
b9d01e
+ */
b9d01e
+grub_size_t
b9d01e
+grub_arch_dl_min_alignment (void)
b9d01e
+{
b9d01e
+#ifdef GRUB_MACHINE_EFI
b9d01e
+  return 4096;
b9d01e
+#else
b9d01e
+  return 1;
b9d01e
+#endif
b9d01e
+}
b9d01e
diff --git a/grub-core/kern/i386/dl.c b/grub-core/kern/i386/dl.c
b9d01e
index 1346da5cc9..d6b4681fc9 100644
b9d01e
--- a/grub-core/kern/i386/dl.c
b9d01e
+++ b/grub-core/kern/i386/dl.c
b9d01e
@@ -79,3 +79,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
b9d01e
 
b9d01e
   return GRUB_ERR_NONE;
b9d01e
 }
b9d01e
+
b9d01e
+/*
b9d01e
+ * Tell the loader what our minimum section alignment is.
b9d01e
+ */
b9d01e
+grub_size_t
b9d01e
+grub_arch_dl_min_alignment (void)
b9d01e
+{
b9d01e
+#ifdef GRUB_MACHINE_EFI
b9d01e
+  return 4096;
b9d01e
+#else
b9d01e
+  return 1;
b9d01e
+#endif
b9d01e
+}
b9d01e
diff --git a/grub-core/kern/ia64/dl.c b/grub-core/kern/ia64/dl.c
b9d01e
index ebcf316298..3bb753a89b 100644
b9d01e
--- a/grub-core/kern/ia64/dl.c
b9d01e
+++ b/grub-core/kern/ia64/dl.c
b9d01e
@@ -143,3 +143,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
b9d01e
     }
b9d01e
   return GRUB_ERR_NONE;
b9d01e
 }
b9d01e
+
b9d01e
+/*
b9d01e
+ * Tell the loader what our minimum section alignment is.
b9d01e
+ */
b9d01e
+grub_size_t
b9d01e
+grub_arch_dl_min_alignment (void)
b9d01e
+{
b9d01e
+  return 1;
b9d01e
+}
b9d01e
diff --git a/grub-core/kern/mips/dl.c b/grub-core/kern/mips/dl.c
b9d01e
index 5d7d299c74..6d83bd71e9 100644
b9d01e
--- a/grub-core/kern/mips/dl.c
b9d01e
+++ b/grub-core/kern/mips/dl.c
b9d01e
@@ -272,3 +272,11 @@ grub_arch_dl_init_linker (void)
b9d01e
   grub_dl_register_symbol ("_gp_disp", &_gp_disp_dummy, 0, 0);
b9d01e
 }
b9d01e
 
b9d01e
+/*
b9d01e
+ * Tell the loader what our minimum section alignment is.
b9d01e
+ */
b9d01e
+grub_size_t
b9d01e
+grub_arch_dl_min_alignment (void)
b9d01e
+{
b9d01e
+  return 1;
b9d01e
+}
b9d01e
diff --git a/grub-core/kern/powerpc/dl.c b/grub-core/kern/powerpc/dl.c
b9d01e
index 3a7fa3ed3d..577e27d871 100644
b9d01e
--- a/grub-core/kern/powerpc/dl.c
b9d01e
+++ b/grub-core/kern/powerpc/dl.c
b9d01e
@@ -165,3 +165,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
b9d01e
 
b9d01e
   return GRUB_ERR_NONE;
b9d01e
 }
b9d01e
+
b9d01e
+/*
b9d01e
+ * Tell the loader what our minimum section alignment is.
b9d01e
+ */
b9d01e
+grub_size_t
b9d01e
+grub_arch_dl_min_alignment (void)
b9d01e
+{
b9d01e
+  return 1;
b9d01e
+}
b9d01e
diff --git a/grub-core/kern/sparc64/dl.c b/grub-core/kern/sparc64/dl.c
b9d01e
index 739be47174..c741c1782e 100644
b9d01e
--- a/grub-core/kern/sparc64/dl.c
b9d01e
+++ b/grub-core/kern/sparc64/dl.c
b9d01e
@@ -184,3 +184,12 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
b9d01e
 
b9d01e
   return GRUB_ERR_NONE;
b9d01e
 }
b9d01e
+
b9d01e
+/*
b9d01e
+ * Tell the loader what our minimum section alignment is.
b9d01e
+ */
b9d01e
+grub_size_t
b9d01e
+grub_arch_dl_min_alignment (void)
b9d01e
+{
b9d01e
+  return 1;
b9d01e
+}
b9d01e
diff --git a/grub-core/kern/x86_64/dl.c b/grub-core/kern/x86_64/dl.c
b9d01e
index 3a73e6e6ce..6c20b7c367 100644
b9d01e
--- a/grub-core/kern/x86_64/dl.c
b9d01e
+++ b/grub-core/kern/x86_64/dl.c
b9d01e
@@ -114,3 +114,16 @@ grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
b9d01e
 
b9d01e
   return GRUB_ERR_NONE;
b9d01e
 }
b9d01e
+
b9d01e
+/*
b9d01e
+ * Tell the loader what our minimum section alignment is.
b9d01e
+ */
b9d01e
+grub_size_t
b9d01e
+grub_arch_dl_min_alignment (void)
b9d01e
+{
b9d01e
+#ifdef GRUB_MACHINE_EFI
b9d01e
+  return 4096;
b9d01e
+#else
b9d01e
+  return 1;
b9d01e
+#endif
b9d01e
+}
b9d01e
diff --git a/include/grub/dl.h b/include/grub/dl.h
b9d01e
index 9ec6caf3f9..dd4c3e7ff4 100644
b9d01e
--- a/include/grub/dl.h
b9d01e
+++ b/include/grub/dl.h
b9d01e
@@ -280,6 +280,8 @@ grub_err_t grub_arch_dl_check_header (void *ehdr);
b9d01e
 grub_err_t
b9d01e
 grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr,
b9d01e
 			       Elf_Shdr *s, grub_dl_segment_t seg);
b9d01e
+grub_size_t
b9d01e
+grub_arch_dl_min_alignment (void);
b9d01e
 #endif
b9d01e
 
b9d01e
 #if defined (_mips)
b9d01e
diff --git a/docs/grub-dev.texi b/docs/grub-dev.texi
b9d01e
index 03d53498c5..dbfb94bc06 100644
b9d01e
--- a/docs/grub-dev.texi
b9d01e
+++ b/docs/grub-dev.texi
b9d01e
@@ -638,9 +638,9 @@ declare startup asm file ($cpu_$platform_startup) as well as any other files
b9d01e
 (e.g. init.c and callwrap.S) (e.g. $cpu_$platform = kern/$cpu/$platform/init.c).
b9d01e
 At this stage you will also need to add dummy dl.c and cache.S with functions
b9d01e
 grub_err_t grub_arch_dl_check_header (void *ehdr), grub_err_t
b9d01e
-grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) (dl.c) and
b9d01e
-void grub_arch_sync_caches (void *address, grub_size_t len) (cache.S). They
b9d01e
-won't be used for now.
b9d01e
+grub_arch_dl_relocate_symbols (grub_dl_t mod, void *ehdr) (dl.c), grub_uint32_t
b9d01e
+grub_arch_dl_min_alignment (void), and void grub_arch_sync_caches (void
b9d01e
+*address, grub_size_t len) (cache.S). They won't be used for now.
b9d01e
 
b9d01e
 You will need to create directory include/$cpu/$platform and a file
b9d01e
 include/$cpu/types.h. The later folowing this template: