aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorDavid Rientjes <rientjes@google.com>2014-03-03 18:38:18 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-04-03 15:01:05 -0400
commitdef52acc90faab583b124f3177d55c15d125e2d1 (patch)
tree067db9dcb4ce2ebdbc0894763c02818bd1eceb6d /include
parentd113edc6c7027a8290ddfb2f0c5ab8291a582945 (diff)
mm: close PageTail race
commit 668f9abbd4334e6c29fa8acd71635c4f9101caa7 upstream. Commit bf6bddf1924e ("mm: introduce compaction and migration for ballooned pages") introduces page_count(page) into memory compaction which dereferences page->first_page if PageTail(page). This results in a very rare NULL pointer dereference on the aforementioned page_count(page). Indeed, anything that does compound_head(), including page_count() is susceptible to racing with prep_compound_page() and seeing a NULL or dangling page->first_page pointer. This patch uses Andrea's implementation of compound_trans_head() that deals with such a race and makes it the default compound_head() implementation. This includes a read memory barrier that ensures that if PageTail(head) is true that we return a head page that is neither NULL nor dangling. The patch then adds a store memory barrier to prep_compound_page() to ensure page->first_page is set. This is the safest way to ensure we see the head page that we are expecting, PageTail(page) is already in the unlikely() path and the memory barriers are unfortunately required. Hugetlbfs is the exception, we don't enforce a store memory barrier during init since no race is possible. Signed-off-by: David Rientjes <rientjes@google.com> Cc: Holger Kiehl <Holger.Kiehl@dwd.de> Cc: Christoph Lameter <cl@linux.com> Cc: Rafael Aquini <aquini@redhat.com> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: Michal Hocko <mhocko@suse.cz> Cc: Mel Gorman <mgorman@suse.de> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: Rik van Riel <riel@redhat.com> Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'include')
-rw-r--r--include/linux/huge_mm.h18
-rw-r--r--include/linux/mm.h14
2 files changed, 12 insertions, 20 deletions
diff --git a/include/linux/huge_mm.h b/include/linux/huge_mm.h
index 528454c2caa9..a193bb3e4138 100644
--- a/include/linux/huge_mm.h
+++ b/include/linux/huge_mm.h
@@ -159,23 +159,6 @@ static inline int hpage_nr_pages(struct page *page)
159 return HPAGE_PMD_NR; 159 return HPAGE_PMD_NR;
160 return 1; 160 return 1;
161} 161}
162static inline struct page *compound_trans_head(struct page *page)
163{
164 if (PageTail(page)) {
165 struct page *head;
166 head = page->first_page;
167 smp_rmb();
168 /*
169 * head may be a dangling pointer.
170 * __split_huge_page_refcount clears PageTail before
171 * overwriting first_page, so if PageTail is still
172 * there it means the head pointer isn't dangling.
173 */
174 if (PageTail(page))
175 return head;
176 }
177 return page;
178}
179 162
180extern int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma, 163extern int do_huge_pmd_numa_page(struct mm_struct *mm, struct vm_area_struct *vma,
181 unsigned long addr, pmd_t pmd, pmd_t *pmdp); 164 unsigned long addr, pmd_t pmd, pmd_t *pmdp);
@@ -205,7 +188,6 @@ static inline int split_huge_page(struct page *page)
205 do { } while (0) 188 do { } while (0)
206#define split_huge_page_pmd_mm(__mm, __address, __pmd) \ 189#define split_huge_page_pmd_mm(__mm, __address, __pmd) \
207 do { } while (0) 190 do { } while (0)
208#define compound_trans_head(page) compound_head(page)
209static inline int hugepage_madvise(struct vm_area_struct *vma, 191static inline int hugepage_madvise(struct vm_area_struct *vma,
210 unsigned long *vm_flags, int advice) 192 unsigned long *vm_flags, int advice)
211{ 193{
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 3bf21c3502d0..a9a48309f045 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -361,8 +361,18 @@ static inline void compound_unlock_irqrestore(struct page *page,
361 361
362static inline struct page *compound_head(struct page *page) 362static inline struct page *compound_head(struct page *page)
363{ 363{
364 if (unlikely(PageTail(page))) 364 if (unlikely(PageTail(page))) {
365 return page->first_page; 365 struct page *head = page->first_page;
366
367 /*
368 * page->first_page may be a dangling pointer to an old
369 * compound page, so recheck that it is still a tail
370 * page before returning.
371 */
372 smp_rmb();
373 if (likely(PageTail(page)))
374 return head;
375 }
366 return page; 376 return page;
367} 377}
368 378