aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sparc/include
diff options
context:
space:
mode:
authorKirill A. Shutemov <kirill.shutemov@linux.intel.com>2015-02-11 18:25:35 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2015-02-11 20:06:01 -0500
commit4ecf886045152d2ddf98ae74e39f789868ac1f98 (patch)
tree6cde1110b8bad8824ffc882640e89a1c4af3d68c /arch/sparc/include
parent9fbc1f635fd0bd28cb32550211bf095753ac637a (diff)
sparc32: fix broken set_pte()
32-bit sparc uses swap instruction to implement set_pte(). It called using GCC inline assembler. But it misses the "memory" clobber to indicate that pte value will be updated in memory. As result GCC doesn't know that it cannot postpone pte pointer dereference which occurs before set_pte() to post-set_pte() time. It leads to real-world bugs -- [1]. In this situation we have code: ptent = ptep_modify_prot_start(mm, addr, pte); ptent = pte_modify(ptent, newprot); ... ptep_modify_prot_commit(mm, addr, pte, ptent); ptep_modify_prot_start() in sparc case is just 'pte' dereference plus pte_clear(). pte_clear() calls broken set_pte(). GCC thinks it's valid to dereference 'pte' again on pte_modify() and gets cleared pte. ptep_modify_prot_commit() puts 'pteent' with pfn==0 back to page table, which eventually leads to the crash. [1] http://lkml.kernel.org/r/54C06B19.8060305@roeck-us.net Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Reported-by: Guenter Roeck <linux@roeck-us.net> Tested-by: Guenter Roeck <linux@roeck-us.net> Cc: Paul Moore <pmoore@redhat.com> Cc: Joonsoo Kim <iamjoonsoo.kim@lge.com> Cc: David Miller <davem@davemloft.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/sparc/include')
-rw-r--r--arch/sparc/include/asm/pgtable_32.h3
1 files changed, 2 insertions, 1 deletions
diff --git a/arch/sparc/include/asm/pgtable_32.h b/arch/sparc/include/asm/pgtable_32.h
index b2f7dc46a7d1..9912eb0b499a 100644
--- a/arch/sparc/include/asm/pgtable_32.h
+++ b/arch/sparc/include/asm/pgtable_32.h
@@ -102,7 +102,8 @@ extern unsigned long empty_zero_page;
102 */ 102 */
103static inline unsigned long srmmu_swap(unsigned long *addr, unsigned long value) 103static inline unsigned long srmmu_swap(unsigned long *addr, unsigned long value)
104{ 104{
105 __asm__ __volatile__("swap [%2], %0" : "=&r" (value) : "0" (value), "r" (addr)); 105 __asm__ __volatile__("swap [%2], %0" :
106 "=&r" (value) : "0" (value), "r" (addr) : "memory");
106 return value; 107 return value;
107} 108}
108 109