hugetlb: fix race condition in hugetlb_fault()
svn path=/dists/sid/linux-2.6/; revision=18923
This commit is contained in:
parent
52fc1edcc2
commit
43569a1e44
|
@ -10,6 +10,7 @@ linux-2.6 (3.2.14-2) UNRELEASED; urgency=low
|
|||
* drm/radeon/kms: fix fans after resume (Closes: #596741)
|
||||
* [x86] hv: Update all Hyper-V drivers to 3.4-rc1 (Closes: #661318)
|
||||
* nfs: Fix length of buffer copied in __nfs4_get_acl_uncached
|
||||
* hugetlb: fix race condition in hugetlb_fault()
|
||||
|
||||
[ Jonathan Nieder ]
|
||||
* [x86] ioat: fix size of 'completion' for Xen (Closes: #660554)
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
From: Chris Metcalf <cmetcalf@tilera.com>
|
||||
Date: Thu, 12 Apr 2012 12:49:15 -0700
|
||||
Subject: [PATCH] hugetlb: fix race condition in hugetlb_fault()
|
||||
|
||||
commit 66aebce747eaf9bc456bf1f1b217d8db843031d0 upstream.
|
||||
|
||||
The race is as follows:
|
||||
|
||||
Suppose a multi-threaded task forks a new process (on cpu A), thus
|
||||
bumping up the ref count on all the pages. While the fork is occurring
|
||||
(and thus we have marked all the PTEs as read-only), another thread in
|
||||
the original process (on cpu B) tries to write to a huge page, taking an
|
||||
access violation from the write-protect and calling hugetlb_cow(). Now,
|
||||
suppose the fork() fails. It will undo the COW and decrement the ref
|
||||
count on the pages, so the ref count on the huge page drops back to 1.
|
||||
Meanwhile hugetlb_cow() also decrements the ref count by one on the
|
||||
original page, since the original address space doesn't need it any
|
||||
more, having copied a new page to replace the original page. This
|
||||
leaves the ref count at zero, and when we call unlock_page(), we panic.
|
||||
|
||||
fork on CPU A fault on CPU B
|
||||
============= ==============
|
||||
...
|
||||
down_write(&parent->mmap_sem);
|
||||
down_write_nested(&child->mmap_sem);
|
||||
...
|
||||
while duplicating vmas
|
||||
if error
|
||||
break;
|
||||
...
|
||||
up_write(&child->mmap_sem);
|
||||
up_write(&parent->mmap_sem); ...
|
||||
down_read(&parent->mmap_sem);
|
||||
...
|
||||
lock_page(page);
|
||||
handle COW
|
||||
page_mapcount(old_page) == 2
|
||||
alloc and prepare new_page
|
||||
...
|
||||
handle error
|
||||
page_remove_rmap(page);
|
||||
put_page(page);
|
||||
...
|
||||
fold new_page into pte
|
||||
page_remove_rmap(page);
|
||||
put_page(page);
|
||||
...
|
||||
oops ==> unlock_page(page);
|
||||
up_read(&parent->mmap_sem);
|
||||
|
||||
The solution is to take an extra reference to the page while we are
|
||||
holding the lock on it.
|
||||
|
||||
Signed-off-by: Chris Metcalf <cmetcalf@tilera.com>
|
||||
Cc: Hillf Danton <dhillf@gmail.com>
|
||||
Cc: Michal Hocko <mhocko@suse.cz>
|
||||
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
|
||||
Cc: Hugh Dickins <hughd@google.com>
|
||||
Cc: <stable@vger.kernel.org>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
|
||||
---
|
||||
mm/hugetlb.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
|
||||
index b8ce6f4..cd65cb1 100644
|
||||
--- a/mm/hugetlb.c
|
||||
+++ b/mm/hugetlb.c
|
||||
@@ -2791,6 +2791,7 @@ int hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
|
||||
* so no worry about deadlock.
|
||||
*/
|
||||
page = pte_page(entry);
|
||||
+ get_page(page);
|
||||
if (page != pagecache_page)
|
||||
lock_page(page);
|
||||
|
||||
@@ -2822,6 +2823,7 @@ out_page_table_lock:
|
||||
}
|
||||
if (page != pagecache_page)
|
||||
unlock_page(page);
|
||||
+ put_page(page);
|
||||
|
||||
out_mutex:
|
||||
mutex_unlock(&hugetlb_instantiation_mutex);
|
||||
--
|
||||
1.7.9.5
|
||||
|
|
@ -179,3 +179,4 @@
|
|||
+ debian/nls-Avoid-ABI-change-from-improvement-to-utf8s_to_ut.patch
|
||||
|
||||
+ bugfix/all/nfs-Fix-length-of-buffer-copied-in-__nfs4_get_acl_uncach.patch
|
||||
+ bugfix/all/hugetlb-fix-race-condition-in-hugetlb_fault.patch
|
||||
|
|
Loading…
Reference in New Issue