From: Arjan van de Ven <arjan@linux.intel.com>
To: linux-i2c@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, syzkaller-bugs@googlegroups.com,
wsa+renesas@sang-engineering.com,
syzbot+c0291c8c9aaa473c7721@syzkaller.appspotmail.com
Subject: Re: [syzbot] [i2c?] WARNING: refcount bug in i2c_get_adapter (2)
Date: Sat, 25 Apr 2026 14:45:15 -0700 [thread overview]
Message-ID: <20260425214530.424398-1-arjan@linux.intel.com> (raw)
In-Reply-To: <69e93024.a00a0220.17a17.0031.GAE@google.com>
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.
prev parent reply other threads:[~2026-04-25 21:44 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
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 message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260425214530.424398-1-arjan@linux.intel.com \
--to=arjan@linux.intel.com \
--cc=linux-i2c@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=syzbot+c0291c8c9aaa473c7721@syzkaller.appspotmail.com \
--cc=syzkaller-bugs@googlegroups.com \
--cc=wsa+renesas@sang-engineering.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.