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

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