Bug 6490

Summary: Overflow on 32bit archs in page_alloc.c, setup_per_zone_pages_min
Product: Memory Management Reporter: ake.sandgren
Component: Page AllocatorAssignee: Paolo 'Blaisorblade' Giarrusso (blaisorblade)
Severity: normal CC: mbligh
Priority: P2    
Hardware: i386   
OS: Linux   
Kernel Version: 2.6.15 Subsystem:
Regression: --- Bisected commit-id:

Description ake.sandgren 2006-05-04 05:39:39 UTC
Most recent kernel where this bug did not occur:
Hardware Environment: Systems with 896MB of memory or more
Software Environment:
Problem Description:

There is a unsigned long overflow in page_alloc.c in setup_per_zone_pages_min.
unsigned long tmp;
spin_lock_irqsave(&zone->lru_lock, flags);
tmp = (pages_min * zone->present_pages) / lowmem_pages;

If min_free_kbytes is changed to a value slightly larger then 76260
the tmp = (... calculation will overflow on a 32bit arch resulting in setting
pages_min for the normal zone to a bad value.

There is no check in min_free_kbytes_sysctl_handler to cap min_free_kbytes to
prevent this from happening.

something like
tmp = (pages_min * (zone->present_pages >> 3))/(lowmem_pages >> 3)
will give the same result +- 1 page without overflowing for larger min_free_kbytes.

Steps to reproduce:
Look at /proc/zoneinfo
increase min_free_kbytes to 131072
Look at /proc/zoneinfo
Comment 1 Martin J. Bligh 2006-05-04 09:56:49 UTC
I think it's actually worse than this.

Surely zone->present_pages could be 2^24 on a 64GB system (OK, 63 highmem, but
who's counting), which means that min_free_kbytes is limited to about 1024K to
make it still work?

I'd be tempted to just do 

tmp = pages_min * (zone->present_pages / lowmem_pages), which will lose us 
a little granularity, but do we really care?

Worst case we could do something like:

tmp = (pages_min * ((zone->present_pages << 4) / lowmem_pages)) >> 4

What's the worst case for min_free_kbytes?
Comment 2 Paolo 'Blaisorblade' Giarrusso 2007-02-21 09:22:47 UTC
This should have been fixed by commit ac924c6034d9095f95ee889f7e31bbb9145da0c2 
in kernel 2.6.17, this bug should then be closed.