]> git.baikalelectronics.ru Git - kernel.git/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)
committerHelge Deller <deller@gmx.de>
Sat, 13 Nov 2021 21:10:56 +0000 (22:10 +0100)
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+
arch/parisc/include/asm/pgtable.h
arch/parisc/kernel/cache.c

index 7badd872f05ac79a907640547741a1dfbb546782..3e7cf882639fb9e45ebbca1900ecf3c616cf74c0 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)
@@ -303,6 +308,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)
@@ -410,7 +416,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 c61827e4928ae3952719554cd5e4e6e8228f6b00..94150b91c96fb66e9c08e683991959f1a98d620a 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