Thread (10 messages) read the whole thread 10 messages, 1 author, 1d ago
HOTtoday

Revision v8 of 8 in this series.

Revisions (8)
  1. v1 [diff vs current]
  2. v2 [diff vs current]
  3. v4 [diff vs current]
  4. v5 [diff vs current]
  5. v6 [diff vs current]
  6. v8 [diff vs current]
  7. v8 current
  8. v10 [diff vs current]

[PATCH net-next v8 4/9] net: mdio: realtek-rtl9300: extend controller structure lifetime

From: Markus Stockhausen <hidden>
Date: 2026-07-29 16:02:55
Also in: linux-devicetree
Subsystem: ethernet phy library, networking drivers, the rest · Maintainers: Andrew Lunn, Heiner Kallweit, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds

Currently, the driver allocates the otto_emdio_priv structure using
devm_kzalloc(), tightly coupling its lifetime to the platform device.
Additionally the driver registers up to four MDIO buses. During unbind
or removal the devm teardown process might free the controller private
data before the MDIO buses are fully unregistered. This can lead to a
use-after-free if the MDIO subsystem accesses bus-specific operations
(which rely on controller->priv) during the teardown phase.

Fix this by decoupling the lifetime of the private data from the
platform device and directly tying it to the registered MDIO buses:

- Switch from devm() to normal functions in controller probing and
  introduce a kref counter.
- For each successfully initialized bus, increment the kref counter and
  register a devm action to decrement it on cleanup.
- Free the controller->priv data after the final reference has been
  removed.

For the new logic to work properly the controller from now on relies
on at least one attached bus. If no bus is given probing fails as
the controller has no real justification for existence.

Signed-off-by: Markus Stockhausen <redacted>
---
 drivers/net/mdio/mdio-realtek-rtl9300.c | 56 +++++++++++++++++++------
 1 file changed, 44 insertions(+), 12 deletions(-)
diff --git a/drivers/net/mdio/mdio-realtek-rtl9300.c b/drivers/net/mdio/mdio-realtek-rtl9300.c
index 73ac5fdcd267..3b99978a2511 100644
--- a/drivers/net/mdio/mdio-realtek-rtl9300.c
+++ b/drivers/net/mdio/mdio-realtek-rtl9300.c
@@ -116,6 +116,7 @@
 #include <linux/bitmap.h>
 #include <linux/bits.h>
 #include <linux/find.h>
+#include <linux/kref.h>
 #include <linux/mdio.h>
 #include <linux/mfd/syscon.h>
 #include <linux/mutex.h>
@@ -198,6 +199,7 @@ struct otto_emdio_cmd_regs {
 
 struct otto_emdio_priv {
 	const struct otto_emdio_info *info;
+	struct kref kref;
 	struct regmap *regmap;
 	struct mutex lock; /* protect HW access */
 	DECLARE_BITMAP(valid_ports, MAX_PORTS);
@@ -582,6 +584,21 @@ static int otto_emdio_9310_setup_controller(struct otto_emdio_priv *priv)
 	return 0;
 }
 
+static void otto_emdio_cleanup_controller(struct kref *kref)
+{
+	struct otto_emdio_priv *priv = container_of(kref, struct otto_emdio_priv, kref);
+
+	mutex_destroy(&priv->lock);
+	kfree(priv);
+}
+
+static void otto_emdio_cleanup_bus(void *data)
+{
+	struct otto_emdio_priv *priv = data;
+
+	kref_put(&priv->kref, otto_emdio_cleanup_controller);
+}
+
 static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv,
 				 struct fwnode_handle *node)
 {
@@ -617,6 +634,11 @@ static int otto_emdio_probe_one(struct device *dev, struct otto_emdio_priv *priv
 
 	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", dev_name(dev), mdio_bus);
 
+	kref_get(&priv->kref);
+	err = devm_add_action_or_reset(&bus->dev, otto_emdio_cleanup_bus, priv);
+	if (err)
+		return dev_err_probe(dev, err, "cannot register cleanup action\n");
+
 	err = devm_of_mdiobus_register(dev, bus, to_of_node(node));
 	if (err)
 		return dev_err_probe(dev, err, "cannot register MDIO bus\n");
@@ -731,44 +753,54 @@ static int otto_emdio_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct otto_emdio_priv *priv;
+	int buses_registered = 0;
 	int err;
 
-	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	priv = kzalloc_obj(*priv, GFP_KERNEL);
 	if (!priv)
 		return -ENOMEM;
 
-	err = devm_mutex_init(dev, &priv->lock);
-	if (err)
-		return err;
+	kref_init(&priv->kref);
+	mutex_init(&priv->lock);
 
 	priv->info = device_get_match_data(dev);
 	priv->regmap = syscon_node_to_regmap(dev->parent->of_node);
-	if (IS_ERR(priv->regmap))
-		return PTR_ERR(priv->regmap);
+	if (IS_ERR(priv->regmap)) {
+		err = PTR_ERR(priv->regmap);
+		goto err_out;
+	}
 
 	platform_set_drvdata(pdev, priv);
 
 	err = otto_emdio_map_ports(dev);
 	if (err)
-		return err;
+		goto err_out;
 
 	err = otto_emdio_setup_topology(priv);
 	if (err)
-		return err;
+		goto err_out;
 
 	if (priv->info->setup_controller) {
 		err = priv->info->setup_controller(priv);
-		if (err)
-			return dev_err_probe(dev, err, "failed to setup MDIO bus controller\n");
+		if (err) {
+			dev_err_probe(dev, err, "failed to setup MDIO bus controller\n");
+			goto err_out;
+		}
 	}
 
 	device_for_each_child_node_scoped(dev, child) {
 		err = otto_emdio_probe_one(dev, priv, child);
 		if (err)
-			return err;
+			goto err_out;
+		buses_registered++;
 	}
 
-	return 0;
+	if (!buses_registered)
+		err = dev_err_probe(dev, -ENODEV, "no MDIO buses registered\n");
+err_out:
+	kref_put(&priv->kref, otto_emdio_cleanup_controller);
+
+	return err;
 }
 
 static const struct otto_emdio_info otto_emdio_9300_info = {
-- 
2.55.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help