[PATCH 2/2] drivers/base: fix corrupt device tree

Sascha Hauer s.hauer at pengutronix.de
Wed Dec 12 09:07:51 EST 2012


dev_add_child is a very unsafe function. If called multiple times
it allows setting the same device to different parents thus corrupting
the siblings list. This happens regularly since:

| commit c2e568d19c5c34a05a1002d25280bf113b72b752
| Author: Jean-Christophe PLAGNIOL-VILLARD <plagnioj at jcrosoft.com>
| Date:   Sat Nov 3 16:11:05 2012 +0100
|
|    bus: add bus device
|
|    automatically add it as parent of any bus device if none already specified
|
|    we have now a nice output per bus

If for example a FATfs is mounted this nice output per bus often ends with:

>     `---- fat0
>     `---- 0
>          `---- 0x86f0000087020031-0x86f000410df27124: /dev/<NULL>
>          `---- sram00
>               `---- 0x00000000-0xffffffffffffffff: /dev/<NULL>
>               `---- 0x00000000-0xffffffffffffffff: /dev/<NULL>
>               unable to handle NULL pointer dereference at address 0x0000000c
> pc : [<87f08a20>]    lr : [<87f08a04>]
> sp : 86eff8c0  ip : 87f3fbde  fp : ffffffff
> r10: ffffffff  r9 : 00000000  r8 : 00000003
> r7 : 86f075b8  r6 : 00000002  r5 : ffffffec  r4 : 86f07544
> r3 : 00000000  r2 : 43f900b4  r1 : 00000020  r0 : 00000005
> Flags: Nzcv  IRQs off  FIQs off  Mode SVC_32
> [<87f08a20>] (do_devinfo_subtree+0x90/0x130) from [<87f08a90>] (do_devinfo_subtree+0x100/0x130)
>
> [<87f3e070>] (unwind_backtrace+0x0/0x90) from [<87f28514>] (panic+0x28/0x3c)
> [<87f28514>] (panic+0x28/0x3c) from [<87f3e4b8>] (do_exception+0x10/0x14)
> [<87f3e4b8>] (do_exception+0x10/0x14) from [<87f3e544>] (do_data_abort+0x2c/0x38)
> [<87f3e544>] (do_data_abort+0x2c/0x38) from [<87f3e268>] (data_abort+0x48/0x60)

This patch fixes this by adding a device to its parents children list in
register_device so that dev_add_child is no longer needed. This function
is removed from the tree. Now callers of register_device have to clearly
set the parent *before* registering a device.

Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
Reported-by: Jan Lübbe <jlu at pengutronix.de>
---
 common/console.c           |    2 +-
 drivers/base/driver.c      |   32 +++++++++++---------------------
 drivers/i2c/i2c.c          |    4 ----
 drivers/mci/mci-core.c     |    2 +-
 drivers/mtd/core.c         |    4 +---
 drivers/net/phy/mdio_bus.c |    4 ----
 drivers/net/phy/phy.c      |    3 ++-
 drivers/spi/spi.c          |    2 +-
 drivers/w1/w1.c            |    4 ----
 fs/fs.c                    |    2 +-
 include/driver.h           |    5 -----
 net/eth.c                  |    2 +-
 12 files changed, 19 insertions(+), 47 deletions(-)

diff --git a/common/console.c b/common/console.c
index fdd5f42..a085e2d 100644
--- a/common/console.c
+++ b/common/console.c
@@ -151,7 +151,7 @@ int console_register(struct console_device *newcdev)
 	dev->id = DEVICE_ID_DYNAMIC;
 	strcpy(dev->name, "cs");
 	if (newcdev->dev)
-		dev_add_child(newcdev->dev, dev);
+		dev->parent = newcdev->dev;
 	platform_device_register(dev);
 
 	if (newcdev->setbrg) {
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 5b3542b..d4066fc 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -133,21 +133,21 @@ int register_device(struct device_d *new_device)
 	INIT_LIST_HEAD(&new_device->parameters);
 	INIT_LIST_HEAD(&new_device->active);
 
-	if (!new_device->bus)
-		return 0;
-
-	if (!new_device->parent) {
-		new_device->parent = &new_device->bus->dev;
-		dev_add_child(new_device->parent, new_device);
-	}
+	if (new_device->bus) {
+		if (!new_device->parent)
+			new_device->parent = &new_device->bus->dev;
 
-	list_add_tail(&new_device->bus_list, &new_device->bus->device_list);
+		list_add_tail(&new_device->bus_list, &new_device->bus->device_list);
 
-	bus_for_each_driver(new_device->bus, drv) {
-		if (!match(drv, new_device))
-			break;
+		bus_for_each_driver(new_device->bus, drv) {
+			if (!match(drv, new_device))
+				break;
+		}
 	}
 
+	if (new_device->parent)
+		list_add_tail(&new_device->sibling, &new_device->parent->children);
+
 	return 0;
 }
 EXPORT_SYMBOL(register_device);
@@ -186,16 +186,6 @@ int unregister_device(struct device_d *old_dev)
 }
 EXPORT_SYMBOL(unregister_device);
 
-int dev_add_child(struct device_d *dev, struct device_d *child)
-{
-	child->parent = dev;
-
-	list_add_tail(&child->sibling, &dev->children);
-
-	return 0;
-}
-EXPORT_SYMBOL(dev_add_child);
-
 struct driver_d *get_driver_by_name(const char *name)
 {
 	struct driver_d *drv;
diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c
index 07eace9..4862df3 100644
--- a/drivers/i2c/i2c.c
+++ b/drivers/i2c/i2c.c
@@ -258,7 +258,6 @@ struct i2c_client *i2c_new_device(struct i2c_adapter *adapter,
 	client->addr = chip->addr;
 
 	client->dev.parent = &adapter->dev;
-	dev_add_child(client->dev.parent, &client->dev);
 
 	status = register_device(&client->dev);
 
@@ -403,9 +402,6 @@ int i2c_add_numbered_adapter(struct i2c_adapter *adapter)
 	adapter->dev.id = adapter->nr;
 	strcpy(adapter->dev.name, "i2c");
 
-	if (adapter->dev.parent)
-		dev_add_child(adapter->dev.parent, &adapter->dev);
-
 	ret = register_device(&adapter->dev);
 	if (ret)
 		return ret;
diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 88892b6..e29bd2e 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -1557,7 +1557,7 @@ int mci_register(struct mci_host *host)
 	mci_dev->id = DEVICE_ID_DYNAMIC;
 	strcpy(mci_dev->name, mci_driver.name);
 	mci_dev->platform_data = host;
-	dev_add_child(host->hw_dev, mci_dev);
+	mci_dev->parent = host->hw_dev;
 
 	return platform_device_register(mci_dev);
 }
diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c
index b5916da..92b6435 100644
--- a/drivers/mtd/core.c
+++ b/drivers/mtd/core.c
@@ -184,10 +184,8 @@ int add_mtd_device(struct mtd_info *mtd, char *devname)
 		devname = "mtd";
 	strcpy(mtd->class_dev.name, devname);
 	mtd->class_dev.id = DEVICE_ID_DYNAMIC;
-	if (mtd->parent) {
+	if (mtd->parent)
 		mtd->class_dev.parent = mtd->parent;
-		dev_add_child(mtd->class_dev.parent, &mtd->class_dev);
-	}
 	register_device(&mtd->class_dev);
 
 	mtd->cdev.ops = &mtd_ops;
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 1d20bb0..d1d802b 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -47,8 +47,6 @@ int mdiobus_register(struct mii_bus *bus)
 	bus->dev.id = DEVICE_ID_DYNAMIC;
 	strcpy(bus->dev.name, "miibus");
 	bus->dev.parent = bus->parent;
-	if (bus->parent)
-		dev_add_child(bus->parent, &bus->dev);
 
 	err = register_device(&bus->dev);
 	if (err) {
@@ -157,8 +155,6 @@ static int mdio_bus_probe(struct device_d *_dev)
 	char str[16];
 
 	dev->attached_dev->phydev = dev;
-	dev->dev.parent = &dev->attached_dev->dev;
-	dev_add_child(dev->dev.parent, _dev);
 
 	if (drv->probe) {
 		ret = drv->probe(dev);
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index 58546f8..9622455 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -157,7 +157,6 @@ static struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int p
 	dev->phy_id = phy_id;
 
 	dev->bus = bus;
-	dev->dev.parent = bus->parent;
 	dev->dev.bus = &mdio_bus_type;
 
 	strcpy(dev->dev.name, "phy");
@@ -229,6 +228,8 @@ static int phy_register_device(struct phy_device* dev)
 {
 	int ret;
 
+	dev->dev.parent = &dev->attached_dev->dev;
+
 	ret = register_device(&dev->dev);
 	if (ret)
 		return ret;
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 6a5bd6d..d58b664 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -81,7 +81,7 @@ struct spi_device *spi_new_device(struct spi_master *master,
 	proxy->dev.id = DEVICE_ID_DYNAMIC;
 	proxy->dev.type_data = proxy;
 	proxy->dev.device_node = chip->device_node;
-	dev_add_child(master->dev, &proxy->dev);
+	proxy->dev.parent = master->dev;
 
 	/* drivers may modify this initial i/o setup */
 	status = master->setup(proxy);
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index 11b8320..d2f94c9 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -413,7 +413,6 @@ static int w1_device_register(struct w1_bus *bus, struct w1_device *dev)
 	dev->bus = bus;
 
 	dev->dev.parent = &bus->dev;
-	dev_add_child(dev->dev.parent, &dev->dev);
 
 	ret = register_device(&dev->dev);
 	if (ret)
@@ -600,10 +599,7 @@ int w1_bus_register(struct w1_bus *bus)
 
 	strcpy(bus->dev.name, "w1_bus");
 	bus->dev.id = DEVICE_ID_DYNAMIC;
-
 	bus->dev.parent = bus->parent;
-	if (bus->parent)
-		dev_add_child(bus->parent, &bus->dev);
 
 	ret = register_device(&bus->dev);
 	if (ret)
diff --git a/fs/fs.c b/fs/fs.c
index 4c8c61a..04331fc 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -1227,7 +1227,7 @@ int mount(const char *device, const char *fsname, const char *_path)
 		fsdev->cdev = cdev_by_name(device + 5);
 
 	if (fsdev->cdev) {
-		dev_add_child(fsdev->cdev->dev, &fsdev->dev);
+		fsdev->dev.parent = fsdev->cdev->dev;
 		fsdev->parent_device = fsdev->cdev->dev;
 	}
 
diff --git a/include/driver.h b/include/driver.h
index 98df4a1..7ad0374 100644
--- a/include/driver.h
+++ b/include/driver.h
@@ -158,11 +158,6 @@ int device_probe(struct device_d *dev);
  */
 int unregister_device(struct device_d *);
 
-/* Organize devices in a tree. These functions do _not_ register or
- * unregister a device. Only registered devices are allowed here.
- */
-int dev_add_child(struct device_d *dev, struct device_d *child);
-
 /* Iterate over a devices children
  */
 #define device_for_each_child(dev, child) \
diff --git a/net/eth.c b/net/eth.c
index 101fc10..493ecf9 100644
--- a/net/eth.c
+++ b/net/eth.c
@@ -260,7 +260,7 @@ int eth_register(struct eth_device *edev)
 	edev->dev.id = DEVICE_ID_DYNAMIC;
 
 	if (edev->parent)
-		dev_add_child(edev->parent, &edev->dev);
+		edev->dev.parent = edev->parent;
 
 	register_device(&edev->dev);
 
-- 
1.7.10.4




More information about the barebox mailing list