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

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