On Sep 29, 2022, at 3:29 PM, Rick Edgecombe <rick.p.edgecombe@xxxxxxxxx> wrote: > From: Yu-cheng Yu <yu-cheng.yu@xxxxxxxxx> > > When Shadow Stack is in use, Write=0,Dirty=1 PTE are reserved for shadow > stack. Copy-on-write PTes then have Write=0,Cow=1. > > When a PTE goes from Write=1,Dirty=1 to Write=0,Cow=1, it could > become a transient shadow stack PTE in two cases: > > The first case is that 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. > > The second case is that when _PAGE_DIRTY is replaced with _PAGE_COW non- > atomically, a transient shadow stack PTE can be created as a result. > Thus, prevent that with cmpxchg. > > Dave Hansen, Jann Horn, Andy Lutomirski, and Peter Zijlstra provided many > insights to the issue. Jann Horn provided the cmpxchg solution. > > Signed-off-by: Yu-cheng Yu <yu-cheng.yu@xxxxxxxxx> > Co-developed-by: Rick Edgecombe <rick.p.edgecombe@xxxxxxxxx> > Signed-off-by: Rick Edgecombe <rick.p.edgecombe@xxxxxxxxx> > > --- > > v2: > - Compile out some code due to clang build error > - Clarify commit log (dhansen) > - Normalize PTE bit descriptions between patches (dhansen) > - Update comment with text from (dhansen) > > Yu-cheng v30: > - Replace (pmdval_t) cast with CONFIG_PGTABLE_LEVELES > 2 (Borislav Petkov). > > arch/x86/include/asm/pgtable.h | 36 ++++++++++++++++++++++++++++++++++ > 1 file changed, 36 insertions(+) > > diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h > index 2f2963429f48..58c7bf9d7392 100644 > --- a/arch/x86/include/asm/pgtable.h > +++ b/arch/x86/include/asm/pgtable.h > @@ -1287,6 +1287,23 @@ 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) > { > +#ifdef CONFIG_X86_SHADOW_STACK > + /* > + * Avoid accidentally creating shadow stack PTEs > + * (Write=0,Dirty=1). Use cmpxchg() to prevent races with > + * the hardware setting Dirty=1. > + */ > + if (cpu_feature_enabled(X86_FEATURE_SHSTK)) { > + pte_t old_pte, new_pte; > + > + old_pte = READ_ONCE(*ptep); > + do { > + new_pte = pte_wrprotect(old_pte); > + } while (!try_cmpxchg(&ptep->pte, &old_pte.pte, new_pte.pte)); > + > + return; > + } > +#endif There is no way of using IS_ENABLED() here instead of these ifdefs? Did you have a look at ptep_set_access_flags() and friends and checked they do not need to be changed too? Perhaps you should at least add some assertion just to ensure nothing breaks.