Commit 9bc4f28a authored by Nadav Amit's avatar Nadav Amit Committed by Thomas Gleixner

x86/mm: Use WRITE_ONCE() when setting PTEs

When page-table entries are set, the compiler might optimize their
assignment by using multiple instructions to set the PTE. This might
turn into a security hazard if the user somehow manages to use the
interim PTE. L1TF does not make our lives easier, making even an interim
non-present PTE a security hazard.

Using WRITE_ONCE() to set PTEs and friends should prevent this potential
security hazard.

I skimmed the differences in the binary with and without this patch. The
differences are (obviously) greater when CONFIG_PARAVIRT=n as more
code optimizations are possible. For better and worse, the impact on the
binary with this patch is pretty small. Skimming the code did not cause
anything to jump out as a security hazard, but it seems that at least
move_soft_dirty_pte() caused set_pte_at() to use multiple writes.
Signed-off-by: default avatarNadav Amit <namit@vmware.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Acked-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Cc: Dave Hansen <dave.hansen@linux.intel.com>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Sean Christopherson <sean.j.christopherson@intel.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: stable@vger.kernel.org
Link: https://lkml.kernel.org/r/20180902181451.80520-1-namit@vmware.com
parent 47b7360c
...@@ -1195,7 +1195,7 @@ static inline pmd_t pmdp_establish(struct vm_area_struct *vma, ...@@ -1195,7 +1195,7 @@ static inline pmd_t pmdp_establish(struct vm_area_struct *vma,
return xchg(pmdp, pmd); return xchg(pmdp, pmd);
} else { } else {
pmd_t old = *pmdp; pmd_t old = *pmdp;
*pmdp = pmd; WRITE_ONCE(*pmdp, pmd);
return old; return old;
} }
} }
......
...@@ -55,15 +55,15 @@ struct mm_struct; ...@@ -55,15 +55,15 @@ struct mm_struct;
void set_pte_vaddr_p4d(p4d_t *p4d_page, unsigned long vaddr, pte_t new_pte); void set_pte_vaddr_p4d(p4d_t *p4d_page, unsigned long vaddr, pte_t new_pte);
void set_pte_vaddr_pud(pud_t *pud_page, unsigned long vaddr, pte_t new_pte); void set_pte_vaddr_pud(pud_t *pud_page, unsigned long vaddr, pte_t new_pte);
static inline void native_pte_clear(struct mm_struct *mm, unsigned long addr, static inline void native_set_pte(pte_t *ptep, pte_t pte)
pte_t *ptep)
{ {
*ptep = native_make_pte(0); WRITE_ONCE(*ptep, pte);
} }
static inline void native_set_pte(pte_t *ptep, pte_t pte) static inline void native_pte_clear(struct mm_struct *mm, unsigned long addr,
pte_t *ptep)
{ {
*ptep = pte; native_set_pte(ptep, native_make_pte(0));
} }
static inline void native_set_pte_atomic(pte_t *ptep, pte_t pte) static inline void native_set_pte_atomic(pte_t *ptep, pte_t pte)
...@@ -73,7 +73,7 @@ static inline void native_set_pte_atomic(pte_t *ptep, pte_t pte) ...@@ -73,7 +73,7 @@ static inline void native_set_pte_atomic(pte_t *ptep, pte_t pte)
static inline void native_set_pmd(pmd_t *pmdp, pmd_t pmd) static inline void native_set_pmd(pmd_t *pmdp, pmd_t pmd)
{ {
*pmdp = pmd; WRITE_ONCE(*pmdp, pmd);
} }
static inline void native_pmd_clear(pmd_t *pmd) static inline void native_pmd_clear(pmd_t *pmd)
...@@ -109,7 +109,7 @@ static inline pmd_t native_pmdp_get_and_clear(pmd_t *xp) ...@@ -109,7 +109,7 @@ static inline pmd_t native_pmdp_get_and_clear(pmd_t *xp)
static inline void native_set_pud(pud_t *pudp, pud_t pud) static inline void native_set_pud(pud_t *pudp, pud_t pud)
{ {
*pudp = pud; WRITE_ONCE(*pudp, pud);
} }
static inline void native_pud_clear(pud_t *pud) static inline void native_pud_clear(pud_t *pud)
...@@ -137,13 +137,13 @@ static inline void native_set_p4d(p4d_t *p4dp, p4d_t p4d) ...@@ -137,13 +137,13 @@ static inline void native_set_p4d(p4d_t *p4dp, p4d_t p4d)
pgd_t pgd; pgd_t pgd;
if (pgtable_l5_enabled() || !IS_ENABLED(CONFIG_PAGE_TABLE_ISOLATION)) { if (pgtable_l5_enabled() || !IS_ENABLED(CONFIG_PAGE_TABLE_ISOLATION)) {
*p4dp = p4d; WRITE_ONCE(*p4dp, p4d);
return; return;
} }
pgd = native_make_pgd(native_p4d_val(p4d)); pgd = native_make_pgd(native_p4d_val(p4d));
pgd = pti_set_user_pgtbl((pgd_t *)p4dp, pgd); pgd = pti_set_user_pgtbl((pgd_t *)p4dp, pgd);
*p4dp = native_make_p4d(native_pgd_val(pgd)); WRITE_ONCE(*p4dp, native_make_p4d(native_pgd_val(pgd)));
} }
static inline void native_p4d_clear(p4d_t *p4d) static inline void native_p4d_clear(p4d_t *p4d)
...@@ -153,7 +153,7 @@ static inline void native_p4d_clear(p4d_t *p4d) ...@@ -153,7 +153,7 @@ static inline void native_p4d_clear(p4d_t *p4d)
static inline void native_set_pgd(pgd_t *pgdp, pgd_t pgd) static inline void native_set_pgd(pgd_t *pgdp, pgd_t pgd)
{ {
*pgdp = pti_set_user_pgtbl(pgdp, pgd); WRITE_ONCE(*pgdp, pti_set_user_pgtbl(pgdp, pgd));
} }
static inline void native_pgd_clear(pgd_t *pgd) static inline void native_pgd_clear(pgd_t *pgd)
......
...@@ -269,7 +269,7 @@ static void mop_up_one_pmd(struct mm_struct *mm, pgd_t *pgdp) ...@@ -269,7 +269,7 @@ static void mop_up_one_pmd(struct mm_struct *mm, pgd_t *pgdp)
if (pgd_val(pgd) != 0) { if (pgd_val(pgd) != 0) {
pmd_t *pmd = (pmd_t *)pgd_page_vaddr(pgd); pmd_t *pmd = (pmd_t *)pgd_page_vaddr(pgd);
*pgdp = native_make_pgd(0); pgd_clear(pgdp);
paravirt_release_pmd(pgd_val(pgd) >> PAGE_SHIFT); paravirt_release_pmd(pgd_val(pgd) >> PAGE_SHIFT);
pmd_free(mm, pmd); pmd_free(mm, pmd);
...@@ -494,7 +494,7 @@ int ptep_set_access_flags(struct vm_area_struct *vma, ...@@ -494,7 +494,7 @@ int ptep_set_access_flags(struct vm_area_struct *vma,
int changed = !pte_same(*ptep, entry); int changed = !pte_same(*ptep, entry);
if (changed && dirty) if (changed && dirty)
*ptep = entry; set_pte(ptep, entry);
return changed; return changed;
} }
...@@ -509,7 +509,7 @@ int pmdp_set_access_flags(struct vm_area_struct *vma, ...@@ -509,7 +509,7 @@ int pmdp_set_access_flags(struct vm_area_struct *vma,
VM_BUG_ON(address & ~HPAGE_PMD_MASK); VM_BUG_ON(address & ~HPAGE_PMD_MASK);
if (changed && dirty) { if (changed && dirty) {
*pmdp = entry; set_pmd(pmdp, entry);
/* /*
* We had a write-protection fault here and changed the pmd * We had a write-protection fault here and changed the pmd
* to to more permissive. No need to flush the TLB for that, * to to more permissive. No need to flush the TLB for that,
...@@ -529,7 +529,7 @@ int pudp_set_access_flags(struct vm_area_struct *vma, unsigned long address, ...@@ -529,7 +529,7 @@ int pudp_set_access_flags(struct vm_area_struct *vma, unsigned long address,
VM_BUG_ON(address & ~HPAGE_PUD_MASK); VM_BUG_ON(address & ~HPAGE_PUD_MASK);
if (changed && dirty) { if (changed && dirty) {
*pudp = entry; set_pud(pudp, entry);
/* /*
* We had a write-protection fault here and changed the pud * We had a write-protection fault here and changed the pud
* to to more permissive. No need to flush the TLB for that, * to to more permissive. No need to flush the TLB for that,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment