[PATCH RFC] i2c: core: fix race between adapter removal and new_device_store

"syzbot" <[email protected]>
Newsgroups dev.linux.lists.syzbot
Message-ID <[email protected]>
There is a race condition between the removal of an I2C adapter
(`i2c_del_adapter`) and the instantiation of a new I2C client device via
sysfs (`new_device_store`).

When an adapter is being removed, `i2c_del_adapter()` removes it from
`i2c_adapter_idr`, deregisters clients (emptying `adap->userspace_clients`
under `adap->userspace_clients_lock`), and recursively removes the
adapter's debugfs directory. Concurrently, a write to the `new_device`
sysfs attribute invokes `new_device_store()`, which calls
`i2c_new_client_device()` to instantiate and probe a new client. If this
happens after the adapter's debugfs directory is removed,
`i2c_device_probe()` will attempt to create a debugfs directory for the new
client using the now-removed (negative) adapter debugfs dentry as the
parent. This results in a NULL-pointer dereference in `inode_lock_nested()`
when `debugfs_create_dir()` tries to lock the parent's inode.

Additionally, if the crash did not occur, the newly created client would be
added to the `adap->userspace_clients` list after
`i2c_deregister_clients()` had already finished, leaking the device and
associated resources.

The crash trace looks like this:

Unable to handle kernel paging request at virtual address dfff800000000028
KASAN: null-ptr-deref in range [0x0000000000000140-0x0000000000000147]
...
Call trace:
 kasan_mem_to_shadow include/linux/kasan.h:65 [inline] (P)
 kasan_byte_accessible+0x10/0x20 mm/kasan/generic.c:210 (P)
 kasan_check_byte include/linux/kasan.h:402 [inline]
 lock_acquire+0xa4/0x364 kernel/locking/lockdep.c:5842
 down_write_nested+0x58/0xcc kernel/locking/rwsem.c:1757
 inode_lock_nested include/linux/fs.h:1069 [inline]
 __start_dirop+0xa0/0xf4 fs/namei.c:2918
 start_dirop+0x38/0x4c fs/namei.c:2942
 simple_start_creating+0xcc/0x114 fs/libfs.c:2305
 debugfs_start_creating+0xf8/0x1ac fs/debugfs/inode.c:394
 debugfs_create_dir+0x30/0x2f4 fs/debugfs/inode.c:572
 i2c_device_probe+0x7fc/0x9d4 drivers/i2c/i2c-core-base.c:588

To fix both the crash and the resource leak, synchronize
`new_device_store()` with `i2c_del_adapter()`. This is achieved by
extending the `adap->userspace_clients_lock` critical section in
`new_device_store()` to cover the entire client creation process.
Furthermore, a liveness check is added: while holding the lock, we check if
the adapter is still present in `i2c_adapter_idr` under `core_lock`. If the
adapter is already being removed, we safely abort and return `-ENODEV`.
This guarantees that if `new_device_store()` starts first,
`i2c_del_adapter()` will block waiting for `userspace_clients_lock`,
delaying the debugfs removal until the client is fully instantiated and
added to the list, ensuring it gets properly cleaned up.

Fixes: 99cd8e25875a ("i2c: Add a sysfs interface to instantiate devices")
Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot
Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=cc460646984184ad435a
Link: https://syzkaller.appspot.com/ai_job?id=f83d9281-0bbe-47a6-ac52-5b5911268adc
To: "Andi Shyti" <[email protected]>
To: <[email protected]>
To: "Jean Delvare" <[email protected]>
Cc: <[email protected]>

---
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index 3ec04787a..884a26695 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -1304,12 +1304,25 @@ new_device_store(struct device *dev, struct device_attribute *attr,
 		info.flags |= I2C_CLIENT_SLAVE;
 	}
 
+	mutex_lock_nested(&adap->userspace_clients_lock,
+			  i2c_adapter_depth(adap));
+
+	/* Check if the adapter is still registered / alive */
+	mutex_lock(&core_lock);
+	if (idr_find(&i2c_adapter_idr, adap->nr) != adap) {
+		mutex_unlock(&core_lock);
+		mutex_unlock(&adap->userspace_clients_lock);
+		return -ENODEV;
+	}
+	mutex_unlock(&core_lock);
+
 	client = i2c_new_client_device(adap, &info);
-	if (IS_ERR(client))
+	if (IS_ERR(client)) {
+		mutex_unlock(&adap->userspace_clients_lock);
 		return PTR_ERR(client);
+	}
 
 	/* Keep track of the added device */
-	mutex_lock(&adap->userspace_clients_lock);
 	list_add_tail(&client->detected, &adap->userspace_clients);
 	mutex_unlock(&adap->userspace_clients_lock);
 	dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",


base-commit: 075b74841bd0065a3bda3440873c747938e69b68
-- 
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to Sign-off the patch as a human author
and send it to the upstream kernel mailing lists.
Reply with '#syz reject' to reject it ('#syz unreject' to undo).

See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
You can comment on the patch as usual, syzbot will try to address
the comments and send a new version of the patch if necessary.
syzbot engineers can be reached at [email protected].
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.