diff options
author | Andrew Morton <akpm@osdl.org> | 2006-09-08 12:48:38 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-09-08 13:22:50 -0400 |
commit | 016eb4a0ed06a3677d67a584da901f0e9a63c666 (patch) | |
tree | 574ead6bd03d31ca7036ff2389cb0400f3fd63fc /mm/truncate.c | |
parent | 3665d0e58fa44f50c744f85c7e8ad21d5b10e206 (diff) |
[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>
Diffstat (limited to 'mm/truncate.c')
-rw-r--r-- | mm/truncate.c | 11 |
1 files changed, 7 insertions, 4 deletions
diff --git a/mm/truncate.c b/mm/truncate.c index cf1b015df4a7..c6ab55ec6883 100644 --- a/mm/truncate.c +++ b/mm/truncate.c | |||
@@ -68,10 +68,10 @@ invalidate_complete_page(struct address_space *mapping, struct page *page) | |||
68 | return 0; | 68 | return 0; |
69 | 69 | ||
70 | write_lock_irq(&mapping->tree_lock); | 70 | write_lock_irq(&mapping->tree_lock); |
71 | if (PageDirty(page)) { | 71 | if (PageDirty(page)) |
72 | write_unlock_irq(&mapping->tree_lock); | 72 | goto failed; |
73 | return 0; | 73 | if (page_count(page) != 2) /* caller's ref + pagecache ref */ |
74 | } | 74 | goto failed; |
75 | 75 | ||
76 | BUG_ON(PagePrivate(page)); | 76 | BUG_ON(PagePrivate(page)); |
77 | __remove_from_page_cache(page); | 77 | __remove_from_page_cache(page); |
@@ -79,6 +79,9 @@ invalidate_complete_page(struct address_space *mapping, struct page *page) | |||
79 | ClearPageUptodate(page); | 79 | ClearPageUptodate(page); |
80 | page_cache_release(page); /* pagecache ref */ | 80 | page_cache_release(page); /* pagecache ref */ |
81 | return 1; | 81 | return 1; |
82 | failed: | ||
83 | write_unlock_irq(&mapping->tree_lock); | ||
84 | return 0; | ||
82 | } | 85 | } |
83 | 86 | ||
84 | /** | 87 | /** |