arm64: use XN table mappings for the linear region
cover arm64: use XN table mappings for the linear region
This patch tweaks the kernel page table population code to set the UXNTable and PXNTable bits on all table entries that cover the linear region. This removes the ability for lower level mappings to grant executable permissions which are never needed in the linear region. And given that swapper's PGD level is mapped r/o and can only be updated via the fixmap API, this cannot be trivially reverted by poking writable memory.
This does not address a known exploit or vulnerability, but it applies the principle of least privilege in a way that does not result in any space or runtime overhead.
Ard Biesheuvel (2):
arm64: mm: add missing P4D definitions and use them consistently
arm64: mm: use XN table mapping attributes for the linear region
arch/arm64/include/asm/pgtable-hwdef.h | 12 ++++++++ arch/arm64/mm/mmu.c | 31 ++++++++++++++------ 2 files changed, 34 insertions(+), 9 deletions(-)
Signature · 7 lines
2.20.1 _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH 1/2 arm64: mm: add missing P4D definitions and use them consistently
Even though level 0, 1 and 2 descriptors share the same attribute encodings, let's be a bit more consistent about using the right one at the right level. So add new macros for level 0/P4D definitions, and clean up some inconsistencies involving these macros.
--- arch/arm64/include/asm/pgtable-hwdef.h | 9 +++++++++ arch/arm64/mm/mmu.c | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-)
arch/arm64/include/asm/pgtable-hwdef.h +9 -0
@@ -94,6 +94,15 @@/* * Hardware page table definitions. * +* Level 0 descriptor (P4D). +*/ +#define P4D_TYPE_TABLE (_AT(p4dval_t, 3) << 0) +#define P4D_TABLE_BIT (_AT(p4dval_t, 1) << 1) +#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] */ + +/* * Level 1 descriptor (PUD). */ #define PUD_TYPE_TABLE (_AT(pudval_t, 3) << 0)
arch/arm64/mm/mmu.c +3 -3
@@ -309,7 +309,7 @@ static void alloc_init_pud(pgd_t *pgdp, unsigned long addr, unsigned long end,phys_addr_t pud_phys;BUG_ON (!pgtable_alloc); pud_phys =pgtable_alloc (PUD_SHIFT ); -__p4d_populate (p4dp, pud_phys,PUD_TYPE_TABLE ); +__p4d_populate (p4dp, pud_phys,P4D_TYPE_TABLE ); p4d =READ_ONCE (*p4dp); }BUG_ON (p4d_bad (p4d));
@@ -1209,11 +1209,11 @@ void __init early_fixmap_init(void) pudp =pud_offset_kimg (p4dp, addr); }else {if (p4d_none (p4d)) -__p4d_populate (p4dp,__pa_symbol (bm_pud),PUD_TYPE_TABLE ); +__p4d_populate (p4dp,__pa_symbol (bm_pud),P4D_TYPE_TABLE ); pudp =fixmap_pud (addr); }if (pud_none (READ_ONCE (*pudp))) -__pud_populate (pudp,__pa_symbol (bm_pmd),PMD_TYPE_TABLE ); +__pud_populate (pudp,__pa_symbol (bm_pmd),PUD_TYPE_TABLE ); pmdp =fixmap_pmd (addr);__pmd_populate (pmdp,__pa_symbol (bm_pte),PMD_TYPE_TABLE );
Signature · 7 lines
2.20.1 _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Re: [PATCH 1/2] arm64: mm: add missing P4D definitions and use them consistently
Quoted 6 lines · Ard Biesheuvel
Even though level 0, 1 and 2 descriptors share the same attribute encodings, let's be a bit more consistent about using the right one at the right level. So add new macros for level 0/P4D definitions, and clean up some inconsistencies involving these macros.
Signed-off-by Ard Biesheuvel <[email protected]>
Quoted 55 lines
--- arch/arm64/include/asm/pgtable-hwdef.h | 9 +++++++++ arch/arm64/mm/mmu.c | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-)arch/arm64/include/asm/pgtable-hwdef.h +9
-0@@ -94,6 +94,15 @@/* * Hardware page table definitions. * +* Level 0 descriptor (P4D). +*/ +#define P4D_TYPE_TABLE (_AT(p4dval_t, 3) << 0) +#define P4D_TABLE_BIT (_AT(p4dval_t, 1) << 1) +#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] */ + +/* * Level 1 descriptor (PUD). */ #define PUD_TYPE_TABLE (_AT(pudval_t, 3) << 0)arch/arm64/mm/mmu.c +3
-3@@ -309,7 +309,7 @@ static void alloc_init_pud(pgd_t *pgdp, unsigned long addr, unsigned long end,phys_addr_t pud_phys;BUG_ON (!pgtable_alloc); pud_phys =pgtable_alloc (PUD_SHIFT ); -__p4d_populate (p4dp, pud_phys,PUD_TYPE_TABLE ); +__p4d_populate (p4dp, pud_phys,P4D_TYPE_TABLE ); p4d =READ_ONCE (*p4dp); }BUG_ON (p4d_bad (p4d));@@ -1209,11 +1209,11 @@ void __init early_fixmap_init(void) pudp =pud_offset_kimg (p4dp, addr); }else {if (p4d_none (p4d)) -__p4d_populate (p4dp,__pa_symbol (bm_pud),PUD_TYPE_TABLE ); +__p4d_populate (p4dp,__pa_symbol (bm_pud),P4D_TYPE_TABLE ); pudp =fixmap_pud (addr); }if (pud_none (READ_ONCE (*pudp))) -__pud_populate (pudp,__pa_symbol (bm_pmd),PMD_TYPE_TABLE ); +__pud_populate (pudp,__pa_symbol (bm_pmd),PUD_TYPE_TABLE ); pmdp =fixmap_pmd (addr);__pmd_populate (pmdp,__pa_symbol (bm_pte),PMD_TYPE_TABLE );Signature · 1 line
2.20.1
_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Re: [PATCH 1/2] arm64: mm: add missing P4D definitions and use them consistently
In reply to Ard BiesheuvelQuoted 49 lines · Ard Biesheuvel
Even though level 0, 1 and 2 descriptors share the same attribute encodings, let's be a bit more consistent about using the right one at the right level. So add new macros for level 0/P4D definitions, and clean up some inconsistencies involving these macros.
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/include/asm/pgtable-hwdef.h | 9 +++++++++ arch/arm64/mm/mmu.c | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-)arch/arm64/include/asm/pgtable-hwdef.h +9
-0@@ -94,6 +94,15 @@/* * Hardware page table definitions. * +* Level 0 descriptor (P4D). +*/ +#define P4D_TYPE_TABLE (_AT(p4dval_t, 3) << 0) +#define P4D_TABLE_BIT (_AT(p4dval_t, 1) << 1) +#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] */ + +/* * Level 1 descriptor (PUD). */ #define PUD_TYPE_TABLE (_AT(pudval_t, 3) << 0)arch/arm64/mm/mmu.c +1
-1@@ -309,7 +309,7 @@ static void alloc_init_pud(pgd_t *pgdp, unsigned long addr, unsigned long end,phys_addr_t pud_phys;BUG_ON (!pgtable_alloc); pud_phys =pgtable_alloc (PUD_SHIFT ); -__p4d_populate (p4dp, pud_phys,PUD_TYPE_TABLE ); +__p4d_populate (p4dp, pud_phys,P4D_TYPE_TABLE ); p4d =READ_ONCE (*p4dp); }BUG_ON (p4d_bad (p4d));@@ -1209,11 +1209,11 @@ void __init early_fixmap_init(void) pudp =pud_offset_kimg (p4dp, addr); }else {if (p4d_none (p4d))
arch/arm64/mm/mmu.c void __init early_fixmap_init(void) · line 1219
Quoted 10 lines
arch/arm64/mm/mmu.c +2
-2-__p4d_populate (p4dp,__pa_symbol (bm_pud),PUD_TYPE_TABLE ); +__p4d_populate (p4dp,__pa_symbol (bm_pud),P4D_TYPE_TABLE ); pudp =fixmap_pud (addr); }if (pud_none (READ_ONCE (*pudp))) -__pud_populate (pudp,__pa_symbol (bm_pmd),PMD_TYPE_TABLE ); +__pud_populate (pudp,__pa_symbol (bm_pmd),PUD_TYPE_TABLE ); pmdp =fixmap_pmd (addr);__pmd_populate (pmdp,__pa_symbol (bm_pte),PMD_TYPE_TABLE );
I guess there are still some more similar inconsistencies around.
arch/arm64/include/asm/pgalloc.h +2 -2
@@ -27,7 +27,7 @@ static inline void __pud_populate(pud_t *pudp, phys_addr_t pmdp, pudval_t prot)static inline void pud_populate (struct mm_struct *mm,pud_t *pudp,pmd_t *pmdp) { -__pud_populate (pudp,__pa (pmdp),PMD_TYPE_TABLE ); +__pud_populate (pudp,__pa (pmdp),PUD_TYPE_TABLE ); }#else static inline void __pud_populate (pud_t *pudp,phys_addr_t pmdp,pudval_t prot)
@@ -45,7 +45,7 @@ static inline void __p4d_populate(p4d_t *p4dp, phys_addr_t pudp, p4dval_t prot)static inline void p4d_populate (struct mm_struct *mm,p4d_t *p4dp,pud_t *pudp) { -__p4d_populate (p4dp,__pa (pudp),PUD_TYPE_TABLE ); +__p4d_populate (p4dp,__pa (pudp),P4D_TYPE_TABLE ); }#else static inline void __p4d_populate (p4d_t *p4dp,phys_addr_t pudp,p4dval_t prot)
arch/arm64/mm/kasan_init.c +2 -2
@@ -79,7 +79,7 @@ static pmd_t *__init kasan_pmd_offset(pud_t *pudp, unsigned long addr, int node,phys_addr_t pmd_phys = early ?__pa_symbol (kasan_early_shadow_pmd) :kasan_alloc_zeroed_page (node); -__pud_populate (pudp, pmd_phys,PMD_TYPE_TABLE ); +__pud_populate (pudp, pmd_phys,PUD_TYPE_TABLE ); }return early ?pmd_offset_kimg (pudp, addr) :pmd_offset (pudp, addr);
@@ -92,7 +92,7 @@ static pud_t *__init kasan_pud_offset(p4d_t *p4dp, unsigned long addr, int node,phys_addr_t pud_phys = early ?__pa_symbol (kasan_early_shadow_pud) :kasan_alloc_zeroed_page (node); -__p4d_populate (p4dp, pud_phys,PMD_TYPE_TABLE ); +__p4d_populate (p4dp, pud_phys,P4D_TYPE_TABLE ); }return early ?pud_offset_kimg (p4dp, addr) :pud_offset (p4dp, addr);
_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Re: [PATCH 1/2] arm64: mm: add missing P4D definitions and use them consistently
Quoted 57 lines · Anshuman Khandual
Quoted 53 lines · Ard Biesheuvel
Even though level 0, 1 and 2 descriptors share the same attribute encodings, let's be a bit more consistent about using the right one at the right level. So add new macros for level 0/P4D definitions, and clean up some inconsistencies involving these macros.
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/include/asm/pgtable-hwdef.h | 9 +++++++++ arch/arm64/mm/mmu.c | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-)arch/arm64/include/asm/pgtable-hwdef.h +9
-0@@ -94,6 +94,15 @@/* * Hardware page table definitions. * +* Level 0 descriptor (P4D). +*/ +#define P4D_TYPE_TABLE (_AT(p4dval_t, 3) << 0) +#define P4D_TABLE_BIT (_AT(p4dval_t, 1) << 1) +#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] */ + +/* * Level 1 descriptor (PUD). */ #define PUD_TYPE_TABLE (_AT(pudval_t, 3) << 0)arch/arm64/mm/mmu.c +2
-2@@ -309,7 +309,7 @@ static void alloc_init_pud(pgd_t *pgdp, unsigned long addr, unsigned long end,phys_addr_t pud_phys;BUG_ON (!pgtable_alloc); pud_phys =pgtable_alloc (PUD_SHIFT ); -__p4d_populate (p4dp, pud_phys,PUD_TYPE_TABLE ); +__p4d_populate (p4dp, pud_phys,P4D_TYPE_TABLE ); p4d =READ_ONCE (*p4dp); }BUG_ON (p4d_bad (p4d));@@ -1209,11 +1209,11 @@ void __init early_fixmap_init(void) pudp =pud_offset_kimg (p4dp, addr); }else {if (p4d_none (p4d)) -__p4d_populate (p4dp,__pa_symbol (bm_pud),PUD_TYPE_TABLE ); +__p4d_populate (p4dp,__pa_symbol (bm_pud),P4D_TYPE_TABLE ); pudp =fixmap_pud (addr); }if (pud_none (READ_ONCE (*pudp)))
Quoted 9 lines
arch/arm64/mm/mmu.c void __init early_fixmap_init(void) · line 1219
Quoted 6 lines
arch/arm64/mm/mmu.c +1
-1-__pud_populate (pudp,__pa_symbol (bm_pmd),PMD_TYPE_TABLE ); +__pud_populate (pudp,__pa_symbol (bm_pmd),PUD_TYPE_TABLE ); pmdp =fixmap_pmd (addr);__pmd_populate (pmdp,__pa_symbol (bm_pte),PMD_TYPE_TABLE );I guess there are still some more similar inconsistencies around.
Indeed - I will fix those as well in v2.
Thanks,
Ard.
Quoted 36 lines
arch/arm64/include/asm/pgalloc.h +2
-2@@ -27,7 +27,7 @@ static inline void __pud_populate(pud_t *pudp, phys_addr_t pmdp, pudval_t prot)static inline void pud_populate (struct mm_struct *mm,pud_t *pudp,pmd_t *pmdp) { -__pud_populate (pudp,__pa (pmdp),PMD_TYPE_TABLE ); +__pud_populate (pudp,__pa (pmdp),PUD_TYPE_TABLE ); }#else static inline void __pud_populate (pud_t *pudp,phys_addr_t pmdp,pudval_t prot)@@ -45,7 +45,7 @@ static inline void __p4d_populate(p4d_t *p4dp, phys_addr_t pudp, p4dval_t prot)static inline void p4d_populate (struct mm_struct *mm,p4d_t *p4dp,pud_t *pudp) { -__p4d_populate (p4dp,__pa (pudp),PUD_TYPE_TABLE ); +__p4d_populate (p4dp,__pa (pudp),P4D_TYPE_TABLE ); }#else static inline void __p4d_populate (p4d_t *p4dp,phys_addr_t pudp,p4dval_t prot)arch/arm64/mm/kasan_init.c +1
-1@@ -79,7 +79,7 @@ static pmd_t *__init kasan_pmd_offset(pud_t *pudp, unsigned long addr, int node,phys_addr_t pmd_phys = early ?__pa_symbol (kasan_early_shadow_pmd) :kasan_alloc_zeroed_page (node); -__pud_populate (pudp, pmd_phys,PMD_TYPE_TABLE ); +__pud_populate (pudp, pmd_phys,PUD_TYPE_TABLE ); }return early ?pmd_offset_kimg (pudp, addr) :pmd_offset (pudp, addr);
arch/arm64/mm/kasan_init.c static pud_t *__init kasan_pud_offset(p4d_t *p4dp, unsigned long addr, int node, · line 98
Quoted 8 lines
arch/arm64/mm/kasan_init.c +1
-1@@ -92,7 +92,7 @@ static pud_t *__init kasan_pud_offset(p4d_t *p4dp, unsigned long addr, int node,phys_addr_t pud_phys = early ?__pa_symbol (kasan_early_shadow_pud) :kasan_alloc_zeroed_page (node); -__p4d_populate (p4dp, pud_phys,PMD_TYPE_TABLE ); +__p4d_populate (p4dp, pud_phys,P4D_TYPE_TABLE ); }return early ?pud_offset_kimg (p4dp, addr) :pud_offset (p4dp, addr);
_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
PATCH 2/2 arm64: mm: use XN table mapping attributes for the linear region
In reply to Ard BiesheuvelThe 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/UXN 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.
--- arch/arm64/include/asm/pgtable-hwdef.h | 3 +++ arch/arm64/mm/mmu.c | 27 +++++++++++++++----- 2 files changed, 23 insertions(+), 7 deletions(-)
arch/arm64/include/asm/pgtable-hwdef.h +3 -0
@@ -101,6 +101,7 @@#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_XN (_AT(p4dval_t, 3) << 59)/* UXN:PXN */ /* * Level 1 descriptor (PUD).
@@ -110,6 +111,7 @@#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_XN (_AT(pudval_t, 3) << 59)/* UXN:PXN */ /* * Level 2 descriptor (PMD).
@@ -131,6 +133,7 @@#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_XN (_AT(pmdval_t, 3) << 59)/* UXN:PXN */ /* * AttrIndx[2:0] encoding (mapping attributes defined in the MAIR* registers).
arch/arm64/mm/mmu.c +20 -7
@@ -39,6 +39,7 @@#define NO_BLOCK_MAPPINGS BIT(0)#define NO_CONT_MAPPINGS BIT(1) +#define NO_EXEC_MAPPINGS BIT(2)u64 idmap_t0sz =TCR_T0SZ (VA_BITS );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 ;phys_addr_t pte_phys; + +if (flags &NO_EXEC_MAPPINGS ) + pmdval |=PMD_TABLE_XN ;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 ;phys_addr_t pmd_phys; + +if (flags &NO_EXEC_MAPPINGS ) + pudval |=PUD_TABLE_XN ;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 ;phys_addr_t pud_phys; + +if (flags &NO_EXEC_MAPPINGS ) + p4dval |=P4D_TABLE_XN ;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));
@@ -489,11 +502,11 @@ static void __init map_mem(pgd_t *pgdp)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;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
@@ -1462,7 +1475,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));
@@ -1472,7 +1485,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,
Signature · 7 lines
2.20.1 _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Re: [PATCH 2/2] arm64: mm: use XN table mapping attributes for the linear region
Quoted 24 lines · Ard Biesheuvel
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/UXN 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.
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/include/asm/pgtable-hwdef.h | 3 +++ arch/arm64/mm/mmu.c | 27 +++++++++++++++----- 2 files changed, 23 insertions(+), 7 deletions(-)arch/arm64/include/asm/pgtable-hwdef.h +0
-0
arch/arm64/include/asm/pgtable-hwdef.h line 104
Quoted 4 lines
arch/arm64/include/asm/pgtable-hwdef.h +1
-0@@ -101,6 +101,7 @@#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_XN (_AT(p4dval_t, 3) << 59)/* UXN:PXN */
I reckon it's worth having separate P?D_TABLE_UXN and P?D_TABLE_PXN definitions, so that it's easier/possible to:
* Set UXNTable for *all* kernel mappings
* Set PXNTable for *all* user mappings
... though we don't need to do either immediately.
Otherwise, this looks good to me, and with or without that split:
Mark.
Quoted 114 lines
arch/arm64/include/asm/pgtable-hwdef.h +2
-0/* * Level 1 descriptor (PUD). @@ -110,6 +111,7 @@#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_XN (_AT(pudval_t, 3) << 59)/* UXN:PXN */ /* * Level 2 descriptor (PMD). @@ -131,6 +133,7 @@#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_XN (_AT(pmdval_t, 3) << 59)/* UXN:PXN */ /* * AttrIndx[2:0] encoding (mapping attributes defined in the MAIR* registers). arch/arm64/mm/mmu.c +20
-7@@ -39,6 +39,7 @@#define NO_BLOCK_MAPPINGS BIT(0)#define NO_CONT_MAPPINGS BIT(1) +#define NO_EXEC_MAPPINGS BIT(2)u64 idmap_t0sz =TCR_T0SZ (VA_BITS );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 ;phys_addr_t pte_phys; + +if (flags &NO_EXEC_MAPPINGS ) + pmdval |=PMD_TABLE_XN ;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 ;phys_addr_t pmd_phys; + +if (flags &NO_EXEC_MAPPINGS ) + pudval |=PUD_TABLE_XN ;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 ;phys_addr_t pud_phys; + +if (flags &NO_EXEC_MAPPINGS ) + p4dval |=P4D_TABLE_XN ;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));@@ -489,11 +502,11 @@ static void __init map_mem(pgd_t *pgdp)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;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 @@ -1462,7 +1475,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));@@ -1472,7 +1485,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,Signature · 1 line
2.20.1
_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Re: [PATCH 2/2] arm64: mm: use XN table mapping attributes for the linear region
Quoted 36 lines · Mark Rutland
Quoted 24 lines · Ard Biesheuvel
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/UXN 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.
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/include/asm/pgtable-hwdef.h | 3 +++ arch/arm64/mm/mmu.c | 27 +++++++++++++++----- 2 files changed, 23 insertions(+), 7 deletions(-)arch/arm64/include/asm/pgtable-hwdef.h +0
-0arch/arm64/include/asm/pgtable-hwdef.h line 104
Quoted 4 lines
arch/arm64/include/asm/pgtable-hwdef.h +1
-0@@ -101,6 +101,7 @@#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_XN (_AT(p4dval_t, 3) << 59)/* UXN:PXN */ I reckon it's worth having separate P?D_TABLE_UXN and P?D_TABLE_PXN definitions, so that it's easier/possible to:
* Set UXNTable for *all* kernel mappings
Quoted 8 lines
* Set PXNTable for *all* user mappings
... though we don't need to do either immediately.
Otherwise, this looks good to me, and with or without that split:
Acked-by Mark Rutland <[email protected]>
Yeah, it's probably better to split them right away.
Quoted 109 lines
Quoted 109 lines
arch/arm64/include/asm/pgtable-hwdef.h +2
-0/* * Level 1 descriptor (PUD). @@ -110,6 +111,7 @@#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_XN (_AT(pudval_t, 3) << 59)/* UXN:PXN */ /* * Level 2 descriptor (PMD). @@ -131,6 +133,7 @@#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_XN (_AT(pmdval_t, 3) << 59)/* UXN:PXN */ /* * AttrIndx[2:0] encoding (mapping attributes defined in the MAIR* registers). arch/arm64/mm/mmu.c +20
-7@@ -39,6 +39,7 @@#define NO_BLOCK_MAPPINGS BIT(0)#define NO_CONT_MAPPINGS BIT(1) +#define NO_EXEC_MAPPINGS BIT(2)u64 idmap_t0sz =TCR_T0SZ (VA_BITS );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 ;phys_addr_t pte_phys; + +if (flags &NO_EXEC_MAPPINGS ) + pmdval |=PMD_TABLE_XN ;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 ;phys_addr_t pmd_phys; + +if (flags &NO_EXEC_MAPPINGS ) + pudval |=PUD_TABLE_XN ;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 ;phys_addr_t pud_phys; + +if (flags &NO_EXEC_MAPPINGS ) + p4dval |=P4D_TABLE_XN ;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));@@ -489,11 +502,11 @@ static void __init map_mem(pgd_t *pgdp)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;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 @@ -1462,7 +1475,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));@@ -1472,7 +1485,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 ;
Quoted 5 lines
Quoted 5 lines
arch/arm64/mm/mmu.c +0
-1__create_pgd_mapping(swapper_pg_dir, start, __phys_to_virt(start), size, params->pgprot, __pgd_pgtable_alloc, --2.20.1
_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Catalin Marinas
maintainerRe: [PATCH 2/2] arm64: mm: use XN table mapping attributes for the linear region
In reply to Ard BiesheuvelQuoted 5 lines · Ard Biesheuvel
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.
Quoted 7 lines
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/UXN 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.
In ARMv8.1 the architecture added the possibility of disabling the hierarchical page table permissions (FEAT_HPDS) so that we can use these bits for software.
Is there any big advantage to using the hierarchical permissions vs some sanity check in set_pte() for example?
Signature · 6 lines
Catalin _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Re: [PATCH 2/2] arm64: mm: use XN table mapping attributes for the linear region
Quoted 15 lines · Catalin Marinas
Quoted 5 lines · Ard Biesheuvel
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.
Quoted 7 lines
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/UXN 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.
Quoted 4 lines
In ARMv8.1 the architecture added the possibility of disabling the hierarchical page table permissions (
FEAT_HPDS) so that we can use these bits for software.
Sure, but I don't think there is a shortage of software bits in table descriptors, right? And we don't enable the feature in the first place.
Quoted 3 lines
Is there any big advantage to using the hierarchical permissions vs some sanity check in
set_pte()for example?
There is a big advantage: the fact that the permissions are both hierarchical and subtractive.
Sanity checks in set_pte() only cover page mappings that were created in the correct way. But that does not help us if an attacker manages a single 64-bit write that creates a page or table entry pointing to a page under their control. Taking away the exec permissions at the levels above makes it much more difficult to carry out such an attack, especially given that the root level is not mapped read-write to begin with.
_______________________________________________
linux-arm-kernel mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Catalin Marinas
maintainerRe: [PATCH 2/2] arm64: mm: use XN table mapping attributes for the linear region
Quoted 15 lines · Ard Biesheuvel
Quoted 14 lines · Catalin Marinas
Quoted 12 lines · Ard Biesheuvel
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/UXN 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.
Quoted 7 lines
Quoted 3 lines
In ARMv8.1 the architecture added the possibility of disabling the hierarchical page table permissions (
FEAT_HPDS) so that we can use these bits for software.Sure, but I don't think there is a shortage of software bits in table descriptors, right? And we don't enable the feature in the first place.
We are short of software bits but in the *pte*, so disabling the hierarchical permissions doesn't anyway help. So, ignore me, the patches are fine ;).
Signature · 6 lines
Catalin _______________________________________________ linux-arm-kernel mailing list [email protected] http://lists.infradead.org/mailman/listinfo/linux-arm-kernel