[PATCH v2] sparc: Fix leaked platform_device references from of_find_device_by_node()

From: Ma Ke

Date: Fri Oct 16 2026 - 01:15:33 EST


of_find_device_by_node() is documented in drivers/of/platform.c:

* Takes a reference to the embedded struct device which needs to be
* dropped after use.

Several functions in arch/sparc/kernel/of_device_common.c look a
device up that way and never drop the reference:
irq_of_parse_and_map(), of_address_to_resource(), of_iomap() and
of_propagate_archdata(). On sparc these are the arch overrides of the
generic OF helpers, so a reference leaks on every interrupt mapping,
address translation and ioremap, and of_propagate_archdata()
additionally leaks one per child node because it recurses.

Release the reference once the value has been read out of the device.

Found by code review.

Fixes: 44266215e3c8 ("sparc: Implement irq_of_parse_and_map() and irq_dispose_mapping().")
Fixes: 930952af15f8 ("sparc: Implement of_address_to_resource().")
Fixes: 9889376ab91f ("sparc: Implement of_iomap().")
Fixes: 5059625ed886 ("sparc: Add OF archdata propagation helper.")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Ma Ke <make_ruc2021@xxxxxxx>
---
Changes in v2:
- Cover irq_of_parse_and_map(), of_address_to_resource() and
of_iomap() as well. They are in the same file and leaked the
reference in the same way, so the subject was widened from "in
archdata propagation" to match, and the Fixes tags are now one per
function.
- of_create_pci_dev() in arch/sparc/kernel/pci.c looks similar but is
not. It hands the device to pci_init_dev_archdata(), which stores it
in sd->op, and keeps using it afterwards, so the reference is
deliberately held there.
---
arch/sparc/kernel/of_device_common.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/arch/sparc/kernel/of_device_common.c b/arch/sparc/kernel/of_device_common.c
index 7f2f9972b59b..e2e1c1e97f90 100644
--- a/arch/sparc/kernel/of_device_common.c
+++ b/arch/sparc/kernel/of_device_common.c
@@ -15,11 +15,15 @@
unsigned int irq_of_parse_and_map(struct device_node *node, int index)
{
struct platform_device *op = of_find_device_by_node(node);
+ unsigned int irq;

if (!op || index >= op->archdata.num_irqs)
return 0;

- return op->archdata.irqs[index];
+ irq = op->archdata.irqs[index];
+ platform_device_put(op);
+
+ return irq;
}
EXPORT_SYMBOL(irq_of_parse_and_map);

@@ -32,6 +36,8 @@ int of_address_to_resource(struct device_node *node, int index,
return -EINVAL;

memcpy(r, &op->archdata.resource[index], sizeof(*r));
+ platform_device_put(op);
+
return 0;
}
EXPORT_SYMBOL_GPL(of_address_to_resource);
@@ -40,13 +46,16 @@ void __iomem *of_iomap(struct device_node *node, int index)
{
struct platform_device *op = of_find_device_by_node(node);
struct resource *r;
+ void __iomem *ret;

if (!op || index >= op->num_resources)
return NULL;

r = &op->archdata.resource[index];
+ ret = of_ioremap(r, 0, resource_size(r), (char *) r->name);
+ platform_device_put(op);

- return of_ioremap(r, 0, resource_size(r), (char *) r->name);
+ return ret;
}
EXPORT_SYMBOL(of_iomap);

@@ -70,6 +79,8 @@ void of_propagate_archdata(struct platform_device *bus)

if (dp->child)
of_propagate_archdata(op);
+
+ platform_device_put(op);
}
}

--
2.43.0