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

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