mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 06:44:00 -04:00
arch: make execmem setup available regardless of CONFIG_MODULES
execmem does not depend on modules, on the contrary modules use execmem. To make execmem available when CONFIG_MODULES=n, for instance for kprobes, split execmem_params initialization out from arch/*/kernel/module.c and compile it when CONFIG_EXECMEM=y Signed-off-by: Mike Rapoport (IBM) <rppt@kernel.org> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Signed-off-by: Luis Chamberlain <mcgrof@kernel.org>
This commit is contained in:
committed by
Luis Chamberlain
parent
1b750c2fbf
commit
0cc2dc4902
@@ -32,6 +32,7 @@
|
||||
#include <linux/hugetlb.h>
|
||||
#include <linux/acpi_iort.h>
|
||||
#include <linux/kmemleak.h>
|
||||
#include <linux/execmem.h>
|
||||
|
||||
#include <asm/boot.h>
|
||||
#include <asm/fixmap.h>
|
||||
@@ -432,3 +433,142 @@ void dump_mem_limit(void)
|
||||
pr_emerg("Memory Limit: none\n");
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXECMEM
|
||||
static u64 module_direct_base __ro_after_init = 0;
|
||||
static u64 module_plt_base __ro_after_init = 0;
|
||||
|
||||
/*
|
||||
* Choose a random page-aligned base address for a window of 'size' bytes which
|
||||
* entirely contains the interval [start, end - 1].
|
||||
*/
|
||||
static u64 __init random_bounding_box(u64 size, u64 start, u64 end)
|
||||
{
|
||||
u64 max_pgoff, pgoff;
|
||||
|
||||
if ((end - start) >= size)
|
||||
return 0;
|
||||
|
||||
max_pgoff = (size - (end - start)) / PAGE_SIZE;
|
||||
pgoff = get_random_u32_inclusive(0, max_pgoff);
|
||||
|
||||
return start - pgoff * PAGE_SIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Modules may directly reference data and text anywhere within the kernel
|
||||
* image and other modules. References using PREL32 relocations have a +/-2G
|
||||
* range, and so we need to ensure that the entire kernel image and all modules
|
||||
* fall within a 2G window such that these are always within range.
|
||||
*
|
||||
* Modules may directly branch to functions and code within the kernel text,
|
||||
* and to functions and code within other modules. These branches will use
|
||||
* CALL26/JUMP26 relocations with a +/-128M range. Without PLTs, we must ensure
|
||||
* that the entire kernel text and all module text falls within a 128M window
|
||||
* such that these are always within range. With PLTs, we can expand this to a
|
||||
* 2G window.
|
||||
*
|
||||
* We chose the 128M region to surround the entire kernel image (rather than
|
||||
* just the text) as using the same bounds for the 128M and 2G regions ensures
|
||||
* by construction that we never select a 128M region that is not a subset of
|
||||
* the 2G region. For very large and unusual kernel configurations this means
|
||||
* we may fall back to PLTs where they could have been avoided, but this keeps
|
||||
* the logic significantly simpler.
|
||||
*/
|
||||
static int __init module_init_limits(void)
|
||||
{
|
||||
u64 kernel_end = (u64)_end;
|
||||
u64 kernel_start = (u64)_text;
|
||||
u64 kernel_size = kernel_end - kernel_start;
|
||||
|
||||
/*
|
||||
* The default modules region is placed immediately below the kernel
|
||||
* image, and is large enough to use the full 2G relocation range.
|
||||
*/
|
||||
BUILD_BUG_ON(KIMAGE_VADDR != MODULES_END);
|
||||
BUILD_BUG_ON(MODULES_VSIZE < SZ_2G);
|
||||
|
||||
if (!kaslr_enabled()) {
|
||||
if (kernel_size < SZ_128M)
|
||||
module_direct_base = kernel_end - SZ_128M;
|
||||
if (kernel_size < SZ_2G)
|
||||
module_plt_base = kernel_end - SZ_2G;
|
||||
} else {
|
||||
u64 min = kernel_start;
|
||||
u64 max = kernel_end;
|
||||
|
||||
if (IS_ENABLED(CONFIG_RANDOMIZE_MODULE_REGION_FULL)) {
|
||||
pr_info("2G module region forced by RANDOMIZE_MODULE_REGION_FULL\n");
|
||||
} else {
|
||||
module_direct_base = random_bounding_box(SZ_128M, min, max);
|
||||
if (module_direct_base) {
|
||||
min = module_direct_base;
|
||||
max = module_direct_base + SZ_128M;
|
||||
}
|
||||
}
|
||||
|
||||
module_plt_base = random_bounding_box(SZ_2G, min, max);
|
||||
}
|
||||
|
||||
pr_info("%llu pages in range for non-PLT usage",
|
||||
module_direct_base ? (SZ_128M - kernel_size) / PAGE_SIZE : 0);
|
||||
pr_info("%llu pages in range for PLT usage",
|
||||
module_plt_base ? (SZ_2G - kernel_size) / PAGE_SIZE : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct execmem_info execmem_info __ro_after_init;
|
||||
|
||||
struct execmem_info __init *execmem_arch_setup(void)
|
||||
{
|
||||
unsigned long fallback_start = 0, fallback_end = 0;
|
||||
unsigned long start = 0, end = 0;
|
||||
|
||||
module_init_limits();
|
||||
|
||||
/*
|
||||
* Where possible, prefer to allocate within direct branch range of the
|
||||
* kernel such that no PLTs are necessary.
|
||||
*/
|
||||
if (module_direct_base) {
|
||||
start = module_direct_base;
|
||||
end = module_direct_base + SZ_128M;
|
||||
|
||||
if (module_plt_base) {
|
||||
fallback_start = module_plt_base;
|
||||
fallback_end = module_plt_base + SZ_2G;
|
||||
}
|
||||
} else if (module_plt_base) {
|
||||
start = module_plt_base;
|
||||
end = module_plt_base + SZ_2G;
|
||||
}
|
||||
|
||||
execmem_info = (struct execmem_info){
|
||||
.ranges = {
|
||||
[EXECMEM_DEFAULT] = {
|
||||
.start = start,
|
||||
.end = end,
|
||||
.pgprot = PAGE_KERNEL,
|
||||
.alignment = 1,
|
||||
.fallback_start = fallback_start,
|
||||
.fallback_end = fallback_end,
|
||||
},
|
||||
[EXECMEM_KPROBES] = {
|
||||
.start = VMALLOC_START,
|
||||
.end = VMALLOC_END,
|
||||
.pgprot = PAGE_KERNEL_ROX,
|
||||
.alignment = 1,
|
||||
},
|
||||
[EXECMEM_BPF] = {
|
||||
.start = VMALLOC_START,
|
||||
.end = VMALLOC_END,
|
||||
.pgprot = PAGE_KERNEL,
|
||||
.alignment = 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return &execmem_info;
|
||||
}
|
||||
#endif /* CONFIG_EXECMEM */
|
||||
|
||||
Reference in New Issue
Block a user