For NX, we need to set write and executable permissions on the sections of grub modules when we load them.
On sections with SHF_ALLOC set, which is typically everything except .modname and the symbol and string tables, this patch clears the Read Only flag on sections that have the ELF flag SHF_WRITE set, and clears the No eXecute flag on sections with SHF_EXECINSTR set. In all other cases it sets both flags. Signed-off-by: Peter Jones <pjo...@redhat.com> Signed-off-by: Robbie Harwood <rharw...@redhat.com> Signed-off-by: Laszlo Ersek <ler...@redhat.com> Signed-off-by: Jan Setje-Eilers <jan.setjeeil...@oracle.com> Signed-off-by: Mate Kukri <mate.ku...@canonical.com> --- grub-core/kern/dl.c | 85 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/grub-core/kern/dl.c b/grub-core/kern/dl.c index 47705f5af..54daa7f30 100644 --- a/grub-core/kern/dl.c +++ b/grub-core/kern/dl.c @@ -634,6 +634,84 @@ 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) + 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) + if (seg->section == s->sh_info) + break; + + 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) + 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; +} +#endif + /* Load a module from core memory. */ grub_dl_t grub_dl_load_core_noinit (void *addr, grub_size_t size) @@ -680,7 +758,12 @@ 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) +#ifdef GRUB_MACHINE_EFI + || grub_dl_set_mem_attrs (mod, e)) +#else + ) +#endif { mod->fini = 0; grub_dl_unload (mod); -- 2.39.2 _______________________________________________ Grub-devel mailing list Grub-devel@gnu.org https://lists.gnu.org/mailman/listinfo/grub-devel