Re: [PATCH 05/11] x86/mm: do not auto-massage page protections
From: Tom Lendacky
Date: Thu Apr 05 2018 - 15:49:33 EST
On 4/3/2018 8:09 PM, Dave Hansen wrote:
>
> From: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx>
>
> A PTE is constructed from a physical address and a pgprotval_t.
> __PAGE_KERNEL, for instance, is a pgprot_t and must be converted
> into a pgprotval_t before it can be used to create a PTE. This is
> done implicitly within functions like pfn_pte() by massage_pgprot().
>
> However, this makes it very challenging to set bits (and keep them
> set) if your bit is being filtered out by massage_pgprot().
>
> This moves the bit filtering out of pfn_pte() and friends. For
> users of PAGE_KERNEL*, filtering will be done automatically inside
> those macros but for users of __PAGE_KERNEL*, they need to do their
> own filtering now.
>
> Note that we also just move pfn_pte/pmd/pud() over to check_pgprot()
> instead of massage_pgprot(). This way, we still *look* for
> unsupported bits and properly warn about them if we find them. This
> might happen if an unfiltered __PAGE_KERNEL* value was passed in,
> for instance.
>
> Signed-off-by: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx>
> Cc: Andrea Arcangeli <aarcange@xxxxxxxxxx>
> Cc: Andy Lutomirski <luto@xxxxxxxxxx>
> Cc: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
> Cc: Kees Cook <keescook@xxxxxxxxxx>
> Cc: Hugh Dickins <hughd@xxxxxxxxxx>
> Cc: Juergen Gross <jgross@xxxxxxxx>
> Cc: x86@xxxxxxxxxx
> Cc: Nadav Amit <namit@xxxxxxxxxx>
> ---
>
> b/arch/x86/include/asm/pgtable.h | 27 ++++++++++++++++++++++-----
> b/arch/x86/kernel/head64.c | 2 ++
> b/arch/x86/kernel/ldt.c | 6 +++++-
> b/arch/x86/mm/ident_map.c | 3 +++
> b/arch/x86/mm/iomap_32.c | 6 ++++++
> b/arch/x86/mm/ioremap.c | 3 +++
> b/arch/x86/mm/kasan_init_64.c | 14 +++++++++++++-
> b/arch/x86/mm/pgtable.c | 3 +++
> b/arch/x86/power/hibernate_64.c | 20 +++++++++++++++-----
> 9 files changed, 72 insertions(+), 12 deletions(-)
>
This fails to build for me when I enable KASLR (RANDOMIZE_BASE), with the
following error:
arch/x86/boot/compressed/kaslr_64.o: In function `kernel_ident_mapping_init':
kaslr_64.c:(.text+0x245): undefined reference to `__default_kernel_pte_mask'
make[2]: *** [arch/x86/boot/compressed/vmlinux] Error 1
make[1]: *** [arch/x86/boot/compressed/vmlinux] Error 2
make: *** [bzImage] Error 2
Thanks,
Tom
> diff -puN arch/x86/include/asm/pgtable.h~x86-no-auto-massage arch/x86/include/asm/pgtable.h
> --- a/arch/x86/include/asm/pgtable.h~x86-no-auto-massage 2018-04-02 16:41:14.811605173 -0700
> +++ b/arch/x86/include/asm/pgtable.h 2018-04-02 16:41:14.829605173 -0700
> @@ -526,22 +526,39 @@ static inline pgprotval_t massage_pgprot
> return protval;
> }
>
> +static inline pgprotval_t check_pgprot(pgprot_t pgprot)
> +{
> + pgprotval_t massaged_val = massage_pgprot(pgprot);
> +
> + /* mmdebug.h can not be included here because of dependencies */
> +#ifdef CONFIG_DEBUG_VM
> + WARN_ONCE(pgprot_val(pgprot) != massaged_val,
> + "attempted to set unsupported pgprot: %016lx "
> + "bits: %016lx supported: %016lx\n",
> + pgprot_val(pgprot),
> + pgprot_val(pgprot) ^ massaged_val,
> + __supported_pte_mask);
> +#endif
> +
> + return massaged_val;
> +}
> +
> static inline pte_t pfn_pte(unsigned long page_nr, pgprot_t pgprot)
> {
> return __pte(((phys_addr_t)page_nr << PAGE_SHIFT) |
> - massage_pgprot(pgprot));
> + check_pgprot(pgprot));
> }
>
> static inline pmd_t pfn_pmd(unsigned long page_nr, pgprot_t pgprot)
> {
> return __pmd(((phys_addr_t)page_nr << PAGE_SHIFT) |
> - massage_pgprot(pgprot));
> + check_pgprot(pgprot));
> }
>
> static inline pud_t pfn_pud(unsigned long page_nr, pgprot_t pgprot)
> {
> return __pud(((phys_addr_t)page_nr << PAGE_SHIFT) |
> - massage_pgprot(pgprot));
> + check_pgprot(pgprot));
> }
>
> static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
> @@ -553,7 +570,7 @@ static inline pte_t pte_modify(pte_t pte
> * the newprot (if present):
> */
> val &= _PAGE_CHG_MASK;
> - val |= massage_pgprot(newprot) & ~_PAGE_CHG_MASK;
> + val |= check_pgprot(newprot) & ~_PAGE_CHG_MASK;
>
> return __pte(val);
> }
> @@ -563,7 +580,7 @@ static inline pmd_t pmd_modify(pmd_t pmd
> pmdval_t val = pmd_val(pmd);
>
> val &= _HPAGE_CHG_MASK;
> - val |= massage_pgprot(newprot) & ~_HPAGE_CHG_MASK;
> + val |= check_pgprot(newprot) & ~_HPAGE_CHG_MASK;
>
> return __pmd(val);
> }
> diff -puN arch/x86/kernel/head64.c~x86-no-auto-massage arch/x86/kernel/head64.c
> --- a/arch/x86/kernel/head64.c~x86-no-auto-massage 2018-04-02 16:41:14.813605173 -0700
> +++ b/arch/x86/kernel/head64.c 2018-04-02 16:41:14.830605173 -0700
> @@ -195,6 +195,8 @@ unsigned long __head __startup_64(unsign
> pud[i + 1] = (pudval_t)pmd + pgtable_flags;
>
> pmd_entry = __PAGE_KERNEL_LARGE_EXEC & ~_PAGE_GLOBAL;
> + /* Filter out unsupported __PAGE_KERNEL_* bits: */
> + pmd_entry &= __supported_pte_mask;
> pmd_entry += sme_get_me_mask();
> pmd_entry += physaddr;
>
> diff -puN arch/x86/kernel/ldt.c~x86-no-auto-massage arch/x86/kernel/ldt.c
> --- a/arch/x86/kernel/ldt.c~x86-no-auto-massage 2018-04-02 16:41:14.815605173 -0700
> +++ b/arch/x86/kernel/ldt.c 2018-04-02 16:41:14.830605173 -0700
> @@ -145,6 +145,7 @@ map_ldt_struct(struct mm_struct *mm, str
> unsigned long offset = i << PAGE_SHIFT;
> const void *src = (char *)ldt->entries + offset;
> unsigned long pfn;
> + pgprot_t pte_prot;
> pte_t pte, *ptep;
>
> va = (unsigned long)ldt_slot_va(slot) + offset;
> @@ -163,7 +164,10 @@ map_ldt_struct(struct mm_struct *mm, str
> * target via some kernel interface which misses a
> * permission check.
> */
> - pte = pfn_pte(pfn, __pgprot(__PAGE_KERNEL_RO & ~_PAGE_GLOBAL));
> + pte_prot = __pgprot(__PAGE_KERNEL_RO & ~_PAGE_GLOBAL);
> + /* Filter out unsuppored __PAGE_KERNEL* bits: */
> + pgprot_val(pte_prot) |= __supported_pte_mask;
> + pte = pfn_pte(pfn, pte_prot);
> set_pte_at(mm, va, ptep, pte);
> pte_unmap_unlock(ptep, ptl);
> }
> diff -puN arch/x86/mm/ident_map.c~x86-no-auto-massage arch/x86/mm/ident_map.c
> --- a/arch/x86/mm/ident_map.c~x86-no-auto-massage 2018-04-02 16:41:14.817605173 -0700
> +++ b/arch/x86/mm/ident_map.c 2018-04-02 16:41:14.830605173 -0700
> @@ -98,6 +98,9 @@ int kernel_ident_mapping_init(struct x86
> if (!info->kernpg_flag)
> info->kernpg_flag = _KERNPG_TABLE;
>
> + /* Filter out unsupported __PAGE_KERNEL_* bits: */
> + info->kernpg_flag &= __default_kernel_pte_mask;
> +
> for (; addr < end; addr = next) {
> pgd_t *pgd = pgd_page + pgd_index(addr);
> p4d_t *p4d;
> diff -puN arch/x86/mm/iomap_32.c~x86-no-auto-massage arch/x86/mm/iomap_32.c
> --- a/arch/x86/mm/iomap_32.c~x86-no-auto-massage 2018-04-02 16:41:14.818605173 -0700
> +++ b/arch/x86/mm/iomap_32.c 2018-04-02 16:41:14.830605173 -0700
> @@ -44,6 +44,9 @@ int iomap_create_wc(resource_size_t base
> return ret;
>
> *prot = __pgprot(__PAGE_KERNEL | cachemode2protval(pcm));
> + /* Filter out unsupported __PAGE_KERNEL* bits: */
> + pgprot_val(*prot) &= __default_kernel_pte_mask;
> +
> return 0;
> }
> EXPORT_SYMBOL_GPL(iomap_create_wc);
> @@ -88,6 +91,9 @@ iomap_atomic_prot_pfn(unsigned long pfn,
> prot = __pgprot(__PAGE_KERNEL |
> cachemode2protval(_PAGE_CACHE_MODE_UC_MINUS));
>
> + /* Filter out unsupported __PAGE_KERNEL* bits: */
> + pgprot_val(prot) &= __default_kernel_pte_mask;
> +
> return (void __force __iomem *) kmap_atomic_prot_pfn(pfn, prot);
> }
> EXPORT_SYMBOL_GPL(iomap_atomic_prot_pfn);
> diff -puN arch/x86/mm/ioremap.c~x86-no-auto-massage arch/x86/mm/ioremap.c
> --- a/arch/x86/mm/ioremap.c~x86-no-auto-massage 2018-04-02 16:41:14.820605173 -0700
> +++ b/arch/x86/mm/ioremap.c 2018-04-02 16:41:14.831605173 -0700
> @@ -816,6 +816,9 @@ void __init __early_set_fixmap(enum fixe
> }
> pte = early_ioremap_pte(addr);
>
> + /* Sanitize 'prot' against any unsupported bits: */
> + pgprot_val(flags) &= __default_kernel_pte_mask;
> +
> if (pgprot_val(flags))
> set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, flags));
> else
> diff -puN arch/x86/mm/kasan_init_64.c~x86-no-auto-massage arch/x86/mm/kasan_init_64.c
> --- a/arch/x86/mm/kasan_init_64.c~x86-no-auto-massage 2018-04-02 16:41:14.822605173 -0700
> +++ b/arch/x86/mm/kasan_init_64.c 2018-04-02 16:41:14.831605173 -0700
> @@ -269,6 +269,12 @@ void __init kasan_early_init(void)
> pudval_t pud_val = __pa_nodebug(kasan_zero_pmd) | _KERNPG_TABLE;
> p4dval_t p4d_val = __pa_nodebug(kasan_zero_pud) | _KERNPG_TABLE;
>
> + /* Mask out unsupported __PAGE_KERNEL bits: */
> + pte_val &= __default_kernel_pte_mask;
> + pmd_val &= __default_kernel_pte_mask;
> + pud_val &= __default_kernel_pte_mask;
> + p4d_val &= __default_kernel_pte_mask;
> +
> for (i = 0; i < PTRS_PER_PTE; i++)
> kasan_zero_pte[i] = __pte(pte_val);
>
> @@ -371,7 +377,13 @@ void __init kasan_init(void)
> */
> memset(kasan_zero_page, 0, PAGE_SIZE);
> for (i = 0; i < PTRS_PER_PTE; i++) {
> - pte_t pte = __pte(__pa(kasan_zero_page) | __PAGE_KERNEL_RO | _PAGE_ENC);
> + pte_t pte;
> + pgprot_t prot;
> +
> + prot = __pgprot(__PAGE_KERNEL_RO | _PAGE_ENC);
> + pgprot_val(prot) &= __default_kernel_pte_mask;
> +
> + pte = __pte(__pa(kasan_zero_page) | pgprot_val(prot));
> set_pte(&kasan_zero_pte[i], pte);
> }
> /* Flush TLBs again to be sure that write protection applied. */
> diff -puN arch/x86/mm/pgtable.c~x86-no-auto-massage arch/x86/mm/pgtable.c
> --- a/arch/x86/mm/pgtable.c~x86-no-auto-massage 2018-04-02 16:41:14.824605173 -0700
> +++ b/arch/x86/mm/pgtable.c 2018-04-02 16:41:14.831605173 -0700
> @@ -583,6 +583,9 @@ void __native_set_fixmap(enum fixed_addr
> void native_set_fixmap(enum fixed_addresses idx, phys_addr_t phys,
> pgprot_t flags)
> {
> + /* Sanitize 'prot' against any unsupported bits: */
> + pgprot_val(flags) &= __default_kernel_pte_mask;
> +
> __native_set_fixmap(idx, pfn_pte(phys >> PAGE_SHIFT, flags));
> }
>
> diff -puN arch/x86/power/hibernate_64.c~x86-no-auto-massage arch/x86/power/hibernate_64.c
> --- a/arch/x86/power/hibernate_64.c~x86-no-auto-massage 2018-04-02 16:41:14.826605173 -0700
> +++ b/arch/x86/power/hibernate_64.c 2018-04-02 16:41:14.831605173 -0700
> @@ -51,6 +51,12 @@ static int set_up_temporary_text_mapping
> pmd_t *pmd;
> pud_t *pud;
> p4d_t *p4d = NULL;
> + pgprot_t pgtable_prot = __pgprot(_KERNPG_TABLE);
> + pgprot_t pmd_text_prot = __pgprot(__PAGE_KERNEL_LARGE_EXEC);
> +
> + /* Filter out unsupported __PAGE_KERNEL* bits: */
> + pgprot_val(pmd_text_prot) &= __default_kernel_pte_mask;
> + pgprot_val(pgtable_prot) &= __default_kernel_pte_mask;
>
> /*
> * The new mapping only has to cover the page containing the image
> @@ -81,15 +87,19 @@ static int set_up_temporary_text_mapping
> return -ENOMEM;
>
> set_pmd(pmd + pmd_index(restore_jump_address),
> - __pmd((jump_address_phys & PMD_MASK) | __PAGE_KERNEL_LARGE_EXEC));
> + __pmd((jump_address_phys & PMD_MASK) | pgprot_val(pmd_text_prot)));
> set_pud(pud + pud_index(restore_jump_address),
> - __pud(__pa(pmd) | _KERNPG_TABLE));
> + __pud(__pa(pmd) | pgprot_val(pgtable_prot)));
> if (p4d) {
> - set_p4d(p4d + p4d_index(restore_jump_address), __p4d(__pa(pud) | _KERNPG_TABLE));
> - set_pgd(pgd + pgd_index(restore_jump_address), __pgd(__pa(p4d) | _KERNPG_TABLE));
> + p4d_t new_p4d = __p4d(__pa(pud) | pgprot_val(pgtable_prot));
> + pgd_t new_pgd = __pgd(__pa(p4d) | pgprot_val(pgtable_prot));
> +
> + set_p4d(p4d + p4d_index(restore_jump_address), new_p4d);
> + set_pgd(pgd + pgd_index(restore_jump_address), new_pgd);
> } else {
> /* No p4d for 4-level paging: point the pgd to the pud page table */
> - set_pgd(pgd + pgd_index(restore_jump_address), __pgd(__pa(pud) | _KERNPG_TABLE));
> + pgd_t new_pgd = __pgd(__pa(p4d) | pgprot_val(pgtable_prot));
> + set_pgd(pgd + pgd_index(restore_jump_address), new_pgd);
> }
>
> return 0;
> _
>