2015-12-11 22:21:59

by Andrew Banman

[permalink] [raw]
Subject: [PATCH] drivers: memory: check for missing sections in test_pages_in_a_zone

test_pages_in_a_zone does not account for the possibility of missing
sections in the given pfn range. pfn_valid_within always returns 1 when
CONFIG_HOLES_IN_ZONE is not set, allowing invalid pfns from missing
sections to pass the test, leading to a kernel oops.

Wrap an additional pfn loop with PAGES_PER_SECTION granularity to check
for missing sections before proceeding into the zone-check code.

This also prevents a crash from offlining memory devices with missing
sections. Despite this, it may be a good idea to keep the related patch
'[PATCH 3/3] drivers: memory: prohibit offlining of memory blocks
with missing sections' because missing sections in a memory block may lead
to other problems not covered by the scope of this fix.

---
mm/memory_hotplug.c | 32 ++++++++++++++++++++------------
1 file changed, 20 insertions(+), 12 deletions(-)

diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 67d488a..0c5f876 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -1375,23 +1375,30 @@ int is_mem_section_removable(unsigned long start_pfn, unsigned long nr_pages)
*/
int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn)
{
- unsigned long pfn;
+ unsigned long pfn, sec_end_pfn;
struct zone *zone = NULL;
struct page *page;
int i;
- for (pfn = start_pfn;
+ for (pfn = start_pfn, sec_end_pfn = SECTION_ALIGN_UP(start_pfn);
pfn < end_pfn;
- pfn += MAX_ORDER_NR_PAGES) {
- i = 0;
- /* This is just a CONFIG_HOLES_IN_ZONE check.*/
- while ((i < MAX_ORDER_NR_PAGES) && !pfn_valid_within(pfn + i))
- i++;
- if (i == MAX_ORDER_NR_PAGES)
+ pfn = sec_end_pfn + 1, sec_end_pfn += PAGES_PER_SECTION) {
+ /* Make sure the memory section is present first */
+ if (!present_section_nr(pfn_to_section_nr(pfn)))
continue;
- page = pfn_to_page(pfn + i);
- if (zone && page_zone(page) != zone)
- return 0;
- zone = page_zone(page);
+ for (; pfn < sec_end_pfn && pfn < end_pfn;
+ pfn += MAX_ORDER_NR_PAGES) {
+ i = 0;
+ /* This is just a CONFIG_HOLES_IN_ZONE check.*/
+ while ((i < MAX_ORDER_NR_PAGES) &&
+ !pfn_valid_within(pfn + i))
+ i++;
+ if (i == MAX_ORDER_NR_PAGES)
+ continue;
+ page = pfn_to_page(pfn + i);
+ if (zone && page_zone(page) != zone)
+ return 0;
+ zone = page_zone(page);
+ }
}
return 1;
}
--
1.7.12.4


2015-12-11 22:52:57

by Andrew Banman

[permalink] [raw]
Subject: Re: [PATCH] drivers: memory: check for missing sections in test_pages_in_a_zone

On Fri, Dec 11, 2015 at 04:21:03PM -0600, Andrew Banman wrote:
> test_pages_in_a_zone does not account for the possibility of missing
> sections in the given pfn range. pfn_valid_within always returns 1 when
> CONFIG_HOLES_IN_ZONE is not set, allowing invalid pfns from missing
> sections to pass the test, leading to a kernel oops.
>
> Wrap an additional pfn loop with PAGES_PER_SECTION granularity to check
> for missing sections before proceeding into the zone-check code.
>
> This also prevents a crash from offlining memory devices with missing
> sections. Despite this, it may be a good idea to keep the related patch
> '[PATCH 3/3] drivers: memory: prohibit offlining of memory blocks
> with missing sections' because missing sections in a memory block may lead
> to other problems not covered by the scope of this fix.
>
> ---

Oops, the signed-off-by got absorbed the email headers. I'll resend. The patch
applies cleanly to the current mainline and to the 4.3.2 and 4.2.7 stable
branches. I'll edit the patch description to reflect that as well.

- Andrew