Blame SOURCES/0276-nx-set-page-permissions-for-loaded-modules.patch

e28c09
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
e28c09
From: Peter Jones <pjones@redhat.com>
e28c09
Date: Mon, 21 Mar 2022 17:46:35 -0400
e28c09
Subject: [PATCH] nx: set page permissions for loaded modules.
e28c09
e28c09
For NX, we need to set write and executable permissions on the sections
e28c09
of grub modules when we load them.
e28c09
e28c09
On sections with SHF_ALLOC set, which is typically everything except
e28c09
.modname and the symbol and string tables, this patch clears the Read
e28c09
Only flag on sections that have the ELF flag SHF_WRITE set, and clears
e28c09
the No eXecute flag on sections with SHF_EXECINSTR set.  In all other
e28c09
cases it sets both flags.
e28c09
e28c09
Signed-off-by: Peter Jones <pjones@redhat.com>
e28c09
[rharwood: arm tgptr -> tgaddr]
e28c09
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
e28c09
(cherry-picked from commit ca74904ede0406b594cbedc52ce8e38a6633d2ae)
e28c09
---
e28c09
 grub-core/kern/dl.c | 120 +++++++++++++++++++++++++++++++++++++++-------------
e28c09
 include/grub/dl.h   |  44 +++++++++++++++++++
e28c09
 2 files changed, 134 insertions(+), 30 deletions(-)
e28c09
e28c09
diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
e28c09
index 8c7aacef39..d5de80186f 100644
e28c09
--- a/grub-core/kern/dl.c
e28c09
+++ b/grub-core/kern/dl.c
e28c09
@@ -285,6 +285,8 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
e28c09
 #endif
e28c09
   char *ptr;
e28c09
 
e28c09
+  grub_dprintf ("modules", "loading segments for \"%s\"\n", mod->name);
e28c09
+
e28c09
   arch_addralign = grub_arch_dl_min_alignment ();
e28c09
 
e28c09
   for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff);
e28c09
@@ -384,6 +386,7 @@ grub_dl_load_segments (grub_dl_t mod, const Elf_Ehdr *e)
e28c09
   ptr += got;
e28c09
 #endif
e28c09
 
e28c09
+  grub_dprintf ("modules", "done loading segments for \"%s\"\n", mod->name);
e28c09
   return GRUB_ERR_NONE;
e28c09
 }
e28c09
 
e28c09
@@ -517,23 +520,6 @@ grub_dl_find_section (Elf_Ehdr *e, const char *name)
e28c09
       return s;
e28c09
   return NULL;
e28c09
 }
e28c09
-static long
e28c09
-grub_dl_find_section_index (Elf_Ehdr *e, const char *name)
e28c09
-{
e28c09
-  Elf_Shdr *s;
e28c09
-  const char *str;
e28c09
-  unsigned i;
e28c09
-
e28c09
-  s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
e28c09
-  str = (char *) e + s->sh_offset;
e28c09
-
e28c09
-  for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
e28c09
-       i < e->e_shnum;
e28c09
-       i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
e28c09
-    if (grub_strcmp (str + s->sh_name, name) == 0)
e28c09
-      return (long)i;
e28c09
-  return -1;
e28c09
-}
e28c09
 
e28c09
 /* Me, Vladimir Serbinenko, hereby I add this module check as per new
e28c09
    GNU module policy. Note that this license check is informative only.
e28c09
@@ -662,6 +648,7 @@ grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
e28c09
   Elf_Shdr *s;
e28c09
   unsigned i;
e28c09
 
e28c09
+  grub_dprintf ("modules", "relocating symbols for \"%s\"\n", mod->name);
e28c09
   for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
e28c09
        i < e->e_shnum;
e28c09
        i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
e28c09
@@ -670,24 +657,95 @@ grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
e28c09
 	grub_dl_segment_t seg;
e28c09
 	grub_err_t err;
e28c09
 
e28c09
-	/* Find the target segment.  */
e28c09
-	for (seg = mod->segment; seg; seg = seg->next)
e28c09
-	  if (seg->section == s->sh_info)
e28c09
-	    break;
e28c09
+	seg = grub_dl_find_segment(mod, s->sh_info);
e28c09
+        if (!seg)
e28c09
+	  continue;
e28c09
 
e28c09
-	if (seg)
e28c09
-	  {
e28c09
-	    if (!mod->symtab)
e28c09
-	      return grub_error (GRUB_ERR_BAD_MODULE, "relocation without symbol table");
e28c09
+	if (!mod->symtab)
e28c09
+	  return grub_error (GRUB_ERR_BAD_MODULE, "relocation without symbol table");
e28c09
 
e28c09
-	    err = grub_arch_dl_relocate_symbols (mod, ehdr, s, seg);
e28c09
-	    if (err)
e28c09
-	      return err;
e28c09
-	  }
e28c09
+	err = grub_arch_dl_relocate_symbols (mod, ehdr, s, seg);
e28c09
+	if (err)
e28c09
+	  return err;
e28c09
       }
e28c09
 
e28c09
+  grub_dprintf ("modules", "done relocating symbols for \"%s\"\n", mod->name);
e28c09
   return GRUB_ERR_NONE;
e28c09
 }
e28c09
+
e28c09
+static grub_err_t
e28c09
+grub_dl_set_mem_attrs (grub_dl_t mod, void *ehdr)
e28c09
+{
e28c09
+  unsigned i;
e28c09
+  const Elf_Shdr *s;
e28c09
+  const Elf_Ehdr *e = ehdr;
e28c09
+#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
e28c09
+  grub_size_t arch_addralign = grub_arch_dl_min_alignment ();
e28c09
+  grub_addr_t tgaddr;
e28c09
+  grub_uint64_t tgsz;
e28c09
+#endif
e28c09
+
e28c09
+  grub_dprintf ("modules", "updating memory attributes for \"%s\"\n",
e28c09
+		mod->name);
e28c09
+  for (i = 0, s = (const Elf_Shdr *)((const char *) e + e->e_shoff);
e28c09
+       i < e->e_shnum;
e28c09
+       i++, s = (const Elf_Shdr *)((const char *) s + e->e_shentsize))
e28c09
+    {
e28c09
+      grub_dl_segment_t seg;
e28c09
+      grub_uint64_t set_attrs = GRUB_MEM_ATTR_R;
e28c09
+      grub_uint64_t clear_attrs = GRUB_MEM_ATTR_W|GRUB_MEM_ATTR_X;
e28c09
+
e28c09
+      seg = grub_dl_find_segment(mod, i);
e28c09
+      if (!seg)
e28c09
+	continue;
e28c09
+
e28c09
+      if (seg->size == 0 || !(s->sh_flags & SHF_ALLOC))
e28c09
+	continue;
e28c09
+
e28c09
+      if (s->sh_flags & SHF_WRITE)
e28c09
+	{
e28c09
+	  set_attrs |= GRUB_MEM_ATTR_W;
e28c09
+	  clear_attrs &= ~GRUB_MEM_ATTR_W;
e28c09
+	}
e28c09
+
e28c09
+      if (s->sh_flags & SHF_EXECINSTR)
e28c09
+	{
e28c09
+	  set_attrs |= GRUB_MEM_ATTR_X;
e28c09
+	  clear_attrs &= ~GRUB_MEM_ATTR_X;
e28c09
+	}
e28c09
+
e28c09
+      grub_dprintf ("modules", "setting memory attrs for section \"%s\" to -%s%s%s+%s%s%s\n",
e28c09
+		    grub_dl_get_section_name(e, s),
e28c09
+		    (clear_attrs & GRUB_MEM_ATTR_R) ? "r" : "",
e28c09
+		    (clear_attrs & GRUB_MEM_ATTR_W) ? "w" : "",
e28c09
+		    (clear_attrs & GRUB_MEM_ATTR_X) ? "x" : "",
e28c09
+		    (set_attrs & GRUB_MEM_ATTR_R) ? "r" : "",
e28c09
+		    (set_attrs & GRUB_MEM_ATTR_W) ? "w" : "",
e28c09
+		    (set_attrs & GRUB_MEM_ATTR_X) ? "x" : "");
e28c09
+      grub_update_mem_attrs ((grub_addr_t)(seg->addr), seg->size, set_attrs, clear_attrs);
e28c09
+    }
e28c09
+
e28c09
+#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv)
e28c09
+  tgaddr = grub_min((grub_addr_t)mod->tramp, (grub_addr_t)mod->got);
e28c09
+  tgsz = grub_max((grub_addr_t)mod->trampptr, (grub_addr_t)mod->gotptr) - tgaddr;
e28c09
+
e28c09
+  if (tgsz)
e28c09
+    {
e28c09
+      tgsz = ALIGN_UP(tgsz, arch_addralign);
e28c09
+
e28c09
+      grub_dprintf ("modules", "updating attributes for GOT and trampolines\n",
e28c09
+		    mod->name);
e28c09
+      grub_update_mem_attrs (tgaddr, tgsz, GRUB_MEM_ATTR_R|GRUB_MEM_ATTR_X,
e28c09
+			     GRUB_MEM_ATTR_W);
e28c09
+    }
e28c09
+#endif
e28c09
+
e28c09
+  grub_dprintf ("modules", "done updating module memory attributes for \"%s\"\n",
e28c09
+		mod->name);
e28c09
+
e28c09
+  return GRUB_ERR_NONE;
e28c09
+}
e28c09
+
e28c09
 static void
e28c09
 grub_dl_print_gdb_info (grub_dl_t mod, Elf_Ehdr *e)
e28c09
 {
e28c09
@@ -753,6 +811,7 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size)
e28c09
   mod->ref_count = 1;
e28c09
 
e28c09
   grub_dprintf ("modules", "relocating to %p\n", mod);
e28c09
+
e28c09
   /* Me, Vladimir Serbinenko, hereby I add this module check as per new
e28c09
      GNU module policy. Note that this license check is informative only.
e28c09
      Modules have to be licensed under GPLv3 or GPLv3+ (optionally
e28c09
@@ -766,7 +825,8 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size)
e28c09
       || grub_dl_resolve_dependencies (mod, e)
e28c09
       || grub_dl_load_segments (mod, e)
e28c09
       || grub_dl_resolve_symbols (mod, e)
e28c09
-      || grub_dl_relocate_symbols (mod, e))
e28c09
+      || grub_dl_relocate_symbols (mod, e)
e28c09
+      || grub_dl_set_mem_attrs (mod, e))
e28c09
     {
e28c09
       mod->fini = 0;
e28c09
       grub_dl_unload (mod);
e28c09
diff --git a/include/grub/dl.h b/include/grub/dl.h
e28c09
index f36ed5cb17..45ac8e339f 100644
e28c09
--- a/include/grub/dl.h
e28c09
+++ b/include/grub/dl.h
e28c09
@@ -27,6 +27,7 @@
e28c09
 #include <grub/elf.h>
e28c09
 #include <grub/list.h>
e28c09
 #include <grub/misc.h>
e28c09
+#include <grub/mm.h>
e28c09
 #endif
e28c09
 
e28c09
 /*
e28c09
@@ -268,6 +269,49 @@ grub_dl_is_persistent (grub_dl_t mod)
e28c09
   return mod->persistent;
e28c09
 }
e28c09
 
e28c09
+static inline const char *
e28c09
+grub_dl_get_section_name (const Elf_Ehdr *e, const Elf_Shdr *s)
e28c09
+{
e28c09
+  Elf_Shdr *str_s;
e28c09
+  const char *str;
e28c09
+
e28c09
+  str_s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
e28c09
+  str = (char *) e + str_s->sh_offset;
e28c09
+
e28c09
+  return str + s->sh_name;
e28c09
+}
e28c09
+
e28c09
+static inline long
e28c09
+grub_dl_find_section_index (Elf_Ehdr *e, const char *name)
e28c09
+{
e28c09
+  Elf_Shdr *s;
e28c09
+  const char *str;
e28c09
+  unsigned i;
e28c09
+
e28c09
+  s = (Elf_Shdr *) ((char *) e + e->e_shoff + e->e_shstrndx * e->e_shentsize);
e28c09
+  str = (char *) e + s->sh_offset;
e28c09
+
e28c09
+  for (i = 0, s = (Elf_Shdr *) ((char *) e + e->e_shoff);
e28c09
+       i < e->e_shnum;
e28c09
+       i++, s = (Elf_Shdr *) ((char *) s + e->e_shentsize))
e28c09
+    if (grub_strcmp (str + s->sh_name, name) == 0)
e28c09
+      return (long)i;
e28c09
+  return -1;
e28c09
+}
e28c09
+
e28c09
+/* Return the segment for a section of index N */
e28c09
+static inline grub_dl_segment_t
e28c09
+grub_dl_find_segment (grub_dl_t mod, unsigned n)
e28c09
+{
e28c09
+  grub_dl_segment_t seg;
e28c09
+
e28c09
+  for (seg = mod->segment; seg; seg = seg->next)
e28c09
+    if (seg->section == n)
e28c09
+      return seg;
e28c09
+
e28c09
+  return NULL;
e28c09
+}
e28c09
+
e28c09
 #endif
e28c09
 
e28c09
 void * EXPORT_FUNC(grub_resolve_symbol) (const char *name);