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