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