|
|
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);
|