In change_pte_range(), when a PTE is changed for prot_numa, _PAGE_RW is preserved to avoid the additional write fault after the NUMA hinting fault. However, pte_write() now includes both normal writable and shadow stack (RW=0, Dirty=1) PTEs, but the latter does not have _PAGE_RW and has no need to preserve it. Exclude shadow stack from preserve_write test, and apply the same change to change_huge_pmd(). Signed-off-by: Yu-cheng Yu <yu-cheng.yu@xxxxxxxxx> --- mm/huge_memory.c | 7 ++++++- mm/mprotect.c | 9 ++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index de8c8b94e840..6b7dc5dd94de 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -1816,12 +1816,17 @@ int change_huge_pmd(struct vm_area_struct *vma, pmd_t *pmd, bool prot_numa = cp_flags & MM_CP_PROT_NUMA; bool uffd_wp = cp_flags & MM_CP_UFFD_WP; bool uffd_wp_resolve = cp_flags & MM_CP_UFFD_WP_RESOLVE; + bool shstk = arch_shadow_stack_mapping(vma->vm_flags); ptl = __pmd_trans_huge_lock(pmd, vma); if (!ptl) return 0; - preserve_write = prot_numa && pmd_write(*pmd); + /* + * Preserve only normal writable huge PMD, but not shadow + * stack (RW=0, Dirty=1). + */ + preserve_write = prot_numa && pmd_write(*pmd) && !shstk; ret = 1; #ifdef CONFIG_ARCH_ENABLE_THP_MIGRATION diff --git a/mm/mprotect.c b/mm/mprotect.c index c1ce78d688b6..e69278b346a9 100644 --- a/mm/mprotect.c +++ b/mm/mprotect.c @@ -75,7 +75,14 @@ static unsigned long change_pte_range(struct vm_area_struct *vma, pmd_t *pmd, oldpte = *pte; if (pte_present(oldpte)) { pte_t ptent; - bool preserve_write = prot_numa && pte_write(oldpte); + bool shstk = arch_shadow_stack_mapping(vma->vm_flags); + bool preserve_write; + + /* + * Preserve only normal writable PTE, but not shadow + * stack (RW=0, Dirty=1). + */ + preserve_write = prot_numa && pte_write(oldpte) && !shstk; /* * Avoid trapping faults against the zero or KSM -- 2.21.0