* [syzbot] [i2c?] WARNING: refcount bug in i2c_get_adapter (2)
@ 2026-04-22 20:31 syzbot
2026-04-25 21:45 ` Arjan van de Ven
0 siblings, 1 reply; 2+ messages in thread
From: syzbot @ 2026-04-22 20:31 UTC (permalink / raw)
To: linux-i2c, linux-kernel, syzkaller-bugs, wsa+renesas
Hello,
syzbot found the following issue on:
HEAD commit: e753c16cb3dd Merge tag 'spi-fix-v7.0-rc7' of git://git.ker..
git tree: upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=127f9106580000
kernel config: https://syzkaller.appspot.com/x/.config?x=45cb3c58fd963c27
dashboard link: https://syzkaller.appspot.com/bug?extid=c0291c8c9aaa473c7721
compiler: Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8
Unfortunately, I don't have any reproducer for this issue yet.
Downloadable assets:
disk image: https://storage.googleapis.com/syzbot-assets/5ae80002fa8a/disk-e753c16c.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/6ef912fb95e2/vmlinux-e753c16c.xz
kernel image: https://storage.googleapis.com/syzbot-assets/b5b121be2ebf/bzImage-e753c16c.xz
IMPORTANT: if you fix the issue, please add the following tag to the commit:
Reported-by: syzbot+c0291c8c9aaa473c7721@syzkaller.appspotmail.com
------------[ cut here ]------------
refcount_t: addition on 0; use-after-free.
WARNING: lib/refcount.c:25 at refcount_warn_saturate+0x9f/0x110 lib/refcount.c:25, CPU#0: syz.1.506/7352
Modules linked in:
CPU: 0 UID: 0 PID: 7352 Comm: syz.1.506 Tainted: G L syzkaller #0 PREEMPT_{RT,(full)}
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 03/18/2026
RIP: 0010:refcount_warn_saturate+0x9f/0x110 lib/refcount.c:25
Code: eb 66 85 db 74 3e 83 fb 01 75 4c e8 bb d6 25 fd 48 8d 3d 84 01 d6 0a 67 48 0f b9 3a eb 4a e8 a8 d6 25 fd 48 8d 3d 81 01 d6 0a <67> 48 0f b9 3a eb 37 e8 95 d6 25 fd 48 8d 3d 7e 01 d6 0a 67 48 0f
RSP: 0018:ffffc9001ca9f6d8 EFLAGS: 00010283
RAX: ffffffff849eaa68 RBX: 0000000000000002 RCX: 0000000000080000
RDX: ffffc90006421000 RSI: 00000000000006c7 RDI: ffffffff8f74abf0
RBP: 0000000000000000 R08: ffff888020331e80 R09: 0000000000000005
R10: 0000000000000100 R11: 0000000000000004 R12: ffffffff8c04a688
R13: dffffc0000000000 R14: ffff88803b531188 R15: dffffc0000000000
FS: 00007f9769ca66c0(0000) GS:ffff888126332000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007f6df4ab1d80 CR3: 00000000348d0000 CR4: 00000000003526f0
Call Trace:
<TASK>
__refcount_add include/linux/refcount.h:-1 [inline]
__refcount_inc include/linux/refcount.h:366 [inline]
refcount_inc include/linux/refcount.h:383 [inline]
kref_get include/linux/kref.h:45 [inline]
kobject_get+0xfa/0x120 lib/kobject.c:643
i2c_get_adapter+0x6d/0xa0 drivers/i2c/i2c-core-base.c:2612
i2cdev_open+0x48/0x190 drivers/i2c/i2c-dev.c:603
chrdev_open+0x4d0/0x5f0 fs/char_dev.c:411
do_dentry_open+0x83d/0x13e0 fs/open.c:949
vfs_open+0x3b/0x350 fs/open.c:1081
do_open fs/namei.c:4677 [inline]
path_openat+0x2e43/0x38a0 fs/namei.c:4836
do_file_open+0x23e/0x4a0 fs/namei.c:4865
do_sys_openat2+0x113/0x200 fs/open.c:1366
do_sys_open fs/open.c:1372 [inline]
__do_sys_openat fs/open.c:1388 [inline]
__se_sys_openat fs/open.c:1383 [inline]
__x64_sys_openat+0x138/0x170 fs/open.c:1383
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x14d/0xf80 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f976ba0d04e
Code: 08 0f 85 a5 a8 ff ff 49 89 fb 48 89 f0 48 89 d7 48 89 ce 4c 89 c2 4d 89 ca 4c 8b 44 24 08 4c 8b 4c 24 10 4c 89 5c 24 08 0f 05 <c3> 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 80 00 00 00 00 48 83 ec 08
RSP: 002b:00007f9769ca5b28 EFLAGS: 00000246 ORIG_RAX: 0000000000000101
RAX: ffffffffffffffda RBX: 00007f9769ca66c0 RCX: 00007f976ba0d04e
RDX: 0000000000000402 RSI: 00007f9769ca5c00 RDI: ffffffffffffff9c
RBP: 00007f9769ca5c00 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: cccccccccccccccd
R13: 00007f976bcc6038 R14: 00007f976bcc5fa0 R15: 00007fffc400e638
</TASK>
----------------
Code disassembly (best guess):
0: eb 66 jmp 0x68
2: 85 db test %ebx,%ebx
4: 74 3e je 0x44
6: 83 fb 01 cmp $0x1,%ebx
9: 75 4c jne 0x57
b: e8 bb d6 25 fd call 0xfd25d6cb
10: 48 8d 3d 84 01 d6 0a lea 0xad60184(%rip),%rdi # 0xad6019b
17: 67 48 0f b9 3a ud1 (%edx),%rdi
1c: eb 4a jmp 0x68
1e: e8 a8 d6 25 fd call 0xfd25d6cb
23: 48 8d 3d 81 01 d6 0a lea 0xad60181(%rip),%rdi # 0xad601ab
* 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2f: eb 37 jmp 0x68
31: e8 95 d6 25 fd call 0xfd25d6cb
36: 48 8d 3d 7e 01 d6 0a lea 0xad6017e(%rip),%rdi # 0xad601bb
3d: 67 addr32
3e: 48 rex.W
3f: 0f .byte 0xf
---
This report is generated by a bot. It may contain errors.
See https://goo.gl/tpsmEJ for more information about syzbot.
syzbot engineers can be reached at syzkaller@googlegroups.com.
syzbot will keep track of this issue. See:
https://goo.gl/tpsmEJ#status for how to communicate with syzbot.
If the report is already addressed, let syzbot know by replying with:
#syz fix: exact-commit-title
If you want to overwrite report's subsystems, reply with:
#syz set subsystems: new-subsystem
(See the list of subsystem names on the web dashboard)
If the report is a duplicate of another one, reply with:
#syz dup: exact-subject-of-another-report
If you want to undo deduplication, reply with:
#syz undup
^ permalink raw reply [flat|nested] 2+ messages in thread* Re: [syzbot] [i2c?] WARNING: refcount bug in i2c_get_adapter (2)
2026-04-22 20:31 [syzbot] [i2c?] WARNING: refcount bug in i2c_get_adapter (2) syzbot
@ 2026-04-25 21:45 ` Arjan van de Ven
0 siblings, 0 replies; 2+ messages in thread
From: Arjan van de Ven @ 2026-04-25 21:45 UTC (permalink / raw)
To: linux-i2c
Cc: linux-kernel, syzkaller-bugs, wsa+renesas,
syzbot+c0291c8c9aaa473c7721
This email is created by automation to help kernel developers
deal with a large volume of AI generated bug reports by decoding
oopses into more actionable information.
Decoded Backtrace
0. refcount_warn_saturate -- lib/refcount.c:25 (crash site)
11 #define REFCOUNT_WARN(str) WARN_ONCE(1, "refcount_t: " str ".\n")
12
13 void refcount_warn_saturate(refcount_t *r,
13 enum refcount_saturation_type t)
14 {
15 refcount_set(r, REFCOUNT_SATURATED);
16
17 switch (t) {
18 case REFCOUNT_ADD_NOT_ZERO_OVF:
19 case REFCOUNT_ADD_OVF:
20 REFCOUNT_WARN("saturated; leaking memory");
21 break;
22 case REFCOUNT_ADD_UAF:
23 case REFCOUNT_SUB_UAF:
-> 25 REFCOUNT_WARN("addition on 0; use-after-free");
25 break;
26 case REFCOUNT_DEC_LEAK:
27 REFCOUNT_WARN("decrement hit 0; leaking memory");
28 break;
29 default:
30 REFCOUNT_WARN("unknown saturation event!?");
31 }
32 }
RBX = 0x2 = REFCOUNT_ADD_UAF, confirming the switch branch taken.
5. kobject_get -- lib/kobject.c:643
636 struct kobject *kobject_get(struct kobject *kobj)
637 {
638 if (kobj) {
639 if (!kobj->state_initialized)
640 WARN(1, KERN_WARNING
641 "kobject: '%s' (%p): is not initialized, "
641 "yet kobject_get() is being called.\n",
642 kobject_name(kobj), kobj);
-> 643 kref_get(&kobj->kref); /* unconditional increment */
644 }
645 return kobj;
646 }
kobject_get_unless_zero() (line 649) checks for zero first via
kref_get_unless_zero, but is not used here.
6. i2c_get_adapter -- drivers/i2c/i2c-core-base.c:2612
2602 struct i2c_adapter *i2c_get_adapter(int nr)
2603 {
2604 struct i2c_adapter *adapter;
2605
2606 mutex_lock(&core_lock);
2607 adapter = idr_find(&i2c_adapter_idr, nr);
2608 if (!adapter)
2609 goto exit;
2610
2611 if (try_module_get(adapter->owner))
->2612 get_device(&adapter->dev); /* crash: dev refcount == 0 */
2613 else
2614 adapter = NULL;
2615
2616 exit:
2617 mutex_unlock(&core_lock);
2618 return adapter;
2619 }
7. i2cdev_open -- drivers/i2c/i2c-dev.c:603
597 static int i2cdev_open(struct inode *inode, struct file *file)
598 {
599 unsigned int minor = iminor(inode);
600 struct i2c_client *client;
601 struct i2c_adapter *adap;
602
-> 603 adap = i2c_get_adapter(minor);
604 if (!adap)
605 return -ENODEV;
...
618 }
Tentative Analysis
The crash is a "refcount_t: addition on 0; use-after-free" WARNING in
refcount_warn_saturate(), triggered when get_device() is called on an
I2C adapter whose struct device refcount has already reached zero.
The sequence of events:
1. A task opens /dev/i2c-N, which calls i2cdev_open(), which calls
i2c_get_adapter(minor).
2. i2c_get_adapter() takes core_lock, calls idr_find(), and finds the
adapter in the IDR (non-NULL). try_module_get() succeeds.
get_device(&adapter->dev) is then called.
3. Concurrently, i2c_del_adapter() has already called
device_unregister(&adap->dev), which drops the device refcount to
zero. At this point the adapter is still in the IDR: idr_remove()
is not called until after wait_for_completion() returns.
4. There is therefore a window where i2c_get_adapter() can find the
adapter in the IDR and attempt get_device() on an object whose
kobject refcount is already 0, triggering the WARNING.
The race exists because i2c_del_adapter() removes the adapter from
the IDR only *after* wait_for_completion(), which itself is called
after device_unregister() (which drops the device refcount to zero
when there is only one reference). i2c_get_adapter() holds core_lock
for its entire body including the get_device() call, but
i2c_del_adapter() releases core_lock before calling
device_unregister(), so the two can execute concurrently.
The bug was introduced by commit 611e12ea0f12 ("i2c: core: manage i2c
bus device refcount in i2c_[get|put]_adapter", 2015), which added
get_device() to i2c_get_adapter() without reordering the idr_remove()
call in i2c_del_adapter(). Before that commit only try_module_get()
was called, so the idr_remove() ordering was inconsequential.
Potential Solution
Move the idr_remove() call in i2c_del_adapter() to before the
device_unregister() call, still under core_lock. Once the adapter is
removed from the IDR, any concurrent i2c_get_adapter() call will
receive NULL from idr_find() and return -ENODEV. Callers that already
obtained a device reference before the idr_remove() hold a legitimate
reference; wait_for_completion() will correctly wait for them to
release it via i2c_put_adapter().
Rough patch (against e753c16cb3dd):
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ i2c_del_adapter
+ /* Remove from IDR before device_unregister() to prevent a
+ * concurrent i2c_get_adapter() from calling get_device() on a
+ * kobject whose refcount has already reached zero.
+ */
+ mutex_lock(&core_lock);
+ idr_remove(&i2c_adapter_idr, adap->nr);
+ mutex_unlock(&core_lock);
+
/* wait until all references to the device are gone ... */
init_completion(&adap->dev_released);
device_unregister(&adap->dev);
wait_for_completion(&adap->dev_released);
- /* free bus id */
- mutex_lock(&core_lock);
- idr_remove(&i2c_adapter_idr, adap->nr);
- mutex_unlock(&core_lock);
-
More information
Oops-Analysis: http://oops.fenrus.org/reports/lkml/69e93024.a00a0220.17a17.0031.GAE_google.com/
Assisted-by: GitHub Copilot linux-kernel-oops-x86.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-04-25 21:44 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-22 20:31 [syzbot] [i2c?] WARNING: refcount bug in i2c_get_adapter (2) syzbot
2026-04-25 21:45 ` Arjan van de Ven
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.