mirror of
https://github.com/torvalds/linux.git
synced 2026-05-31 10:33:41 +02:00
arm64: map [_text, _stext) virtual address range non-executable+read-only
Since the referenced fixes commit, the kernel's .text section is only
mapped starting from _stext; the region [_text, _stext) is omitted. As a
result, other vmalloc/vmap allocations may use the virtual addresses
nominally in the range [_text, _stext). This address reuse confuses
multiple things:
1. crash_prepare_elf64_headers() sets up a segment in /proc/vmcore
mapping the entire range [_text, _end) to
[__pa_symbol(_text), __pa_symbol(_end)). Reading an address in
[_text, _stext) from /proc/vmcore therefore gives the incorrect
result.
2. Tools doing symbolization (either by reading /proc/kallsyms or based
on the vmlinux ELF file) will incorrectly identify vmalloc/vmap
allocations in [_text, _stext) as kernel symbols.
In practice, both of these issues affect the drgn debugger.
Specifically, there were cases where the vmap IRQ stacks for some CPUs
were allocated in [_text, _stext). As a result, drgn could not get the
stack trace for a crash in an IRQ handler because the core dump
contained invalid data for the IRQ stack address. The stack addresses
were also symbolized as being in the _text symbol.
Fix this by bringing back the mapping of [_text, _stext), but now make
it non-executable and read-only. This prevents other allocations from
using it while still achieving the original goal of not mapping
unpredictable data as executable. Other than the changed protection,
this is effectively a revert of the fixes commit.
Fixes: e2a073dde9 ("arm64: omit [_text, _stext) from permanent kernel mapping")
Cc: stable@vger.kernel.org
Signed-off-by: Omar Sandoval <osandov@fb.com>
Signed-off-by: Will Deacon <will@kernel.org>
This commit is contained in:
parent
fa93b45fd3
commit
5973a62efa
|
|
@ -78,6 +78,12 @@ static void __init map_kernel(u64 kaslr_offset, u64 va_offset, int root_level)
|
||||||
twopass |= enable_scs;
|
twopass |= enable_scs;
|
||||||
prot = twopass ? data_prot : text_prot;
|
prot = twopass ? data_prot : text_prot;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* [_stext, _text) isn't executed after boot and contains some
|
||||||
|
* non-executable, unpredictable data, so map it non-executable.
|
||||||
|
*/
|
||||||
|
map_segment(init_pg_dir, &pgdp, va_offset, _text, _stext, data_prot,
|
||||||
|
false, root_level);
|
||||||
map_segment(init_pg_dir, &pgdp, va_offset, _stext, _etext, prot,
|
map_segment(init_pg_dir, &pgdp, va_offset, _stext, _etext, prot,
|
||||||
!twopass, root_level);
|
!twopass, root_level);
|
||||||
map_segment(init_pg_dir, &pgdp, va_offset, __start_rodata,
|
map_segment(init_pg_dir, &pgdp, va_offset, __start_rodata,
|
||||||
|
|
|
||||||
|
|
@ -214,7 +214,7 @@ static void __init request_standard_resources(void)
|
||||||
unsigned long i = 0;
|
unsigned long i = 0;
|
||||||
size_t res_size;
|
size_t res_size;
|
||||||
|
|
||||||
kernel_code.start = __pa_symbol(_stext);
|
kernel_code.start = __pa_symbol(_text);
|
||||||
kernel_code.end = __pa_symbol(__init_begin - 1);
|
kernel_code.end = __pa_symbol(__init_begin - 1);
|
||||||
kernel_data.start = __pa_symbol(_sdata);
|
kernel_data.start = __pa_symbol(_sdata);
|
||||||
kernel_data.end = __pa_symbol(_end - 1);
|
kernel_data.end = __pa_symbol(_end - 1);
|
||||||
|
|
@ -280,7 +280,7 @@ u64 cpu_logical_map(unsigned int cpu)
|
||||||
|
|
||||||
void __init __no_sanitize_address setup_arch(char **cmdline_p)
|
void __init __no_sanitize_address setup_arch(char **cmdline_p)
|
||||||
{
|
{
|
||||||
setup_initial_init_mm(_stext, _etext, _edata, _end);
|
setup_initial_init_mm(_text, _etext, _edata, _end);
|
||||||
|
|
||||||
*cmdline_p = boot_command_line;
|
*cmdline_p = boot_command_line;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -279,7 +279,7 @@ void __init arm64_memblock_init(void)
|
||||||
* Register the kernel text, kernel data, initrd, and initial
|
* Register the kernel text, kernel data, initrd, and initial
|
||||||
* pagetables with memblock.
|
* pagetables with memblock.
|
||||||
*/
|
*/
|
||||||
memblock_reserve(__pa_symbol(_stext), _end - _stext);
|
memblock_reserve(__pa_symbol(_text), _end - _text);
|
||||||
if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && phys_initrd_size) {
|
if (IS_ENABLED(CONFIG_BLK_DEV_INITRD) && phys_initrd_size) {
|
||||||
/* the generic initrd code expects virtual addresses */
|
/* the generic initrd code expects virtual addresses */
|
||||||
initrd_start = __phys_to_virt(phys_initrd_start);
|
initrd_start = __phys_to_virt(phys_initrd_start);
|
||||||
|
|
|
||||||
|
|
@ -965,8 +965,8 @@ void __init mark_linear_text_alias_ro(void)
|
||||||
/*
|
/*
|
||||||
* Remove the write permissions from the linear alias of .text/.rodata
|
* Remove the write permissions from the linear alias of .text/.rodata
|
||||||
*/
|
*/
|
||||||
update_mapping_prot(__pa_symbol(_stext), (unsigned long)lm_alias(_stext),
|
update_mapping_prot(__pa_symbol(_text), (unsigned long)lm_alias(_text),
|
||||||
(unsigned long)__init_begin - (unsigned long)_stext,
|
(unsigned long)__init_begin - (unsigned long)_text,
|
||||||
PAGE_KERNEL_RO);
|
PAGE_KERNEL_RO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1037,7 +1037,7 @@ static inline bool force_pte_mapping(void)
|
||||||
static void __init map_mem(pgd_t *pgdp)
|
static void __init map_mem(pgd_t *pgdp)
|
||||||
{
|
{
|
||||||
static const u64 direct_map_end = _PAGE_END(VA_BITS_MIN);
|
static const u64 direct_map_end = _PAGE_END(VA_BITS_MIN);
|
||||||
phys_addr_t kernel_start = __pa_symbol(_stext);
|
phys_addr_t kernel_start = __pa_symbol(_text);
|
||||||
phys_addr_t kernel_end = __pa_symbol(__init_begin);
|
phys_addr_t kernel_end = __pa_symbol(__init_begin);
|
||||||
phys_addr_t start, end;
|
phys_addr_t start, end;
|
||||||
phys_addr_t early_kfence_pool;
|
phys_addr_t early_kfence_pool;
|
||||||
|
|
@ -1086,7 +1086,7 @@ static void __init map_mem(pgd_t *pgdp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Map the linear alias of the [_stext, __init_begin) interval
|
* Map the linear alias of the [_text, __init_begin) interval
|
||||||
* as non-executable now, and remove the write permission in
|
* as non-executable now, and remove the write permission in
|
||||||
* mark_linear_text_alias_ro() below (which will be called after
|
* mark_linear_text_alias_ro() below (which will be called after
|
||||||
* alternative patching has completed). This makes the contents
|
* alternative patching has completed). This makes the contents
|
||||||
|
|
@ -1113,6 +1113,10 @@ void mark_rodata_ro(void)
|
||||||
WRITE_ONCE(rodata_is_rw, false);
|
WRITE_ONCE(rodata_is_rw, false);
|
||||||
update_mapping_prot(__pa_symbol(__start_rodata), (unsigned long)__start_rodata,
|
update_mapping_prot(__pa_symbol(__start_rodata), (unsigned long)__start_rodata,
|
||||||
section_size, PAGE_KERNEL_RO);
|
section_size, PAGE_KERNEL_RO);
|
||||||
|
/* mark the range between _text and _stext as read only. */
|
||||||
|
update_mapping_prot(__pa_symbol(_text), (unsigned long)_text,
|
||||||
|
(unsigned long)_stext - (unsigned long)_text,
|
||||||
|
PAGE_KERNEL_RO);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init declare_vma(struct vm_struct *vma,
|
static void __init declare_vma(struct vm_struct *vma,
|
||||||
|
|
@ -1183,7 +1187,7 @@ static void __init declare_kernel_vmas(void)
|
||||||
{
|
{
|
||||||
static struct vm_struct vmlinux_seg[KERNEL_SEGMENT_COUNT];
|
static struct vm_struct vmlinux_seg[KERNEL_SEGMENT_COUNT];
|
||||||
|
|
||||||
declare_vma(&vmlinux_seg[0], _stext, _etext, VM_NO_GUARD);
|
declare_vma(&vmlinux_seg[0], _text, _etext, VM_NO_GUARD);
|
||||||
declare_vma(&vmlinux_seg[1], __start_rodata, __inittext_begin, VM_NO_GUARD);
|
declare_vma(&vmlinux_seg[1], __start_rodata, __inittext_begin, VM_NO_GUARD);
|
||||||
declare_vma(&vmlinux_seg[2], __inittext_begin, __inittext_end, VM_NO_GUARD);
|
declare_vma(&vmlinux_seg[2], __inittext_begin, __inittext_end, VM_NO_GUARD);
|
||||||
declare_vma(&vmlinux_seg[3], __initdata_begin, __initdata_end, VM_NO_GUARD);
|
declare_vma(&vmlinux_seg[3], __initdata_begin, __initdata_end, VM_NO_GUARD);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user