Commit 5ea5306c authored by Ard Biesheuvel's avatar Ard Biesheuvel Committed by Catalin Marinas

arm64: alternatives: apply boot time fixups via the linear mapping

One important rule of thumb when desiging a secure software system is
that memory should never be writable and executable at the same time.
We mostly adhere to this rule in the kernel, except at boot time, when
regions may be mapped RWX until after we are done applying alternatives
or making other one-off changes.

For the alternative patching, we can improve the situation by applying
the fixups via the linear mapping, which is never mapped with executable
permissions. So map the linear alias of .text with RW- permissions
initially, and remove the write permissions as soon as alternative
patching has completed.
Reviewed-by: default avatarLaura Abbott <labbott@redhat.com>
Reviewed-by: default avatarMark Rutland <mark.rutland@arm.com>
Tested-by: default avatarMark Rutland <mark.rutland@arm.com>
Signed-off-by: default avatarArd Biesheuvel <ard.biesheuvel@linaro.org>
Signed-off-by: default avatarCatalin Marinas <catalin.marinas@arm.com>
parent aa8c09be
...@@ -37,5 +37,6 @@ extern void create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys, ...@@ -37,5 +37,6 @@ extern void create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys,
unsigned long virt, phys_addr_t size, unsigned long virt, phys_addr_t size,
pgprot_t prot, bool page_mappings_only); pgprot_t prot, bool page_mappings_only);
extern void *fixmap_remap_fdt(phys_addr_t dt_phys); extern void *fixmap_remap_fdt(phys_addr_t dt_phys);
extern void mark_linear_text_alias_ro(void);
#endif #endif
...@@ -105,11 +105,11 @@ static u32 get_alt_insn(struct alt_instr *alt, u32 *insnptr, u32 *altinsnptr) ...@@ -105,11 +105,11 @@ static u32 get_alt_insn(struct alt_instr *alt, u32 *insnptr, u32 *altinsnptr)
return insn; return insn;
} }
static void __apply_alternatives(void *alt_region) static void __apply_alternatives(void *alt_region, bool use_linear_alias)
{ {
struct alt_instr *alt; struct alt_instr *alt;
struct alt_region *region = alt_region; struct alt_region *region = alt_region;
u32 *origptr, *replptr; u32 *origptr, *replptr, *updptr;
for (alt = region->begin; alt < region->end; alt++) { for (alt = region->begin; alt < region->end; alt++) {
u32 insn; u32 insn;
...@@ -124,11 +124,12 @@ static void __apply_alternatives(void *alt_region) ...@@ -124,11 +124,12 @@ static void __apply_alternatives(void *alt_region)
origptr = ALT_ORIG_PTR(alt); origptr = ALT_ORIG_PTR(alt);
replptr = ALT_REPL_PTR(alt); replptr = ALT_REPL_PTR(alt);
updptr = use_linear_alias ? (u32 *)lm_alias(origptr) : origptr;
nr_inst = alt->alt_len / sizeof(insn); nr_inst = alt->alt_len / sizeof(insn);
for (i = 0; i < nr_inst; i++) { for (i = 0; i < nr_inst; i++) {
insn = get_alt_insn(alt, origptr + i, replptr + i); insn = get_alt_insn(alt, origptr + i, replptr + i);
*(origptr + i) = cpu_to_le32(insn); updptr[i] = cpu_to_le32(insn);
} }
flush_icache_range((uintptr_t)origptr, flush_icache_range((uintptr_t)origptr,
...@@ -155,7 +156,7 @@ static int __apply_alternatives_multi_stop(void *unused) ...@@ -155,7 +156,7 @@ static int __apply_alternatives_multi_stop(void *unused)
isb(); isb();
} else { } else {
BUG_ON(patched); BUG_ON(patched);
__apply_alternatives(&region); __apply_alternatives(&region, true);
/* Barriers provided by the cache flushing */ /* Barriers provided by the cache flushing */
WRITE_ONCE(patched, 1); WRITE_ONCE(patched, 1);
} }
...@@ -176,5 +177,5 @@ void apply_alternatives(void *start, size_t length) ...@@ -176,5 +177,5 @@ void apply_alternatives(void *start, size_t length)
.end = start + length, .end = start + length,
}; };
__apply_alternatives(&region); __apply_alternatives(&region, false);
} }
...@@ -434,6 +434,7 @@ void __init smp_cpus_done(unsigned int max_cpus) ...@@ -434,6 +434,7 @@ void __init smp_cpus_done(unsigned int max_cpus)
setup_cpu_features(); setup_cpu_features();
hyp_mode_check(); hyp_mode_check();
apply_alternatives_all(); apply_alternatives_all();
mark_linear_text_alias_ro();
} }
void __init smp_prepare_boot_cpu(void) void __init smp_prepare_boot_cpu(void)
......
...@@ -372,16 +372,28 @@ static void __init __map_memblock(pgd_t *pgd, phys_addr_t start, phys_addr_t end ...@@ -372,16 +372,28 @@ static void __init __map_memblock(pgd_t *pgd, phys_addr_t start, phys_addr_t end
debug_pagealloc_enabled()); debug_pagealloc_enabled());
/* /*
* Map the linear alias of the [_text, __init_begin) interval as * Map the linear alias of the [_text, __init_begin) interval
* read-only/non-executable. This makes the contents of the * as non-executable now, and remove the write permission in
* region accessible to subsystems such as hibernate, but * mark_linear_text_alias_ro() below (which will be called after
* protects it from inadvertent modification or execution. * alternative patching has completed). This makes the contents
* of the region accessible to subsystems such as hibernate,
* but protects it from inadvertent modification or execution.
*/ */
__create_pgd_mapping(pgd, kernel_start, __phys_to_virt(kernel_start), __create_pgd_mapping(pgd, kernel_start, __phys_to_virt(kernel_start),
kernel_end - kernel_start, PAGE_KERNEL_RO, kernel_end - kernel_start, PAGE_KERNEL,
early_pgtable_alloc, debug_pagealloc_enabled()); early_pgtable_alloc, debug_pagealloc_enabled());
} }
void __init mark_linear_text_alias_ro(void)
{
/*
* Remove the write permissions from the linear alias of .text/.rodata
*/
update_mapping_prot(__pa_symbol(_text), (unsigned long)lm_alias(_text),
(unsigned long)__init_begin - (unsigned long)_text,
PAGE_KERNEL_RO);
}
static void __init map_mem(pgd_t *pgd) static void __init map_mem(pgd_t *pgd)
{ {
struct memblock_region *reg; struct memblock_region *reg;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment