mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-25 03:55:09 +08:00
x86/mm: Update ptep/pmdp_set_wrprotect() for _PAGE_SAVED_DIRTY
When shadow stack is in use, Write=0,Dirty=1 PTE are preserved for shadow stack. Copy-on-write PTEs then have Write=0,SavedDirty=1. When a PTE goes from Write=1,Dirty=1 to Write=0,SavedDirty=1, it could become a transient shadow stack PTE in two cases: 1. Some processors can start a write but end up seeing a Write=0 PTE by the time they get to the Dirty bit, creating a transient shadow stack PTE. However, this will not occur on processors supporting shadow stack, and a TLB flush is not necessary. 2. When _PAGE_DIRTY is replaced with _PAGE_SAVED_DIRTY non-atomically, a transient shadow stack PTE can be created as a result. Prevent the second case when doing a write protection and Dirty->SavedDirty shift at the same time with a CMPXCHG loop. The first case Note, in the PAE case CMPXCHG will need to operate on 8 byte, but try_cmpxchg() will not use CMPXCHG8B, so it cannot operate on a full PAE PTE. However the exiting logic is not operating on a full 8 byte region either, and relies on the fact that the Write bit is in the first 4 bytes when doing the clear_bit(). Since both the Dirty, SavedDirty and Write bits are in the first 4 bytes, casting to a long will be similar to the existing behavior which also casts to a long. Dave Hansen, Jann Horn, Andy Lutomirski, and Peter Zijlstra provided many insights to the issue. Jann Horn provided the CMPXCHG solution. Co-developed-by: Yu-cheng Yu <yu-cheng.yu@intel.com> Signed-off-by: Yu-cheng Yu <yu-cheng.yu@intel.com> Signed-off-by: Rick Edgecombe <rick.p.edgecombe@intel.com> Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com> Acked-by: Mike Rapoport (IBM) <rppt@kernel.org> Tested-by: Pengfei Xu <pengfei.xu@intel.com> Tested-by: John Allen <john.allen@amd.com> Tested-by: Kees Cook <keescook@chromium.org> Link: https://lore.kernel.org/all/20230613001108.3040476-12-rick.p.edgecombe%40intel.com
This commit is contained in:
parent
fca4d413c5
commit
1f6f66f62e
@ -1190,7 +1190,17 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm,
|
||||
static inline void ptep_set_wrprotect(struct mm_struct *mm,
|
||||
unsigned long addr, pte_t *ptep)
|
||||
{
|
||||
clear_bit(_PAGE_BIT_RW, (unsigned long *)&ptep->pte);
|
||||
/*
|
||||
* Avoid accidentally creating shadow stack PTEs
|
||||
* (Write=0,Dirty=1). Use cmpxchg() to prevent races with
|
||||
* the hardware setting Dirty=1.
|
||||
*/
|
||||
pte_t old_pte, new_pte;
|
||||
|
||||
old_pte = READ_ONCE(*ptep);
|
||||
do {
|
||||
new_pte = pte_wrprotect(old_pte);
|
||||
} while (!try_cmpxchg((long *)&ptep->pte, (long *)&old_pte, *(long *)&new_pte));
|
||||
}
|
||||
|
||||
#define flush_tlb_fix_spurious_fault(vma, address, ptep) do { } while (0)
|
||||
@ -1242,7 +1252,17 @@ static inline pud_t pudp_huge_get_and_clear(struct mm_struct *mm,
|
||||
static inline void pmdp_set_wrprotect(struct mm_struct *mm,
|
||||
unsigned long addr, pmd_t *pmdp)
|
||||
{
|
||||
clear_bit(_PAGE_BIT_RW, (unsigned long *)pmdp);
|
||||
/*
|
||||
* Avoid accidentally creating shadow stack PTEs
|
||||
* (Write=0,Dirty=1). Use cmpxchg() to prevent races with
|
||||
* the hardware setting Dirty=1.
|
||||
*/
|
||||
pmd_t old_pmd, new_pmd;
|
||||
|
||||
old_pmd = READ_ONCE(*pmdp);
|
||||
do {
|
||||
new_pmd = pmd_wrprotect(old_pmd);
|
||||
} while (!try_cmpxchg((long *)pmdp, (long *)&old_pmd, *(long *)&new_pmd));
|
||||
}
|
||||
|
||||
#ifndef pmdp_establish
|
||||
|
Loading…
Reference in New Issue
Block a user