File 0006-nx-Set-page-permissions-for-loaded-modules.patch of Package grub2

From c5332993e019400df2122a08a0a5bb4b15aac086 Mon Sep 17 00:00:00 2001
From: Mate Kukri <mate.kukri@canonical.com>
Date: Wed, 9 Oct 2024 09:16:41 +0100
Subject: [PATCH 06/13] nx: Set page permissions for loaded modules

For NX we need to set write and executable permissions on the sections
of GRUB modules when we load them. All allocatable sections are marked
readable. In addition:
  - SHF_WRITE sections are marked as writable,
  - and SHF_EXECINSTR sections are marked as executable.

Where relevant for the platform the tramp and GOT areas are marked non-writable.

Signed-off-by: Peter Jones <pjones@redhat.com>
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Signed-off-by: Jan Setje-Eilers <jan.setjeeilers@oracle.com>
Signed-off-by: Mate Kukri <mate.kukri@canonical.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
---
 grub-core/kern/dl.c | 91 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 90 insertions(+), 1 deletion(-)

diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c
index b384412d17..da31db218d 100644
--- a/grub-core/kern/dl.c
+++ b/grub-core/kern/dl.c
@@ -636,6 +636,94 @@ grub_dl_relocate_symbols (grub_dl_t mod, void *ehdr)
   return GRUB_ERR_NONE;
 }
 
+/* Only define this on EFI to save space in core. */
+#ifdef GRUB_MACHINE_EFI
+static grub_err_t
+grub_dl_set_mem_attrs (grub_dl_t mod, void *ehdr)
+{
+  unsigned i;
+  const Elf_Shdr *s;
+  const Elf_Ehdr *e = ehdr;
+  grub_err_t err;
+#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
+  !defined (__loongarch__)
+  grub_size_t arch_addralign = GRUB_DL_ALIGN;
+  grub_addr_t tgaddr;
+  grub_size_t tgsz;
+#endif
+
+  for (i = 0, s = (const Elf_Shdr *) ((const char *) e + e->e_shoff);
+       i < e->e_shnum;
+       i++, s = (const Elf_Shdr *) ((const char *) s + e->e_shentsize))
+    {
+      grub_dl_segment_t seg;
+      grub_uint64_t set_attrs = GRUB_MEM_ATTR_R;
+      grub_uint64_t clear_attrs = GRUB_MEM_ATTR_W | GRUB_MEM_ATTR_X;
+
+      for (seg = mod->segment; seg; seg = seg->next)
+	/* Does this ELF section's index match GRUB DL segment? */
+	if (seg->section == s->sh_info)
+	  break;
+
+      /* No GRUB DL segment found for this ELF section, skip it. */
+      if (!seg)
+	continue;
+
+      if (seg->size == 0 || !(s->sh_flags & SHF_ALLOC))
+	continue;
+
+      if (s->sh_flags & SHF_WRITE)
+	{
+	  set_attrs |= GRUB_MEM_ATTR_W;
+	  clear_attrs &= ~GRUB_MEM_ATTR_W;
+	}
+
+      if (s->sh_flags & SHF_EXECINSTR)
+	{
+	  set_attrs |= GRUB_MEM_ATTR_X;
+	  clear_attrs &= ~GRUB_MEM_ATTR_X;
+	}
+
+      err = grub_update_mem_attrs ((grub_addr_t) seg->addr, seg->size,
+				   set_attrs, clear_attrs);
+      if (err != GRUB_ERR_NONE)
+	return err;
+    }
+
+#if !defined (__i386__) && !defined (__x86_64__) && !defined(__riscv) && \
+  !defined (__loongarch__)
+  tgaddr = grub_min ((grub_addr_t) mod->tramp, (grub_addr_t) mod->got);
+  tgsz = grub_max ((grub_addr_t) mod->trampptr, (grub_addr_t) mod->gotptr) - tgaddr;
+
+  if (tgsz)
+    {
+      tgsz = ALIGN_UP (tgsz, arch_addralign);
+
+      if (tgaddr < (grub_addr_t) mod->base ||
+	  tgsz > (grub_addr_t) -1 - tgaddr ||
+	  tgaddr + tgsz > (grub_addr_t) mod->base + mod->sz)
+	return grub_error (GRUB_ERR_BUG,
+			   "BUG: trying to protect pages outside of module "
+			   "allocation (\"%s\"): module base %p, size 0x%"
+			   PRIxGRUB_SIZE "; tramp/GOT base 0x%" PRIxGRUB_ADDR
+			   ", size 0x%" PRIxGRUB_SIZE,
+			   mod->name, mod->base, mod->sz, tgaddr, tgsz);
+      err = grub_update_mem_attrs (tgaddr, tgsz, GRUB_MEM_ATTR_R | GRUB_MEM_ATTR_X, GRUB_MEM_ATTR_W);
+      if (err != GRUB_ERR_NONE)
+	return err;
+    }
+#endif
+
+  return GRUB_ERR_NONE;
+}
+#else
+static grub_err_t
+grub_dl_set_mem_attrs (grub_dl_t mod __attribute__ ((unused)), void *ehdr __attribute__ ((unused)))
+{
+  return GRUB_ERR_NONE;
+}
+#endif
+
 /* Load a module from core memory.  */
 grub_dl_t
 grub_dl_load_core_noinit (void *addr, grub_size_t size)
@@ -682,7 +770,8 @@ grub_dl_load_core_noinit (void *addr, grub_size_t size)
       || grub_dl_resolve_dependencies (mod, e)
       || grub_dl_load_segments (mod, e)
       || grub_dl_resolve_symbols (mod, e)
-      || grub_dl_relocate_symbols (mod, e))
+      || grub_dl_relocate_symbols (mod, e)
+      || grub_dl_set_mem_attrs (mod, e))
     {
       mod->fini = 0;
       grub_dl_unload (mod);
-- 
2.49.0

openSUSE Build Service is sponsored by