From: Arjan van de Ven <arjan@linux.intel.com>
To: netdev@vger.kernel.org
Cc: syzbot+ca9d5686d06994c6547c@syzkaller.appspotmail.com,
davem@davemloft.net, edumazet@google.com, horms@kernel.org,
kuba@kernel.org, linux-kernel@vger.kernel.org, pabeni@redhat.com,
syzkaller-bugs@googlegroups.com,
Arjan van de Ven <arjan@linux.intel.com>
Subject: Re: [syzbot] [net?] WARNING: ODEBUG bug in lane_ioctl (3)
Date: Wed, 29 Apr 2026 08:17:41 -0700 [thread overview]
Message-ID: <20260429151741.660828-1-arjan@linux.intel.com> (raw)
In-Reply-To: <69f16c26.170a0220.34e5b8.0013.GAE@google.com>
This email is created by automation to help kernel developers deal with
a large volume of bug reports by decoding oopses into more actionable
information.
Decoded Backtrace
1. __debug_object_init -- crash site (lib/debugobjects.c:632)
The WARN fires inside debug_print_object (inlined into
__debug_object_init). The object at 0xffff888077a60f78 (a timer_list) is
in the ACTIVE state when debug_object_init is called on it a second time.
611 static void debug_print_object(struct debug_obj *obj, char *msg)
612 {
613 const struct debug_obj_descr *descr = obj->descr;
614 static int limit;
622 if (!debug_objects_enabled)
623 return;
625 if (limit < 5 && descr != descr_test) {
626 void *hint = descr->debug_hint ?
627 descr->debug_hint(obj->object) : NULL;
628 limit++;
-> 629 WARN(1, KERN_ERR "ODEBUG: %s %s (active state %u) "
630 "object: %p object type: %s hint: %pS\n",
631 msg, obj_states[obj->state], obj->astate,
632 obj->object, descr->name, hint);
633 }
634 debug_objects_warnings++;
635 }
747 static void
748 __debug_object_init(void *addr, const struct debug_obj_descr *descr,
749 int onstack)
750 {
751 struct debug_obj *obj, o;
752 struct debug_bucket *db;
753 unsigned long flags;
755 debug_objects_fill_pool();
757 db = get_bucket((unsigned long) addr);
759 raw_spin_lock_irqsave(&db->lock, flags);
761 obj = lookup_object_or_alloc(addr, db, descr, onstack, false);
762 if (unlikely(!obj)) {
763 raw_spin_unlock_irqrestore(&db->lock, flags);
764 debug_objects_oom();
765 return;
766 }
768 switch (obj->state) {
769 case ODEBUG_STATE_NONE:
770 case ODEBUG_STATE_INIT:
771 case ODEBUG_STATE_INACTIVE:
772 obj->state = ODEBUG_STATE_INIT;
773 raw_spin_unlock_irqrestore(&db->lock, flags);
774 return;
775 default:
776 break;
777 }
779 o = *obj;
780 raw_spin_unlock_irqrestore(&db->lock, flags);
-> 780 debug_print_object(&o, "init");
783 if (o.state == ODEBUG_STATE_ACTIVE)
784 debug_object_fixup(descr->fixup_init, addr, o.state);
785 }
2. timer_init_key -- kernel/time/timer.c:880
786 static inline void debug_timer_init(struct timer_list *timer)
787 {
-> 788 debug_object_init(timer, &timer_debug_descr);
789 }
834 static inline void debug_init(struct timer_list *timer)
835 {
-> 836 debug_timer_init(timer);
837 trace_timer_init(timer);
838 }
876 void timer_init_key(struct timer_list *timer,
877 void (*func)(struct timer_list *), unsigned int flags,
878 const char *name, struct lock_class_key *key)
879 {
-> 880 debug_init(timer);
881 do_init_timer(timer, func, flags, name, key);
882 }
3. lane_ioctl / lecd_attach / lec_arp_init (net/atm/lec.c:1037)
1264 static void lec_arp_init(struct lec_priv *priv)
1265 {
1266 unsigned short i;
1268 for (i = 0; i < LEC_ARP_TABLE_SIZE; i++)
1269 INIT_HLIST_HEAD(&priv->lec_arp_tables[i]);
1270 INIT_HLIST_HEAD(&priv->lec_arp_empty_ones);
1271 INIT_HLIST_HEAD(&priv->lec_no_forward);
1272 INIT_HLIST_HEAD(&priv->mcast_fwds);
1273 spin_lock_init(&priv->lec_arp_lock);
->1274 INIT_DELAYED_WORK(&priv->lec_arp_work, lec_arp_check_expire);
1275 schedule_delayed_work(&priv->lec_arp_work, LEC_ARP_REFRESH_INTERVAL);
1276 }
748 static int lecd_attach(struct atm_vcc *vcc, int arg)
749 {
750 int i;
751 struct lec_priv *priv;
753 lockdep_assert_held(&lec_mutex);
754 if (arg < 0)
755 arg = 0;
756 if (arg >= MAX_LEC_ITF)
757 return -EINVAL;
758 i = array_index_nospec(arg, MAX_LEC_ITF);
759 if (!dev_lec[i]) {
763 dev_lec[i] = alloc_etherdev(size);
775 priv = netdev_priv(dev_lec[i]);
776 } else {
776 priv = netdev_priv(dev_lec[i]);
777 if (rcu_access_pointer(priv->lecd))
778 return -EADDRINUSE;
779 }
->781 lec_arp_init(priv); // called unconditionally for both new and
// existing priv -- no work cancellation
1018 static int lane_ioctl(struct socket *sock, unsigned int cmd,
1019 unsigned long arg)
1020 {
1034 mutex_lock(&lec_mutex);
1035 switch (cmd) {
1036 case ATMLEC_CTRL:
->1037 err = lecd_attach(vcc, (int)arg);
1038 if (err >= 0)
1039 sock->state = SS_CONNECTED;
1040 break;
1049 mutex_unlock(&lec_mutex);
1050 return err;
1051 }
4. do_vcc_ioctl (net/atm/ioctl.c:159)
153 error = -ENOIOCTLCMD;
155 mutex_lock(&ioctl_mutex);
156 list_for_each(pos, &ioctl_list) {
157 struct atm_ioctl *ic = list_entry(pos, struct atm_ioctl, list);
158 if (try_module_get(ic->owner)) {
->159 error = ic->ioctl(sock, cmd, arg); // dispatches to lane_ioctl
160 module_put(ic->owner);
161 if (error != -ENOIOCTLCMD)
162 break;
163 }
164 }
165 mutex_unlock(&ioctl_mutex);
Tentative Analysis
The ODEBUG WARNING fires when INIT_DELAYED_WORK() is called on a
timer_list (lec_priv.lec_arp_work) that is already in the ACTIVE state.
lec_arp_init() always calls INIT_DELAYED_WORK(&priv->lec_arp_work, ...)
followed by schedule_delayed_work(). lecd_attach() calls lec_arp_init()
unconditionally -- both for a brand-new device and for an existing one
in the else-branch. The only guard for the existing-device path is that
priv->lecd is NULL (no daemon currently attached).
The race is opened by lec_atm_close(), the ATM VCC close handler:
Thread A (lec_atm_close): Thread B (lecd_attach via lane_ioctl):
rcu_assign_pointer(lecd, NULL)
synchronize_rcu() mutex_lock(&lec_mutex)
[window open] sees priv->lecd == NULL -- passes guard
lec_arp_init(priv)
INIT_DELAYED_WORK on active timer
--> ODEBUG WARN
lec_arp_destroy(priv) [too late: work already re-initialized]
lec_atm_close() clears priv->lecd to NULL *before* calling
lec_arp_destroy() (which contains cancel_delayed_work_sync). Because
lec_atm_close() does not hold lec_mutex, Thread B can observe
priv->lecd == NULL while lec_arp_work is still active, pass the guard
in lecd_attach(), and call lec_arp_init() on a live timer.
The lec_mutex protecting dev_lec[] was introduced by commit d13a3824bfd2
("net: atm: add lec_mutex"), which serialised lecd_attach() and friends
but did not update lec_atm_close() to also acquire the mutex. The
unconditional lec_arp_init() call for existing devices predates that
commit and has always been present.
Potential Solution
Add cancel_delayed_work_sync(&priv->lec_arp_work) in the else-branch of
lecd_attach(), immediately before the call to lec_arp_init(). This
ensures any in-flight work is drained before the timer is re-initialized,
regardless of whether lec_atm_close() has already cancelled it.
cancel_delayed_work_sync() is safe to call from a lec_mutex-held context
because lec_arp_check_expire() only acquires priv->lec_arp_lock (a
spinlock) and never tries to take lec_mutex.
} else {
priv = netdev_priv(dev_lec[i]);
if (rcu_access_pointer(priv->lecd))
return -EADDRINUSE;
+ cancel_delayed_work_sync(&priv->lec_arp_work);
}
lec_arp_init(priv);
More information
Oops-Analysis: http://oops.fenrus.org/reports/lkml/69f16c26.170a0220.34e5b8.0013.GAE@google.com/
Assisted-by: GitHub-Copilot linux-kernel-oops.
next prev parent reply other threads:[~2026-04-29 15:16 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-29 2:25 [syzbot] [net?] WARNING: ODEBUG bug in lane_ioctl (3) syzbot
2026-04-29 15:17 ` Arjan van de Ven [this message]
2026-04-29 23:17 ` Jakub Kicinski
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=20260429151741.660828-1-arjan@linux.intel.com \
--to=arjan@linux.intel.com \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=syzbot+ca9d5686d06994c6547c@syzkaller.appspotmail.com \
--cc=syzkaller-bugs@googlegroups.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox