mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-09-22 04:31:58 +08:00
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:
parent
c99b6841d7
commit
4965f5667f
@ -763,6 +763,7 @@ static void __init __reserve_region_with_split(struct resource *root,
|
|||||||
struct resource *parent = root;
|
struct resource *parent = root;
|
||||||
struct resource *conflict;
|
struct resource *conflict;
|
||||||
struct resource *res = kzalloc(sizeof(*res), GFP_ATOMIC);
|
struct resource *res = kzalloc(sizeof(*res), GFP_ATOMIC);
|
||||||
|
struct resource *next_res = NULL;
|
||||||
|
|
||||||
if (!res)
|
if (!res)
|
||||||
return;
|
return;
|
||||||
@ -772,21 +773,46 @@ static void __init __reserve_region_with_split(struct resource *root,
|
|||||||
res->end = end;
|
res->end = end;
|
||||||
res->flags = IORESOURCE_BUSY;
|
res->flags = IORESOURCE_BUSY;
|
||||||
|
|
||||||
conflict = __request_resource(parent, res);
|
while (1) {
|
||||||
if (!conflict)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* failed, split and try again */
|
conflict = __request_resource(parent, res);
|
||||||
kfree(res);
|
if (!conflict) {
|
||||||
|
if (!next_res)
|
||||||
|
break;
|
||||||
|
res = next_res;
|
||||||
|
next_res = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/* conflict covered whole area */
|
/* conflict covered whole area */
|
||||||
if (conflict->start <= start && conflict->end >= end)
|
if (conflict->start <= res->start &&
|
||||||
return;
|
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,
|
void __init reserve_region_with_split(struct resource *root,
|
||||||
|
Loading…
Reference in New Issue
Block a user