In mac_probe() there are multiple calls to of_find_device_by_node(),
fman_bind() and fman_port_bind() which takes references to of_dev->dev.
Not all references taken by these calls are released later on error path
in mac_probe() and in mac_remove() which lead to reference leaks.
Add references release.
Fixes: 3933961682a3 ("fsl/fman: Add FMan MAC driver")
Signed-off-by: Aleksandr Mishin <amishin@t-argos.ru>
---
Compile tested only.
drivers/net/ethernet/freescale/fman/mac.c | 62 +++++++++++++++++------
1 file changed, 47 insertions(+), 15 deletions(-)
diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c
index 9b863db0bf08..11da139082e1 100644
--- a/drivers/net/ethernet/freescale/fman/mac.c
+++ b/drivers/net/ethernet/freescale/fman/mac.c
@@ -204,7 +204,7 @@ static int mac_probe(struct platform_device *_of_dev)
if (err) {
dev_err(dev, "failed to read cell-index for %pOF\n", dev_node);
err = -EINVAL;
- goto _return_of_node_put;
+ goto _return_dev_put;
}
/* cell-index 0 => FMan id 1 */
fman_id = (u8)(val + 1);
@@ -213,40 +213,51 @@ static int mac_probe(struct platform_device *_of_dev)
if (!priv->fman) {
dev_err(dev, "fman_bind(%pOF) failed\n", dev_node);
err = -ENODEV;
- goto _return_of_node_put;
+ goto _return_dev_put;
}
+ /* Two references have been taken in of_find_device_by_node()
+ * and fman_bind(). Release one of them here. The second one
+ * will be released in mac_remove().
+ */
+ put_device(mac_dev->fman_dev);
of_node_put(dev_node);
+ dev_node = NULL;
/* Get the address of the memory mapped registers */
mac_dev->res = platform_get_mem_or_io(_of_dev, 0);
if (!mac_dev->res) {
dev_err(dev, "could not get registers\n");
- return -EINVAL;
+ err = -EINVAL;
+ goto _return_dev_put;
}
err = devm_request_resource(dev, fman_get_mem_region(priv->fman),
mac_dev->res);
if (err) {
dev_err_probe(dev, err, "could not request resource\n");
- return err;
+ goto _return_dev_put;
}
mac_dev->vaddr = devm_ioremap(dev, mac_dev->res->start,
resource_size(mac_dev->res));
if (!mac_dev->vaddr) {
dev_err(dev, "devm_ioremap() failed\n");
- return -EIO;
+ err = -EIO;
+ goto _return_dev_put;
}
- if (!of_device_is_available(mac_node))
- return -ENODEV;
+ if (!of_device_is_available(mac_node)) {
+ err = -ENODEV;
+ goto _return_dev_put;
+ }
/* Get the cell-index */
err = of_property_read_u32(mac_node, "cell-index", &val);
if (err) {
dev_err(dev, "failed to read cell-index for %pOF\n", mac_node);
- return -EINVAL;
+ err = -EINVAL;
+ goto _return_dev_put;
}
priv->cell_index = (u8)val;
@@ -260,22 +271,26 @@ static int mac_probe(struct platform_device *_of_dev)
if (unlikely(nph < 0)) {
dev_err(dev, "of_count_phandle_with_args(%pOF, fsl,fman-ports) failed\n",
mac_node);
- return nph;
+ err = nph;
+ goto _return_dev_put;
}
if (nph != ARRAY_SIZE(mac_dev->port)) {
dev_err(dev, "Not supported number of fman-ports handles of mac node %pOF from device tree\n",
mac_node);
- return -EINVAL;
+ err = -EINVAL;
+ goto _return_dev_put;
}
- for (i = 0; i < ARRAY_SIZE(mac_dev->port); i++) {
+ /* PORT_NUM determines the size of the port array */
+ for (i = 0; i < PORT_NUM; i++) {
/* Find the port node */
dev_node = of_parse_phandle(mac_node, "fsl,fman-ports", i);
if (!dev_node) {
dev_err(dev, "of_parse_phandle(%pOF, fsl,fman-ports) failed\n",
mac_node);
- return -EINVAL;
+ err = -EINVAL;
+ goto _return_dev_arr_put;
}
of_dev = of_find_device_by_node(dev_node);
@@ -283,7 +298,7 @@ static int mac_probe(struct platform_device *_of_dev)
dev_err(dev, "of_find_device_by_node(%pOF) failed\n",
dev_node);
err = -EINVAL;
- goto _return_of_node_put;
+ goto _return_dev_arr_put;
}
mac_dev->fman_port_devs[i] = &of_dev->dev;
@@ -292,9 +307,15 @@ static int mac_probe(struct platform_device *_of_dev)
dev_err(dev, "dev_get_drvdata(%pOF) failed\n",
dev_node);
err = -EINVAL;
- goto _return_of_node_put;
+ goto _return_dev_arr_put;
}
+ /* Two references have been taken in of_find_device_by_node()
+ * and fman_port_bind(). Release one of them here. The second
+ * one will be released in mac_remove().
+ */
+ put_device(mac_dev->fman_port_devs[i]);
of_node_put(dev_node);
+ dev_node = NULL;
}
/* Get the PHY connection type */
@@ -314,7 +335,7 @@ static int mac_probe(struct platform_device *_of_dev)
err = init(mac_dev, mac_node, ¶ms);
if (err < 0)
- return err;
+ goto _return_dev_arr_put;
if (!is_zero_ether_addr(mac_dev->addr))
dev_info(dev, "FMan MAC address: %pM\n", mac_dev->addr);
@@ -329,6 +350,12 @@ static int mac_probe(struct platform_device *_of_dev)
return err;
+_return_dev_arr_put:
+ /* mac_dev is kzalloc'ed */
+ for (i = 0; i < PORT_NUM; i++)
+ put_device(mac_dev->fman_port_devs[i]);
+_return_dev_put:
+ put_device(mac_dev->fman_dev);
_return_of_node_put:
of_node_put(dev_node);
return err;
@@ -337,6 +364,11 @@ static int mac_probe(struct platform_device *_of_dev)
static void mac_remove(struct platform_device *pdev)
{
struct mac_device *mac_dev = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < PORT_NUM; i++)
+ put_device(mac_dev->fman_port_devs[i]);
+ put_device(mac_dev->fman_dev);
platform_device_unregister(mac_dev->priv->eth_dev);
}
--
2.30.2
On 10/15/24 08:01, Aleksandr Mishin wrote: > In mac_probe() there are multiple calls to of_find_device_by_node(), > fman_bind() and fman_port_bind() which takes references to of_dev->dev. > Not all references taken by these calls are released later on error path > in mac_probe() and in mac_remove() which lead to reference leaks. > > Add references release. > > Fixes: 3933961682a3 ("fsl/fman: Add FMan MAC driver") > Signed-off-by: Aleksandr Mishin <amishin@t-argos.ru> > --- > Compile tested only. > > drivers/net/ethernet/freescale/fman/mac.c | 62 +++++++++++++++++------ > 1 file changed, 47 insertions(+), 15 deletions(-) > > diff --git a/drivers/net/ethernet/freescale/fman/mac.c b/drivers/net/ethernet/freescale/fman/mac.c > index 9b863db0bf08..11da139082e1 100644 > --- a/drivers/net/ethernet/freescale/fman/mac.c > +++ b/drivers/net/ethernet/freescale/fman/mac.c > @@ -204,7 +204,7 @@ static int mac_probe(struct platform_device *_of_dev) > if (err) { > dev_err(dev, "failed to read cell-index for %pOF\n", dev_node); > err = -EINVAL; > - goto _return_of_node_put; > + goto _return_dev_put; We are after a succesful of_find_device_by_node and prior to fman_bind(), mac_dev->fman_dev refcount is 1 > @@ -213,40 +213,51 @@ static int mac_probe(struct platform_device *_of_dev) > if (!priv->fman) { > dev_err(dev, "fman_bind(%pOF) failed\n", dev_node); > err = -ENODEV; > - goto _return_of_node_put; > + goto _return_dev_put; > } > > + /* Two references have been taken in of_find_device_by_node() > + * and fman_bind(). Release one of them here. The second one > + * will be released in mac_remove(). > + */ > + put_device(mac_dev->fman_dev); > of_node_put(dev_node); > + dev_node = NULL; > > /* Get the address of the memory mapped registers */ > mac_dev->res = platform_get_mem_or_io(_of_dev, 0); > if (!mac_dev->res) { > dev_err(dev, "could not get registers\n"); > - return -EINVAL; > + err = -EINVAL; > + goto _return_dev_put; Here we are after a successful fman_bind(), mac_dev->fman_dev refcount is 2. _return_dev_put will drop a single reference, this error path looks buggy. Similar issue for the _return_dev_arr_put error path below. Cheers, Paolo
On 17.10.2024 13:01, Paolo Abeni wrote: > On 10/15/24 08:01, Aleksandr Mishin wrote: >> In mac_probe() there are multiple calls to of_find_device_by_node(), >> fman_bind() and fman_port_bind() which takes references to of_dev->dev. >> Not all references taken by these calls are released later on error path >> in mac_probe() and in mac_remove() which lead to reference leaks. >> >> Add references release. >> >> Fixes: 3933961682a3 ("fsl/fman: Add FMan MAC driver") >> Signed-off-by: Aleksandr Mishin <amishin@t-argos.ru> >> --- >> Compile tested only. >> >> drivers/net/ethernet/freescale/fman/mac.c | 62 +++++++++++++++++------ >> 1 file changed, 47 insertions(+), 15 deletions(-) >> >> diff --git a/drivers/net/ethernet/freescale/fman/mac.c >> b/drivers/net/ethernet/freescale/fman/mac.c >> index 9b863db0bf08..11da139082e1 100644 >> --- a/drivers/net/ethernet/freescale/fman/mac.c >> +++ b/drivers/net/ethernet/freescale/fman/mac.c >> @@ -204,7 +204,7 @@ static int mac_probe(struct platform_device >> *_of_dev) >> if (err) { >> dev_err(dev, "failed to read cell-index for %pOF\n", >> dev_node); >> err = -EINVAL; >> - goto _return_of_node_put; >> + goto _return_dev_put; > > We are after a succesful of_find_device_by_node and prior to > fman_bind(), mac_dev->fman_dev refcount is 1 Indeed. refcounts = 1. > >> @@ -213,40 +213,51 @@ static int mac_probe(struct platform_device >> *_of_dev) >> if (!priv->fman) { >> dev_err(dev, "fman_bind(%pOF) failed\n", dev_node); >> err = -ENODEV; >> - goto _return_of_node_put; >> + goto _return_dev_put; >> } refcounts: 1 + 1 = 2. >> + /* Two references have been taken in of_find_device_by_node() >> + * and fman_bind(). Release one of them here. The second one >> + * will be released in mac_remove(). >> + */ >> + put_device(mac_dev->fman_dev); refcounts: 2 - 1 = 1. >> of_node_put(dev_node); >> + dev_node = NULL; >> /* Get the address of the memory mapped registers */ >> mac_dev->res = platform_get_mem_or_io(_of_dev, 0); >> if (!mac_dev->res) { >> dev_err(dev, "could not get registers\n"); >> - return -EINVAL; >> + err = -EINVAL; >> + goto _return_dev_put; > > Here we are after a successful fman_bind(), mac_dev->fman_dev refcount > is 2. _return_dev_put will drop a single reference, this error path > looks buggy. We released 1 reference above with "put_device(mac_dev->fman_dev);". > > Similar issue for the _return_dev_arr_put error path below. Similar situation: we release 1 reference with "put_device(mac_dev->fman_port_devs[i]);". > > Cheers, > > Paolo > -- Kind regards Aleksandr
© 2016 - 2024 Red Hat, Inc.