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 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.