[patch 27/29] Correct hash flushing from huge_ptep_set_wrprotect()

From: Greg KH
Date: Wed Jul 30 2008 - 19:43:40 EST


2.6.25-stable review patch. If anyone has any objections, please let us
know.

------------------
From: David Gibson <david@xxxxxxxxxxxxxxxxxxxxx>

Correct hash flushing from huge_ptep_set_wrprotect() [stable tree version]

A fix for incorrect flushing of the hash page table at fork() for
hugepages was recently committed as
86df86424939d316b1f6cfac1b6204f0c7dee317. Without this fix, a process
can make a MAP_PRIVATE hugepage mapping, then fork() and have writes
to the mapping after the fork() pollute the child's version.

Unfortunately this bug also exists in the stable branch. In fact in
that case copy_hugetlb_page_range() from mm/hugetlb.c calls
ptep_set_wrprotect() directly, the hugepage variant hook
huge_ptep_set_wrprotect() doesn't even exist.

The patch below is a port of the fix to the stable25/master branch.
It introduces a huge_ptep_set_wrprotect() call, but this is #defined
to be equal to ptep_set_wrprotect() unless the arch defines its own
version and sets __HAVE_ARCH_HUGE_PTEP_SET_WRPROTECT.

This arch preprocessor flag is kind of nasty, but it seems the sanest
way to introduce this fix with minimum risk of breaking other archs
for whom prep_set_wprotect() is suitable for hugepages.

Signed-off-by: Andy Whitcroft <apw@xxxxxxxxxxxx>
Signed-off-by: David Gibson <david@xxxxxxxxxxxxxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxx>

---
include/asm-powerpc/pgtable-ppc64.h | 11 +++++++++++
mm/hugetlb.c | 6 +++++-
2 files changed, 16 insertions(+), 1 deletion(-)

--- a/include/asm-powerpc/pgtable-ppc64.h
+++ b/include/asm-powerpc/pgtable-ppc64.h
@@ -311,6 +311,17 @@ static inline void ptep_set_wrprotect(st
old = pte_update(mm, addr, ptep, _PAGE_RW, 0);
}

+#define __HAVE_ARCH_HUGE_PTEP_SET_WRPROTECT
+static inline void huge_ptep_set_wrprotect(struct mm_struct *mm,
+ unsigned long addr, pte_t *ptep)
+{
+ unsigned long old;
+
+ if ((pte_val(*ptep) & _PAGE_RW) == 0)
+ return;
+ old = pte_update(mm, addr, ptep, _PAGE_RW, 1);
+}
+
/*
* We currently remove entries from the hashtable regardless of whether
* the entry was young or dirty. The generic routines only flush if the
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -738,6 +738,10 @@ static void set_huge_ptep_writable(struc
}


+#ifndef __HAVE_ARCH_HUGE_PTEP_SET_WRPROTECT
+#define huge_ptep_set_wrprotect ptep_set_wrprotect
+#endif
+
int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
struct vm_area_struct *vma)
{
@@ -764,7 +768,7 @@ int copy_hugetlb_page_range(struct mm_st
spin_lock(&src->page_table_lock);
if (!pte_none(*src_pte)) {
if (cow)
- ptep_set_wrprotect(src, addr, src_pte);
+ huge_ptep_set_wrprotect(src, addr, src_pte);
entry = *src_pte;
ptepage = pte_page(entry);
get_page(ptepage);

--
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/