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

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