Skip to content

Commit e5136e8

Browse files
committed
mm: Warn on shadow stack memory in wrong vma
The x86 Control-flow Enforcement Technology (CET) feature includes a new type of memory called shadow stack. This shadow stack memory has some unusual properties, which requires some core mm changes to function properly. One sharp edge is that PTEs that are both Write=0 and Dirty=1 are treated as shadow by the CPU, but this combination used to be created by the kernel on x86. Previous patches have changed the kernel to now avoid creating these PTEs unless they are for shadow stack memory. In case any missed corners of the kernel are still creating PTEs like this for non-shadow stack memory, and to catch any re-introductions of the logic, warn if any shadow stack PTEs (Write=0, Dirty=1) are found in non-shadow stack VMAs when they are being zapped. This won't catch transient cases but should have decent coverage. In order to check if a PTE is shadow stack in core mm code, add two arch breakouts arch_check_zapped_pte/pmd(). This will allow shadow stack specific code to be kept in arch/x86. Only do the check if shadow stack is supported by the CPU and configured because in rare cases older CPUs may write Dirty=1 to a Write=0 CPU on older CPUs. This check is handled in pte_shstk()/pmd_shstk(). Signed-off-by: Rick Edgecombe <[email protected]> Signed-off-by: Dave Hansen <[email protected]> Reviewed-by: Mark Brown <[email protected]> Acked-by: Mike Rapoport (IBM) <[email protected]> Tested-by: Pengfei Xu <[email protected]> Tested-by: John Allen <[email protected]> Tested-by: Kees Cook <[email protected]> Link: https://lore.kernel.org/all/20230613001108.3040476-18-rick.p.edgecombe%40intel.com
1 parent 0266e7c commit e5136e8

File tree

5 files changed

+42
-0
lines changed

5 files changed

+42
-0
lines changed

arch/x86/include/asm/pgtable.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1665,6 +1665,12 @@ static inline bool arch_has_hw_pte_young(void)
16651665
return true;
16661666
}
16671667

1668+
#define arch_check_zapped_pte arch_check_zapped_pte
1669+
void arch_check_zapped_pte(struct vm_area_struct *vma, pte_t pte);
1670+
1671+
#define arch_check_zapped_pmd arch_check_zapped_pmd
1672+
void arch_check_zapped_pmd(struct vm_area_struct *vma, pmd_t pmd);
1673+
16681674
#ifdef CONFIG_XEN_PV
16691675
#define arch_has_hw_nonleaf_pmd_young arch_has_hw_nonleaf_pmd_young
16701676
static inline bool arch_has_hw_nonleaf_pmd_young(void)

arch/x86/mm/pgtable.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -886,3 +886,23 @@ pmd_t pmd_mkwrite(pmd_t pmd, struct vm_area_struct *vma)
886886

887887
return pmd_clear_saveddirty(pmd);
888888
}
889+
890+
void arch_check_zapped_pte(struct vm_area_struct *vma, pte_t pte)
891+
{
892+
/*
893+
* Hardware before shadow stack can (rarely) set Dirty=1
894+
* on a Write=0 PTE. So the below condition
895+
* only indicates a software bug when shadow stack is
896+
* supported by the HW. This checking is covered in
897+
* pte_shstk().
898+
*/
899+
VM_WARN_ON_ONCE(!(vma->vm_flags & VM_SHADOW_STACK) &&
900+
pte_shstk(pte));
901+
}
902+
903+
void arch_check_zapped_pmd(struct vm_area_struct *vma, pmd_t pmd)
904+
{
905+
/* See note in arch_check_zapped_pte() */
906+
VM_WARN_ON_ONCE(!(vma->vm_flags & VM_SHADOW_STACK) &&
907+
pmd_shstk(pmd));
908+
}

include/linux/pgtable.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,20 @@ static inline bool arch_has_hw_pte_young(void)
313313
}
314314
#endif
315315

316+
#ifndef arch_check_zapped_pte
317+
static inline void arch_check_zapped_pte(struct vm_area_struct *vma,
318+
pte_t pte)
319+
{
320+
}
321+
#endif
322+
323+
#ifndef arch_check_zapped_pmd
324+
static inline void arch_check_zapped_pmd(struct vm_area_struct *vma,
325+
pmd_t pmd)
326+
{
327+
}
328+
#endif
329+
316330
#ifndef __HAVE_ARCH_PTEP_GET_AND_CLEAR
317331
static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
318332
unsigned long address,

mm/huge_memory.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1681,6 +1681,7 @@ int zap_huge_pmd(struct mmu_gather *tlb, struct vm_area_struct *vma,
16811681
*/
16821682
orig_pmd = pmdp_huge_get_and_clear_full(vma, addr, pmd,
16831683
tlb->fullmm);
1684+
arch_check_zapped_pmd(vma, orig_pmd);
16841685
tlb_remove_pmd_tlb_entry(tlb, pmd, addr);
16851686
if (vma_is_special_huge(vma)) {
16861687
if (arch_needs_pgtable_deposit())

mm/memory.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,6 +1430,7 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
14301430
continue;
14311431
ptent = ptep_get_and_clear_full(mm, addr, pte,
14321432
tlb->fullmm);
1433+
arch_check_zapped_pte(vma, ptent);
14331434
tlb_remove_tlb_entry(tlb, pte, addr);
14341435
zap_install_uffd_wp_if_needed(vma, addr, pte, details,
14351436
ptent);

0 commit comments

Comments
 (0)