kernel/resource.c: fix stack overflow in __reserve_region_with_split()

Using a recursive call add a non-conflicting region in
__reserve_region_with_split() could result in a stack overflow in the case
that the recursive calls are too deep.  Convert the recursive calls to an
iterative loop to avoid the problem.

Tested on a machine containing 135 regions.  The kernel no longer panicked
with stack overflow.

Also tested with code arbitrarily adding regions with no conflict,
embedding two consecutive conflicts and embedding two non-consecutive
conflicts.

Signed-off-by: T Makphaibulchoke <tmac@hp.com>
Reviewed-by: Ram Pai <linuxram@us.ibm.com>
Cc: Paul Gortmaker <paul.gortmaker@gmail.com>
Cc: Wei Yang <weiyang@linux.vnet.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
T Makphaibulchoke 2012-10-04 17:16:55 -07:00 committed by Linus Torvalds
parent c99b6841d7
commit 4965f5667f
1 changed files with 38 additions and 12 deletions

View File

@ -763,6 +763,7 @@ static void __init __reserve_region_with_split(struct resource *root,
struct resource *parent = root;
struct resource *conflict;
struct resource *res = kzalloc(sizeof(*res), GFP_ATOMIC);
struct resource *next_res = NULL;
if (!res)
return;
@ -772,21 +773,46 @@ static void __init __reserve_region_with_split(struct resource *root,
res->end = end;
res->flags = IORESOURCE_BUSY;
conflict = __request_resource(parent, res);
if (!conflict)
return;
while (1) {
/* failed, split and try again */
kfree(res);
conflict = __request_resource(parent, res);
if (!conflict) {
if (!next_res)
break;
res = next_res;
next_res = NULL;
continue;
}
/* conflict covered whole area */
if (conflict->start <= start && conflict->end >= end)
return;
/* conflict covered whole area */
if (conflict->start <= res->start &&
conflict->end >= res->end) {
kfree(res);
WARN_ON(next_res);
break;
}
/* failed, split and try again */
if (conflict->start > res->start) {
end = res->end;
res->end = conflict->start - 1;
if (conflict->end < end) {
next_res = kzalloc(sizeof(*next_res),
GFP_ATOMIC);
if (!next_res) {
kfree(res);
break;
}
next_res->name = name;
next_res->start = conflict->end + 1;
next_res->end = end;
next_res->flags = IORESOURCE_BUSY;
}
} else {
res->start = conflict->end + 1;
}
}
if (conflict->start > start)
__reserve_region_with_split(root, start, conflict->start-1, name);
if (conflict->end < end)
__reserve_region_with_split(root, conflict->end+1, end, name);
}
void __init reserve_region_with_split(struct resource *root,