arm64: mm: use XN table mapping attributes for the linear region
The way the arm64 kernel virtual address space is constructed guarantees that swapper PGD entries are never shared between the linear region on the one hand, and the vmalloc region on the other, which is where all kernel text, module text and BPF text mappings reside. This means that mappings in the linear region (which never require executable permissions) never share any table entries at any level with mappings that do require executable permissions, and so we can set the table-level PXN attributes for all table entries that are created while setting up mappings in the linear region. Since swapper's PGD level page table is mapped r/o itself, this adds another layer of robustness to the way the kernel manages its own page tables. While at it, set the UXN attribute as well for all kernel mappings created at boot. Acked-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Ard Biesheuvel <ardb@kernel.org> Reviewed-by: Anshuman Khandual <anshuman.khandual@arm.com> Link: https://lore.kernel.org/r/20210310104942.174584-3-ardb@kernel.org Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
This commit is contained in:
Родитель
c1fd78a777
Коммит
87143f404f
|
@ -101,6 +101,8 @@
|
|||
#define P4D_TYPE_MASK (_AT(p4dval_t, 3) << 0)
|
||||
#define P4D_TYPE_SECT (_AT(p4dval_t, 1) << 0)
|
||||
#define P4D_SECT_RDONLY (_AT(p4dval_t, 1) << 7) /* AP[2] */
|
||||
#define P4D_TABLE_PXN (_AT(p4dval_t, 1) << 59)
|
||||
#define P4D_TABLE_UXN (_AT(p4dval_t, 1) << 60)
|
||||
|
||||
/*
|
||||
* Level 1 descriptor (PUD).
|
||||
|
@ -110,6 +112,8 @@
|
|||
#define PUD_TYPE_MASK (_AT(pudval_t, 3) << 0)
|
||||
#define PUD_TYPE_SECT (_AT(pudval_t, 1) << 0)
|
||||
#define PUD_SECT_RDONLY (_AT(pudval_t, 1) << 7) /* AP[2] */
|
||||
#define PUD_TABLE_PXN (_AT(pudval_t, 1) << 59)
|
||||
#define PUD_TABLE_UXN (_AT(pudval_t, 1) << 60)
|
||||
|
||||
/*
|
||||
* Level 2 descriptor (PMD).
|
||||
|
@ -131,6 +135,8 @@
|
|||
#define PMD_SECT_CONT (_AT(pmdval_t, 1) << 52)
|
||||
#define PMD_SECT_PXN (_AT(pmdval_t, 1) << 53)
|
||||
#define PMD_SECT_UXN (_AT(pmdval_t, 1) << 54)
|
||||
#define PMD_TABLE_PXN (_AT(pmdval_t, 1) << 59)
|
||||
#define PMD_TABLE_UXN (_AT(pmdval_t, 1) << 60)
|
||||
|
||||
/*
|
||||
* AttrIndx[2:0] encoding (mapping attributes defined in the MAIR* registers).
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
|
||||
#define NO_BLOCK_MAPPINGS BIT(0)
|
||||
#define NO_CONT_MAPPINGS BIT(1)
|
||||
#define NO_EXEC_MAPPINGS BIT(2) /* assumes FEAT_HPDS is not used */
|
||||
|
||||
u64 idmap_t0sz = TCR_T0SZ(VA_BITS_MIN);
|
||||
u64 idmap_ptrs_per_pgd = PTRS_PER_PGD;
|
||||
|
@ -185,10 +186,14 @@ static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr,
|
|||
|
||||
BUG_ON(pmd_sect(pmd));
|
||||
if (pmd_none(pmd)) {
|
||||
pmdval_t pmdval = PMD_TYPE_TABLE | PMD_TABLE_UXN;
|
||||
phys_addr_t pte_phys;
|
||||
|
||||
if (flags & NO_EXEC_MAPPINGS)
|
||||
pmdval |= PMD_TABLE_PXN;
|
||||
BUG_ON(!pgtable_alloc);
|
||||
pte_phys = pgtable_alloc(PAGE_SHIFT);
|
||||
__pmd_populate(pmdp, pte_phys, PMD_TYPE_TABLE);
|
||||
__pmd_populate(pmdp, pte_phys, pmdval);
|
||||
pmd = READ_ONCE(*pmdp);
|
||||
}
|
||||
BUG_ON(pmd_bad(pmd));
|
||||
|
@ -259,10 +264,14 @@ static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr,
|
|||
*/
|
||||
BUG_ON(pud_sect(pud));
|
||||
if (pud_none(pud)) {
|
||||
pudval_t pudval = PUD_TYPE_TABLE | PUD_TABLE_UXN;
|
||||
phys_addr_t pmd_phys;
|
||||
|
||||
if (flags & NO_EXEC_MAPPINGS)
|
||||
pudval |= PUD_TABLE_PXN;
|
||||
BUG_ON(!pgtable_alloc);
|
||||
pmd_phys = pgtable_alloc(PMD_SHIFT);
|
||||
__pud_populate(pudp, pmd_phys, PUD_TYPE_TABLE);
|
||||
__pud_populate(pudp, pmd_phys, pudval);
|
||||
pud = READ_ONCE(*pudp);
|
||||
}
|
||||
BUG_ON(pud_bad(pud));
|
||||
|
@ -306,10 +315,14 @@ static void alloc_init_pud(pgd_t *pgdp, unsigned long addr, unsigned long end,
|
|||
p4d_t p4d = READ_ONCE(*p4dp);
|
||||
|
||||
if (p4d_none(p4d)) {
|
||||
p4dval_t p4dval = P4D_TYPE_TABLE | P4D_TABLE_UXN;
|
||||
phys_addr_t pud_phys;
|
||||
|
||||
if (flags & NO_EXEC_MAPPINGS)
|
||||
p4dval |= P4D_TABLE_PXN;
|
||||
BUG_ON(!pgtable_alloc);
|
||||
pud_phys = pgtable_alloc(PUD_SHIFT);
|
||||
__p4d_populate(p4dp, pud_phys, P4D_TYPE_TABLE);
|
||||
__p4d_populate(p4dp, pud_phys, p4dval);
|
||||
p4d = READ_ONCE(*p4dp);
|
||||
}
|
||||
BUG_ON(p4d_bad(p4d));
|
||||
|
@ -486,14 +499,24 @@ early_param("crashkernel", enable_crash_mem_map);
|
|||
|
||||
static void __init map_mem(pgd_t *pgdp)
|
||||
{
|
||||
static const u64 direct_map_end = _PAGE_END(VA_BITS_MIN);
|
||||
phys_addr_t kernel_start = __pa_symbol(_stext);
|
||||
phys_addr_t kernel_end = __pa_symbol(__init_begin);
|
||||
phys_addr_t start, end;
|
||||
int flags = 0;
|
||||
int flags = NO_EXEC_MAPPINGS;
|
||||
u64 i;
|
||||
|
||||
/*
|
||||
* Setting hierarchical PXNTable attributes on table entries covering
|
||||
* the linear region is only possible if it is guaranteed that no table
|
||||
* entries at any level are being shared between the linear region and
|
||||
* the vmalloc region. Check whether this is true for the PGD level, in
|
||||
* which case it is guaranteed to be true for all other levels as well.
|
||||
*/
|
||||
BUILD_BUG_ON(pgd_index(direct_map_end - 1) == pgd_index(direct_map_end));
|
||||
|
||||
if (rodata_full || crash_mem_map || debug_pagealloc_enabled())
|
||||
flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
|
||||
flags |= NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
|
||||
|
||||
/*
|
||||
* Take care not to create a writable alias for the
|
||||
|
@ -1463,7 +1486,7 @@ struct range arch_get_mappable_range(void)
|
|||
int arch_add_memory(int nid, u64 start, u64 size,
|
||||
struct mhp_params *params)
|
||||
{
|
||||
int ret, flags = 0;
|
||||
int ret, flags = NO_EXEC_MAPPINGS;
|
||||
|
||||
VM_BUG_ON(!mhp_range_allowed(start, size, true));
|
||||
|
||||
|
@ -1473,7 +1496,7 @@ int arch_add_memory(int nid, u64 start, u64 size,
|
|||
*/
|
||||
if (rodata_full || debug_pagealloc_enabled() ||
|
||||
IS_ENABLED(CONFIG_KFENCE))
|
||||
flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
|
||||
flags |= NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS;
|
||||
|
||||
__create_pgd_mapping(swapper_pg_dir, start, __phys_to_virt(start),
|
||||
size, params->pgprot, __pgd_pgtable_alloc,
|
||||
|
|
Загрузка…
Ссылка в новой задаче