]> git.itanic.dy.fi Git - linux-stable/commitdiff
parisc: Flush kernel data mapping in set_pte_at() when installing pte for user page
authorJohn David Anglin <dave.anglin@bell.net>
Mon, 8 Nov 2021 21:48:16 +0000 (16:48 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 23 Sep 2022 12:16:56 +0000 (14:16 +0200)
[ Upstream commit 38860b2c8bb1b92f61396eb06a63adff916fc31d ]

For years, there have been random segmentation faults in userspace on
SMP PA-RISC machines.  It occurred to me that this might be a problem in
set_pte_at().  MIPS and some other architectures do cache flushes when
installing PTEs with the present bit set.

Here I have adapted the code in update_mmu_cache() to flush the kernel
mapping when the kernel flush is deferred, or when the kernel mapping
may alias with the user mapping.  This simplifies calls to
update_mmu_cache().

I also changed the barrier in set_pte() from a compiler barrier to a
full memory barrier.  I know this change is not sufficient to fix the
problem.  It might not be needed.

I have had a few days of operation with 5.14.16 to 5.15.1 and haven't
seen any random segmentation faults on rp3440 or c8000 so far.

Signed-off-by: John David Anglin <dave.anglin@bell.net>
Signed-off-by: Helge Deller <deller@gmx.de>
Cc: stable@kernel.org # 5.12+
Signed-off-by: Sasha Levin <sashal@kernel.org>
arch/parisc/include/asm/pgtable.h
arch/parisc/kernel/cache.c

index 39017210dbf06135123ab98ea6aacc710aaac51c..8964798b8274ed34132ad2f3e7a051f458c413d5 100644 (file)
@@ -76,6 +76,8 @@ static inline void purge_tlb_entries(struct mm_struct *mm, unsigned long addr)
        purge_tlb_end(flags);
 }
 
+extern void __update_cache(pte_t pte);
+
 /* Certain architectures need to do special things when PTEs
  * within a page table are directly modified.  Thus, the following
  * hook is made available.
@@ -83,11 +85,14 @@ static inline void purge_tlb_entries(struct mm_struct *mm, unsigned long addr)
 #define set_pte(pteptr, pteval)                        \
        do {                                    \
                *(pteptr) = (pteval);           \
-               barrier();                      \
+               mb();                           \
        } while(0)
 
 #define set_pte_at(mm, addr, pteptr, pteval)   \
        do {                                    \
+               if (pte_present(pteval) &&      \
+                   pte_user(pteval))           \
+                       __update_cache(pteval); \
                *(pteptr) = (pteval);           \
                purge_tlb_entries(mm, addr);    \
        } while (0)
@@ -305,6 +310,7 @@ extern unsigned long *empty_zero_page;
 
 #define pte_none(x)     (pte_val(x) == 0)
 #define pte_present(x) (pte_val(x) & _PAGE_PRESENT)
+#define pte_user(x)    (pte_val(x) & _PAGE_USER)
 #define pte_clear(mm, addr, xp)  set_pte_at(mm, addr, xp, __pte(0))
 
 #define pmd_flag(x)    (pmd_val(x) & PxD_FLAG_MASK)
@@ -412,7 +418,7 @@ extern void paging_init (void);
 
 #define PG_dcache_dirty         PG_arch_1
 
-extern void update_mmu_cache(struct vm_area_struct *, unsigned long, pte_t *);
+#define update_mmu_cache(vms,addr,ptep) __update_cache(*ptep)
 
 /* Encode and de-code a swap entry */
 
index 86a1a63563fd570286c77fca0a44471c888a83b7..c81ab0cb892555af8df34464783c13866e80400b 100644 (file)
@@ -83,9 +83,9 @@ EXPORT_SYMBOL(flush_cache_all_local);
 #define pfn_va(pfn)    __va(PFN_PHYS(pfn))
 
 void
-update_mmu_cache(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
+__update_cache(pte_t pte)
 {
-       unsigned long pfn = pte_pfn(*ptep);
+       unsigned long pfn = pte_pfn(pte);
        struct page *page;
 
        /* We don't have pte special.  As a result, we can be called with