Re: [PATCH v29 12/32] x86/mm: Update ptep_set_wrprotect() and pmdp_set_wrprotect() for transition from _PAGE_DIRTY to _PAGE_COW

From: Borislav Petkov
Date: Thu Aug 26 2021 - 06:42:19 EST


On Fri, Aug 20, 2021 at 11:11:41AM -0700, Yu-cheng Yu wrote:
> @@ -1322,6 +1340,24 @@ 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)
> {
> + /*
> + * If Shadow Stack is enabled, pmd_wrprotect() moves _PAGE_DIRTY
> + * to _PAGE_COW (see comments at pmd_wrprotect()).
> + * When a thread reads a RW=1, Dirty=0 PMD and before changing it
> + * to RW=0, Dirty=0, another thread could have written to the page
> + * and the PMD is RW=1, Dirty=1 now. Use try_cmpxchg() to detect
> + * PMD changes and update old_pmd, then try again.
> + */
> + if (cpu_feature_enabled(X86_FEATURE_SHSTK)) {
> + pmd_t old_pmd, new_pmd;
> +
> + old_pmd = READ_ONCE(*pmdp);
> + do {
> + new_pmd = pmd_wrprotect(old_pmd);
> + } while (!try_cmpxchg((pmdval_t *)pmdp, (pmdval_t *)&old_pmd, pmd_val(new_pmd)));

>From the previous thread:

> If !(CONFIG_PGTABLE_LEVELS > 2), we don't have pmd_t.pmd.

So I guess you can do this, in line with how the pmd folding is done in
the rest of the mm headers. There's no need to make this more complex
than it is just so that 32-bit !PAE builds but where CET is not even
enabled.

---
diff --git a/arch/x86/include/asm/pgtable.h b/arch/x86/include/asm/pgtable.h
index df4ce715560a..7c0542997790 100644
--- a/arch/x86/include/asm/pgtable.h
+++ b/arch/x86/include/asm/pgtable.h
@@ -1340,6 +1340,7 @@ 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)
{
+#if CONFIG_PGTABLE_LEVELS > 2
/*
* If Shadow Stack is enabled, pmd_wrprotect() moves _PAGE_DIRTY
* to _PAGE_COW (see comments at pmd_wrprotect()).
@@ -1354,10 +1355,11 @@ static inline void pmdp_set_wrprotect(struct mm_struct *mm,
old_pmd = READ_ONCE(*pmdp);
do {
new_pmd = pmd_wrprotect(old_pmd);
- } while (!try_cmpxchg((pmdval_t *)pmdp, (pmdval_t *)&old_pmd, pmd_val(new_pmd)));
+ } while (!try_cmpxchg(&pmdp->pmd, &old_pmd.pmd, new_pmd.pmd));

return;
}
+#endif
clear_bit(_PAGE_BIT_RW, (unsigned long *)pmdp);
}


--
Regards/Gruss,
Boris.

https://people.kernel.org/tglx/notes-about-netiquette