2012-06-15 21:09:47

by Pearson, Greg

[permalink] [raw]
Subject: [PATCH] mm/memblock: fix overlapping allocation when doubling reserved array

The __alloc_memory_core_early() routine will ask memblock for a range
of memory then try to reserve it. If the reserved region array lacks
space for the new range, memblock_double_array() is called to allocate
more space for the array. If memblock is used to allocate memory for
the new array it can end up using a range that overlaps with the range
originally allocated in __alloc_memory_core_early(), leading to possible
data corruption.

With this patch memblock_double_array() now calls memblock_find_in_range()
with a narrowed candidate range so any memory allocated will not overlap
with the original range that was being reserved. The range is narrowed by
passing in the starting address of the previously allocated range as the
end of the candidate range. Since memblock_find_in_range_node() looks for
a free range by walking the free memory list in reverse order (highest
memory address to lowest address) this change should not unnecessarily
exclude chunks of memory that could otherwise be used to satisfy the
request.

Signed-off-by: Greg Pearson <[email protected]>
---
mm/memblock.c | 11 +++++++----
1 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/mm/memblock.c b/mm/memblock.c
index 952123e..599519c 100644
--- a/mm/memblock.c
+++ b/mm/memblock.c
@@ -184,7 +184,8 @@ static void __init_memblock memblock_remove_region(struct memblock_type *type, u
}
}

-static int __init_memblock memblock_double_array(struct memblock_type *type)
+static int __init_memblock memblock_double_array(struct memblock_type *type,
+ phys_addr_t skip_base)
{
struct memblock_region *new_array, *old_array;
phys_addr_t old_size, new_size, addr;
@@ -222,7 +223,8 @@ static int __init_memblock memblock_double_array(struct memblock_type *type)
new_array = kmalloc(new_size, GFP_KERNEL);
addr = new_array ? __pa(new_array) : 0;
} else {
- addr = memblock_find_in_range(0, MEMBLOCK_ALLOC_ACCESSIBLE, new_size, sizeof(phys_addr_t));
+ addr = memblock_find_in_range(0, skip_base,
+ new_size, sizeof(phys_addr_t));
new_array = addr ? __va(addr) : 0;
}
if (!addr) {
@@ -399,7 +401,8 @@ repeat:
*/
if (!insert) {
while (type->cnt + nr_new > type->max)
- if (memblock_double_array(type) < 0)
+ /* Avoid possible overlap if range is being reserved */
+ if (memblock_double_array(type, base) < 0)
return -ENOMEM;
insert = true;
goto repeat;
@@ -450,7 +453,7 @@ static int __init_memblock memblock_isolate_range(struct memblock_type *type,

/* we'll create at most two more regions */
while (type->cnt + 2 > type->max)
- if (memblock_double_array(type) < 0)
+ if (memblock_double_array(type, MEMBLOCK_ALLOC_ACCESSIBLE) < 0)
return -ENOMEM;

for (i = 0; i < type->cnt; i++) {
--
1.7.5.4


2012-06-15 22:05:15

by Yinghai Lu

[permalink] [raw]
Subject: Re: [PATCH] mm/memblock: fix overlapping allocation when doubling reserved array

On Fri, Jun 15, 2012 at 2:09 PM, Greg Pearson <[email protected]> wrote:
> The __alloc_memory_core_early() routine will ask memblock for a range
> of memory then try to reserve it. If the reserved region array lacks
> space for the new range, memblock_double_array() is called to allocate
> more space for the array. If memblock is used to allocate memory for
> the new array it can end up using a range that overlaps with the range
> originally allocated in __alloc_memory_core_early(), leading to possible
> data corruption.
>
> With this patch memblock_double_array() now calls memblock_find_in_range()
> with a narrowed candidate range so any memory allocated will not overlap
> with the original range that was being reserved. The range is narrowed by
> passing in the starting address of the previously allocated range as the
> end of the candidate range. Since memblock_find_in_range_node() looks for
> a free range by walking the free memory list in reverse order (highest
> memory address to lowest address) this change should not unnecessarily
> exclude chunks of memory that could otherwise be used to satisfy the
> request.

old early_res version have exclude_start/exclude_end.

>
> Signed-off-by: Greg Pearson <[email protected]>
> ---
> ?mm/memblock.c | ? 11 +++++++----
> ?1 files changed, 7 insertions(+), 4 deletions(-)
>
> diff --git a/mm/memblock.c b/mm/memblock.c
> index 952123e..599519c 100644
> --- a/mm/memblock.c
> +++ b/mm/memblock.c
> @@ -184,7 +184,8 @@ static void __init_memblock memblock_remove_region(struct memblock_type *type, u
> ? ? ? ?}
> ?}
>
> -static int __init_memblock memblock_double_array(struct memblock_type *type)
> +static int __init_memblock memblock_double_array(struct memblock_type *type,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? phys_addr_t skip_base)

could pass phys_addr_t exclude_base, phys_addr_t execlude_end

> ?{
> ? ? ? ?struct memblock_region *new_array, *old_array;
> ? ? ? ?phys_addr_t old_size, new_size, addr;
> @@ -222,7 +223,8 @@ static int __init_memblock memblock_double_array(struct memblock_type *type)
> ? ? ? ? ? ? ? ?new_array = kmalloc(new_size, GFP_KERNEL);
> ? ? ? ? ? ? ? ?addr = new_array ? __pa(new_array) : 0;
> ? ? ? ?} else {
> - ? ? ? ? ? ? ? addr = memblock_find_in_range(0, MEMBLOCK_ALLOC_ACCESSIBLE, new_size, sizeof(phys_addr_t));
> + ? ? ? ? ? ? ? addr = memblock_find_in_range(0, skip_base,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? new_size, sizeof(phys_addr_t));

could try to search [exclude_end, MEMBLOCK_ALLOC_ACCESSIBLE) at first.
then try [0, execlude_start).

Yinghai

2012-06-15 22:39:15

by Yinghai Lu

[permalink] [raw]
Subject: Re: [PATCH] mm/memblock: fix overlapping allocation when doubling reserved array

On Fri, Jun 15, 2012 at 2:09 PM, Greg Pearson <[email protected]> wrote:
> The __alloc_memory_core_early() routine will ask memblock for a range
> of memory then try to reserve it. If the reserved region array lacks
> space for the new range, memblock_double_array() is called to allocate
> more space for the array. If memblock is used to allocate memory for
> the new array it can end up using a range that overlaps with the range
> originally allocated in __alloc_memory_core_early(), leading to possible
> data corruption.
>
> @@ -399,7 +401,8 @@ repeat:
> ? ? ? ? */
> ? ? ? ?if (!insert) {
> ? ? ? ? ? ? ? ?while (type->cnt + nr_new > type->max)
> - ? ? ? ? ? ? ? ? ? ? ? if (memblock_double_array(type) < 0)
> + ? ? ? ? ? ? ? ? ? ? ? /* Avoid possible overlap if range is being reserved */
> + ? ? ? ? ? ? ? ? ? ? ? if (memblock_double_array(type, base) < 0)

should use obase here.

Yinghai

> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?return -ENOMEM;
> ? ? ? ? ? ? ? ?insert = true;
> ? ? ? ? ? ? ? ?goto repeat;