mirror of
https://github.com/torvalds/linux.git
synced 2025-04-11 04:53:02 +00:00

Instead of using writable copy for module text sections, temporarily remap the memory allocated from execmem's ROX cache as writable and restore its ROX permissions after the module is formed. This will allow removing nasty games with writable copy in alternatives patching on x86. Signed-off-by: "Mike Rapoport (Microsoft)" <rppt@kernel.org> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lore.kernel.org/r/20250126074733.1384926-7-rppt@kernel.org
109 lines
2.5 KiB
C
109 lines
2.5 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Module strict rwx
|
|
*
|
|
* Copyright (C) 2015 Rusty Russell
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/set_memory.h>
|
|
#include <linux/execmem.h>
|
|
#include "internal.h"
|
|
|
|
static int module_set_memory(const struct module *mod, enum mod_mem_type type,
|
|
int (*set_memory)(unsigned long start, int num_pages))
|
|
{
|
|
const struct module_memory *mod_mem = &mod->mem[type];
|
|
|
|
if (!mod_mem->base)
|
|
return 0;
|
|
|
|
set_vm_flush_reset_perms(mod_mem->base);
|
|
return set_memory((unsigned long)mod_mem->base, mod_mem->size >> PAGE_SHIFT);
|
|
}
|
|
|
|
/*
|
|
* Since some arches are moving towards PAGE_KERNEL module allocations instead
|
|
* of PAGE_KERNEL_EXEC, keep module_enable_x() independent of
|
|
* CONFIG_STRICT_MODULE_RWX because they are needed regardless of whether we
|
|
* are strict.
|
|
*/
|
|
int module_enable_text_rox(const struct module *mod)
|
|
{
|
|
for_class_mod_mem_type(type, text) {
|
|
const struct module_memory *mem = &mod->mem[type];
|
|
int ret;
|
|
|
|
if (mem->is_rox)
|
|
ret = execmem_restore_rox(mem->base, mem->size);
|
|
else if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX))
|
|
ret = module_set_memory(mod, type, set_memory_rox);
|
|
else
|
|
ret = module_set_memory(mod, type, set_memory_x);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int module_enable_rodata_ro(const struct module *mod)
|
|
{
|
|
int ret;
|
|
|
|
if (!IS_ENABLED(CONFIG_STRICT_MODULE_RWX) || !rodata_enabled)
|
|
return 0;
|
|
|
|
ret = module_set_memory(mod, MOD_RODATA, set_memory_ro);
|
|
if (ret)
|
|
return ret;
|
|
ret = module_set_memory(mod, MOD_INIT_RODATA, set_memory_ro);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int module_enable_rodata_ro_after_init(const struct module *mod)
|
|
{
|
|
if (!IS_ENABLED(CONFIG_STRICT_MODULE_RWX) || !rodata_enabled)
|
|
return 0;
|
|
|
|
return module_set_memory(mod, MOD_RO_AFTER_INIT, set_memory_ro);
|
|
}
|
|
|
|
int module_enable_data_nx(const struct module *mod)
|
|
{
|
|
if (!IS_ENABLED(CONFIG_STRICT_MODULE_RWX))
|
|
return 0;
|
|
|
|
for_class_mod_mem_type(type, data) {
|
|
int ret = module_set_memory(mod, type, set_memory_nx);
|
|
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int module_enforce_rwx_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
|
|
char *secstrings, struct module *mod)
|
|
{
|
|
const unsigned long shf_wx = SHF_WRITE | SHF_EXECINSTR;
|
|
int i;
|
|
|
|
if (!IS_ENABLED(CONFIG_STRICT_MODULE_RWX))
|
|
return 0;
|
|
|
|
for (i = 0; i < hdr->e_shnum; i++) {
|
|
if ((sechdrs[i].sh_flags & shf_wx) == shf_wx) {
|
|
pr_err("%s: section %s (index %d) has invalid WRITE|EXEC flags\n",
|
|
mod->name, secstrings + sechdrs[i].sh_name, i);
|
|
return -ENOEXEC;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|