| 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 |
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++;
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(-)