From: Andrea Arcangeli <aarcange@redhat.com> Date: Fri, 13 Feb 2009 16:09:48 +0100 Subject: [mm] cow vs gup race fix Message-id: 20090213150948.GC9099@random.random O-Subject: cow vs gup race fix Bugzilla: 471613 RH-Acked-by: Larry Woodman <lwoodman@redhat.com> RH-Acked-by: Rik van Riel <riel@redhat.com> Hello, patch to review before it goes productive, related bugs are: https://bugzilla.redhat.com/show_bug.cgi?id=121733 https://bugzilla.redhat.com/show_bug.cgi?id=471613 This is a longstanding bug introduced when they removed my mapcount vs count check to keep ptes pinned of pages under gup post 2.6.7. Backport from mainline. ------- From: Hugh Dickins <hugh@veritas.com> Date: Tue, 6 Jan 2009 22:39:33 +0000 (-0800) Subject: mm: wp lock page before deciding cow X-Git-Tag: v2.6.29-rc1~450 X-Git-Url: http://127.0.0.1:1234/?p=.git;a=commitdiff_plain;h=ab967d86015a19777955370deebc8262d50fed63 mm: wp lock page before deciding cow An application may rely on get_user_pages() to give it pages writable from userspace and shared with a driver, GUP breaking COW if necessary. It may mprotect() the pages' writability, off and on, from time to time. Normally this works fine (so long as the app does not fork); but just occasionally, under memory pressure, a readonly pte in a newly writable area is COWed unnecessarily, breaking the link with the driver: because do_wp_page() does trylock_page, and falls back to COW whenever that fails. For reliable behaviour in the unshared case, when the trylock_page fails, now unlock pagetable, lock page and relock pagetable, before deciding whether Copy-On-Write is really necessary. Reported-by: Zhou Yingchao Signed-off-by: Hugh Dickins <hugh@veritas.com> Cc: Lee Schermerhorn <lee.schermerhorn@hp.com> Cc: Rik van Riel <riel@redhat.com> Cc: Nick Piggin <nickpiggin@yahoo.com.au> Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com> Cc: Robin Holt <holt@sgi.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> diff --git a/mm/memory.c b/mm/memory.c index 8565f89..5233ed2 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1830,10 +1830,21 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma, * not dirty accountable. */ if (PageAnon(old_page)) { - if (!TestSetPageLocked(old_page)) { - reuse = can_share_swap_page(old_page); - unlock_page(old_page); + if (TestSetPageLocked(old_page)) { + page_cache_get(old_page); + pte_unmap_unlock(page_table, ptl); + lock_page(old_page); + page_table = pte_offset_map_lock(mm, pmd, address, + &ptl); + if (!pte_same(*page_table, orig_pte)) { + unlock_page(old_page); + page_cache_release(old_page); + goto unlock; + } + page_cache_release(old_page); } + reuse = can_share_swap_page(old_page); + unlock_page(old_page); } else if (unlikely((vma->vm_flags & (VM_WRITE|VM_SHARED)) == (VM_WRITE|VM_SHARED))) { /*