aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKirill A. Shutemov <kirill.shutemov@linux.intel.com>2018-01-19 07:49:24 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2018-01-21 20:44:47 -0500
commit0d665e7b109d512b7cae3ccef6e8654714887844 (patch)
treee6ebae5cd32b4f9008d06592a5301937c0be5875
parent0c5b9b5d9adbad4b60491f9ba0d2af38904bb4b9 (diff)
mm, page_vma_mapped: Drop faulty pointer arithmetics in check_pte()
Tetsuo reported random crashes under memory pressure on 32-bit x86 system and tracked down to change that introduced page_vma_mapped_walk(). The root cause of the issue is the faulty pointer math in check_pte(). As ->pte may point to an arbitrary page we have to check that they are belong to the section before doing math. Otherwise it may lead to weird results. It wasn't noticed until now as mem_map[] is virtually contiguous on flatmem or vmemmap sparsemem. Pointer arithmetic just works against all 'struct page' pointers. But with classic sparsemem, it doesn't because each section memap is allocated separately and so consecutive pfns crossing two sections might have struct pages at completely unrelated addresses. Let's restructure code a bit and replace pointer arithmetic with operations on pfns. Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Reported-and-tested-by: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp> Acked-by: Michal Hocko <mhocko@suse.com> Fixes: ace71a19cec5 ("mm: introduce page_vma_mapped_walk()") Cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--include/linux/swapops.h21
-rw-r--r--mm/page_vma_mapped.c63
2 files changed, 59 insertions, 25 deletions
diff --git a/include/linux/swapops.h b/include/linux/swapops.h
index 9c5a2628d6ce..1d3877c39a00 100644
--- a/include/linux/swapops.h
+++ b/include/linux/swapops.h
@@ -124,6 +124,11 @@ static inline bool is_write_device_private_entry(swp_entry_t entry)
124 return unlikely(swp_type(entry) == SWP_DEVICE_WRITE); 124 return unlikely(swp_type(entry) == SWP_DEVICE_WRITE);
125} 125}
126 126
127static inline unsigned long device_private_entry_to_pfn(swp_entry_t entry)
128{
129 return swp_offset(entry);
130}
131
127static inline struct page *device_private_entry_to_page(swp_entry_t entry) 132static inline struct page *device_private_entry_to_page(swp_entry_t entry)
128{ 133{
129 return pfn_to_page(swp_offset(entry)); 134 return pfn_to_page(swp_offset(entry));
@@ -154,6 +159,11 @@ static inline bool is_write_device_private_entry(swp_entry_t entry)
154 return false; 159 return false;
155} 160}
156 161
162static inline unsigned long device_private_entry_to_pfn(swp_entry_t entry)
163{
164 return 0;
165}
166
157static inline struct page *device_private_entry_to_page(swp_entry_t entry) 167static inline struct page *device_private_entry_to_page(swp_entry_t entry)
158{ 168{
159 return NULL; 169 return NULL;
@@ -189,6 +199,11 @@ static inline int is_write_migration_entry(swp_entry_t entry)
189 return unlikely(swp_type(entry) == SWP_MIGRATION_WRITE); 199 return unlikely(swp_type(entry) == SWP_MIGRATION_WRITE);
190} 200}
191 201
202static inline unsigned long migration_entry_to_pfn(swp_entry_t entry)
203{
204 return swp_offset(entry);
205}
206
192static inline struct page *migration_entry_to_page(swp_entry_t entry) 207static inline struct page *migration_entry_to_page(swp_entry_t entry)
193{ 208{
194 struct page *p = pfn_to_page(swp_offset(entry)); 209 struct page *p = pfn_to_page(swp_offset(entry));
@@ -218,6 +233,12 @@ static inline int is_migration_entry(swp_entry_t swp)
218{ 233{
219 return 0; 234 return 0;
220} 235}
236
237static inline unsigned long migration_entry_to_pfn(swp_entry_t entry)
238{
239 return 0;
240}
241
221static inline struct page *migration_entry_to_page(swp_entry_t entry) 242static inline struct page *migration_entry_to_page(swp_entry_t entry)
222{ 243{
223 return NULL; 244 return NULL;
diff --git a/mm/page_vma_mapped.c b/mm/page_vma_mapped.c
index d22b84310f6d..956015614395 100644
--- a/mm/page_vma_mapped.c
+++ b/mm/page_vma_mapped.c
@@ -30,10 +30,29 @@ static bool map_pte(struct page_vma_mapped_walk *pvmw)
30 return true; 30 return true;
31} 31}
32 32
33/**
34 * check_pte - check if @pvmw->page is mapped at the @pvmw->pte
35 *
36 * page_vma_mapped_walk() found a place where @pvmw->page is *potentially*
37 * mapped. check_pte() has to validate this.
38 *
39 * @pvmw->pte may point to empty PTE, swap PTE or PTE pointing to arbitrary
40 * page.
41 *
42 * If PVMW_MIGRATION flag is set, returns true if @pvmw->pte contains migration
43 * entry that points to @pvmw->page or any subpage in case of THP.
44 *
45 * If PVMW_MIGRATION flag is not set, returns true if @pvmw->pte points to
46 * @pvmw->page or any subpage in case of THP.
47 *
48 * Otherwise, return false.
49 *
50 */
33static bool check_pte(struct page_vma_mapped_walk *pvmw) 51static bool check_pte(struct page_vma_mapped_walk *pvmw)
34{ 52{
53 unsigned long pfn;
54
35 if (pvmw->flags & PVMW_MIGRATION) { 55 if (pvmw->flags & PVMW_MIGRATION) {
36#ifdef CONFIG_MIGRATION
37 swp_entry_t entry; 56 swp_entry_t entry;
38 if (!is_swap_pte(*pvmw->pte)) 57 if (!is_swap_pte(*pvmw->pte))
39 return false; 58 return false;
@@ -41,37 +60,31 @@ static bool check_pte(struct page_vma_mapped_walk *pvmw)
41 60
42 if (!is_migration_entry(entry)) 61 if (!is_migration_entry(entry))
43 return false; 62 return false;
44 if (migration_entry_to_page(entry) - pvmw->page >=
45 hpage_nr_pages(pvmw->page)) {
46 return false;
47 }
48 if (migration_entry_to_page(entry) < pvmw->page)
49 return false;
50#else
51 WARN_ON_ONCE(1);
52#endif
53 } else {
54 if (is_swap_pte(*pvmw->pte)) {
55 swp_entry_t entry;
56 63
57 entry = pte_to_swp_entry(*pvmw->pte); 64 pfn = migration_entry_to_pfn(entry);
58 if (is_device_private_entry(entry) && 65 } else if (is_swap_pte(*pvmw->pte)) {
59 device_private_entry_to_page(entry) == pvmw->page) 66 swp_entry_t entry;
60 return true;
61 }
62 67
63 if (!pte_present(*pvmw->pte)) 68 /* Handle un-addressable ZONE_DEVICE memory */
69 entry = pte_to_swp_entry(*pvmw->pte);
70 if (!is_device_private_entry(entry))
64 return false; 71 return false;
65 72
66 /* THP can be referenced by any subpage */ 73 pfn = device_private_entry_to_pfn(entry);
67 if (pte_page(*pvmw->pte) - pvmw->page >= 74 } else {
68 hpage_nr_pages(pvmw->page)) { 75 if (!pte_present(*pvmw->pte))
69 return false;
70 }
71 if (pte_page(*pvmw->pte) < pvmw->page)
72 return false; 76 return false;
77
78 pfn = pte_pfn(*pvmw->pte);
73 } 79 }
74 80
81 if (pfn < page_to_pfn(pvmw->page))
82 return false;
83
84 /* THP can be referenced by any subpage */
85 if (pfn - page_to_pfn(pvmw->page) >= hpage_nr_pages(pvmw->page))
86 return false;
87
75 return true; 88 return true;
76} 89}
77 90