Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4965f566 authored by T Makphaibulchoke's avatar T Makphaibulchoke Committed by Linus Torvalds
Browse files

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: default avatarT Makphaibulchoke <tmac@hp.com>
Reviewed-by: default avatarRam Pai <linuxram@us.ibm.com>
Cc: Paul Gortmaker <paul.gortmaker@gmail.com>
Cc: Wei Yang <weiyang@linux.vnet.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent c99b6841
Loading
Loading
Loading
Loading
+38 −12
Original line number Original line Diff line number Diff line
@@ -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;


	while (1) {

		conflict = __request_resource(parent, res);
		conflict = __request_resource(parent, res);
	if (!conflict)
		if (!conflict) {
		return;
			if (!next_res)
				break;
			res = next_res;
			next_res = NULL;
			continue;
		}


	/* failed, split and try again */
		/* conflict covered whole area */
		if (conflict->start <= res->start &&
				conflict->end >= res->end) {
			kfree(res);
			kfree(res);
			WARN_ON(next_res);
			break;
		}


	/* conflict covered whole area */
		/* failed, split and try again */
	if (conflict->start <= start && conflict->end >= end)
		if (conflict->start > res->start) {
		return;
			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,