Messages in this thread Patch in this message | | | From | "Schmid, Carsten" <> | Subject | [PATCH v2] kernel/resource.c: invalidate parent when freed resource has childs | Date | Wed, 14 Aug 2019 14:48:24 +0000 |
| |
When a resource is freed and has children, the childrens are left without any hint that their parent is no more valid. This caused at least one use-after-free in the xhci-hcd using ext-caps driver when platform code released platform devices.
In such case, warn and release all resources beyond.
Signed-off-by: Carsten Schmid <carsten_schmid@mentor.com> --- v2: - release everything below the released resource, not only one child; re-using __release_child_resources (Inspired by Linus Torvalds outline) - warn only once (According to Linus Torvalds outline) - Keep parent and child name in warning message (eases hunting for the involved parties) --- kernel/resource.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-)
diff --git a/kernel/resource.c b/kernel/resource.c index c3cc6d85ec52..eb48d793aa74 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -239,7 +239,7 @@ static int __release_resource(struct resource *old, bool release_child) return -EINVAL; } -static void __release_child_resources(struct resource *r) +static void __release_child_resources(struct resource *r, bool warn) { struct resource *tmp, *p; resource_size_t size; @@ -252,9 +252,10 @@ static void __release_child_resources(struct resource *r) tmp->parent = NULL; tmp->sibling = NULL; - __release_child_resources(tmp); + __release_child_resources(tmp, warn); - printk(KERN_DEBUG "release child resource %pR\n", tmp); + if (warn) + printk(KERN_DEBUG "release child resource %pR\n", tmp); /* need to restore size, and keep flags */ size = resource_size(tmp); tmp->start = 0; @@ -265,7 +266,7 @@ static void __release_child_resources(struct resource *r) void release_child_resources(struct resource *r) { write_lock(&resource_lock); - __release_child_resources(r); + __release_child_resources(r, true); write_unlock(&resource_lock); } @@ -1172,7 +1173,20 @@ EXPORT_SYMBOL(__request_region); * @n: resource region size * * The described resource region must match a currently busy region. + * If the region has children they are released too. */ +static void check_children(struct resource *parent) +{ + if (parent->child) { + /* warn and release all children */ + WARN_ONCE(1, "%s: %s has child %s, release all children\n", + __func__, parent->name, parent->child->name); + write_lock(&resource_lock); + __release_child_resources(parent, false); + write_unlock(&resource_lock); + } +} + void __release_region(struct resource *parent, resource_size_t start, resource_size_t n) { @@ -1200,6 +1214,10 @@ void __release_region(struct resource *parent, resource_size_t start, write_unlock(&resource_lock); if (res->flags & IORESOURCE_MUXED) wake_up(&muxed_resource_wait); + + /* You should'nt release a resource that has children */ + check_children(res); + free_resource(res); return; } -- 2.17.1
| |