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

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