Răsfoiți Sursa

[PATCH] invalidate_complete_page() race fix

If a CPU faults this page into pagetables after invalidate_mapping_pages()
checked page_mapped(), invalidate_complete_page() will still proceed to remove
the page from pagecache.  This leaves the page-faulting process with a
detached page.  If it was MAP_SHARED then file data loss will ensue.

Fix that up by checking the page's refcount after taking tree_lock.

Cc: Nick Piggin <nickpiggin@yahoo.com.au>
Cc: Hugh Dickins <hugh@veritas.com>
Cc: <stable@kernel.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Andrew Morton 18 ani în urmă
părinte
comite
016eb4a0ed
1 a modificat fișierele cu 7 adăugiri și 4 ștergeri
  1. 7 4
      mm/truncate.c

+ 7 - 4
mm/truncate.c

@@ -68,10 +68,10 @@ invalidate_complete_page(struct address_space *mapping, struct page *page)
 		return 0;
 		return 0;
 
 
 	write_lock_irq(&mapping->tree_lock);
 	write_lock_irq(&mapping->tree_lock);
-	if (PageDirty(page)) {
-		write_unlock_irq(&mapping->tree_lock);
-		return 0;
-	}
+	if (PageDirty(page))
+		goto failed;
+	if (page_count(page) != 2)	/* caller's ref + pagecache ref */
+		goto failed;
 
 
 	BUG_ON(PagePrivate(page));
 	BUG_ON(PagePrivate(page));
 	__remove_from_page_cache(page);
 	__remove_from_page_cache(page);
@@ -79,6 +79,9 @@ invalidate_complete_page(struct address_space *mapping, struct page *page)
 	ClearPageUptodate(page);
 	ClearPageUptodate(page);
 	page_cache_release(page);	/* pagecache ref */
 	page_cache_release(page);	/* pagecache ref */
 	return 1;
 	return 1;
+failed:
+	write_unlock_irq(&mapping->tree_lock);
+	return 0;
 }
 }
 
 
 /**
 /**