diff mbox series

i2c: core: fix slab-use-after-free in i2c_adapter_depth()

Message ID 20260901152353.104578-1-ngocthang2710.1999@gmail.com
State New
Headers show
Series i2c: core: fix slab-use-after-free in i2c_adapter_depth() | expand

Commit Message

ThangNN99 Sept. 1, 2026, 3:23 p.m. UTC
i2c_adapter_depth() walks adapter->dev.parent assuming every
ancestor in the chain stays alive as long as the adapter does. That
is only true while each ancestor stays registered: device_add()
pins dev->parent, but device_del() unpins it regardless of whether
the child device's own memory is still kept alive by someone else.

This breaks for a USB-backed i2c adapter (e.g. radio-usb-si4713)
parented to its usb_device. If the host controller is unbound while
a userspace fd for the adapter is still open, the adapter's own
reference keeps the leaf usb_device alive, but that usb_device's
device_del() has already dropped its pin on its own parent (the
root hub), which then gets freed. A later i2c_transfer() walks into
that freed root hub and KASAN reports a use-after-free read.

Fix it by stopping the walk as soon as we reach a device that is no
longer registered, instead of trusting the rest of the chain.

Reported-by: syzbot+450abcfc7906fe1a1e16@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=450abcfc7906fe1a1e16
Signed-off-by: ThangNN99 <ngocthang2710.1999@gmail.com>
---
 drivers/i2c/i2c-core-base.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)
diff mbox series

Patch

diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index fb25704219c7..8c64a27eef5f 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -1240,7 +1240,12 @@  unsigned int i2c_adapter_depth(struct i2c_adapter *adapter)
 	unsigned int depth = 0;
 	struct device *parent;
 
-	for (parent = adapter->dev.parent; parent; parent = parent->parent)
+	/* An unregistered device may already be freed; stop there. */
+	if (!device_is_registered(&adapter->dev))
+		return depth;
+
+	for (parent = adapter->dev.parent; parent && device_is_registered(parent);
+	     parent = parent->parent)
 		if (parent->type == &i2c_adapter_type)
 			depth++;