WARNING in __batadv_dat_purge

Status: moderation: reported on 2026/11/10 03:11
Subsystems: batman
Labels: prio:low
[Documentation on labels]
Reported-by: syzbot+aaff888db153ef05f947@syzkaller.appspotmail.com
First crash: 1h27m, last: 1h27m
✨ AI-generated root cause summary:

Under PREEMPT_RT kernels, __local_bh_enable_ip() enforces that hard IRQs are enabled using lockdep_assert_irqs_enabled(). When batadv_dat_purge() invokes __batadv_dat_purge(), spin_unlock_bh() calls local_bh_enable(), which trips kernel/softirq.c:289 because lockdep's per-CPU hardirqs_enabled tracker was 0 despite CPU hardware interrupts being enabled (EFLAGS.IF == 1). The lock and bottom-half operations in __batadv_dat_purge() and its callee batadv_dat_entry_put() are properly balanced, indicating lockdep's per-CPU IRQ tracking state became desynchronized from the CPU's hardware interrupt state.

▶ ▼ ✨ Full AI root cause analysis
Based on kernel commit b3dfbde62cf1:

Technical Root Cause and Crash Mechanism

1. Context and Involved Components

2. Triggering Sequence and Violation

  1. In batadv_dat_purge() (invoked as a delayed work item in the bat_events workqueue), __batadv_dat_purge() iterates over the hash table buckets of bat_priv->dat.hash:
    for (i = 0; i < bat_priv->dat.hash->size; i++) {
        head = &bat_priv->dat.hash->table[i];
        list_lock = &bat_priv->dat.hash->list_locks[i];
    
        spin_lock_bh(list_lock);
        hlist_for_each_entry_safe(dat_entry, node_tmp, head, hash_entry) {
            if (to_purge && !to_purge(dat_entry))
                continue;
    
            hlist_del_rcu(&dat_entry->hash_entry);
            batadv_dat_entry_put(dat_entry);
        }
        spin_unlock_bh(list_lock);
    }
    
  2. Under PREEMPT_RT, spin_unlock_bh(list_lock) in include/linux/spinlock_rt.h expands to:
    static __always_inline void spin_unlock_bh(spinlock_t *lock)
    {
        rt_spin_unlock(lock);
        local_bh_enable();
    }
    
  3. local_bh_enable() calls __local_bh_enable_ip(_THIS_IP_, SOFTIRQ_DISABLE_OFFSET). At the entry of __local_bh_enable_ip() (kernel/softirq.c:289):
    void __local_bh_enable_ip(unsigned long ip, unsigned int cnt)
    {
        bool preempt_on = preemptible();
        unsigned long flags;
        u32 pending;
        int curcnt;
    
        WARN_ON_ONCE(in_hardirq());
        lockdep_assert_irqs_enabled();
    
    where lockdep_assert_irqs_enabled() evaluates:
    WARN_ON_ONCE(__lockdep_enabled && !this_cpu_read(hardirqs_enabled));
    
  4. As revealed in the crash register dump, EFLAGS: 00010246 has bit 9 (IF, Interrupt Flag) set to 1, meaning hardware interrupts are indeed active on the CPU. However, lockdep's per-CPU tracking variable hardirqs_enabled read 0 (!this_cpu_read(hardirqs_enabled) was true), triggering the WARN_ON_ONCE.

3. Subsystem and Object Verification

  • In __batadv_dat_purge(), spin_lock_bh() and spin_unlock_bh() are strictly paired around each bucket.
  • Any purged entries invoke batadv_dat_entry_put(), which queues the object via kref_put() and kfree_rcu() (mm/slab_common.c:kvfree_call_rcu()), which uses raw_spin_unlock_irqrestore() and correctly maintains IRQ state flags.
  • Neither __batadv_dat_purge() nor batadv_dat_entry_put() disables hard IRQs. The assertion warning at kernel/softirq.c:289 is caused by lockdep's per-CPU hardirqs_enabled tracker becoming desynchronized from the CPU's hardware interrupt state prior to or concurrently with the bottom-half re-enabling in spin_unlock_bh().
▶ ▼ ✨ AI Jobs (1)
ID Workflow Result Correct Bug Created Started Finished Revision
a2314700 assessment-security DenialOfService: ❌ Exploitable: ❌ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ❌ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ✅ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ WARNING in __batadv_dat_purge 2026/11/10 02:25 2026/11/10 02:25 2026/11/10 03:00 5cecaf1f

Sample crash report:
------------[ cut here ]------------
__lockdep_enabled && !this_cpu_read(hardirqs_enabled)
WARNING: kernel/softirq.c:289 at __local_bh_enable_ip+0x1ea/0x2b0 kernel/softirq.c:289, CPU#1: kworker/u8:12/1182
Modules linked in:
CPU: 1 UID: 0 PID: 1182 Comm: kworker/u8:12 Not tainted syzkaller #0 PREEMPT_{RT,(full)} 
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 11/06/2026
Workqueue: bat_events batadv_dat_purge
RIP: 0010:__local_bh_enable_ip+0x1ea/0x2b0 kernel/softirq.c:289
Code: 5b 41 5c 41 5d 41 5e 41 5f 5d c3 cc cc cc cc cc 90 0f 0b 90 e9 66 fe ff ff e8 02 78 ec 09 41 f7 c4 00 02 00 00 75 d8 eb d7 90 <0f> 0b 90 e9 92 fe ff ff 90 0f 0b 90 e9 6f ff ff ff e8 e0 77 ec 09
RSP: 0018:ffffc9000694fa30 EFLAGS: 00010246
RAX: 0000000000000000 RBX: 0000000000000200 RCX: 0000000000000046
RDX: 0000000000000001 RSI: 0000000000000200 RDI: ffffffff8b3e742e
RBP: 0000000000000400 R08: ffffffff90d5a8bf R09: 1ffffffff21ab517
R10: dffffc0000000000 R11: fffffbfff21ab518 R12: ffffffff8b3e7790
R13: dffffc0000000000 R14: ffff888061f74b90 R15: 0000000000000001
FS:  0000000000000000(0000) GS:ffff88812480e000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fe1171a6000 CR3: 0000000034222000 CR4: 00000000003526f0
Call Trace:
 <TASK>
 local_bh_enable include/linux/bottom_half.h:33 [inline]
 spin_unlock_bh include/linux/spinlock_rt.h:122 [inline]
 __batadv_dat_purge+0x343/0x400 net/batman-adv/distributed-arp-table.c:252
 batadv_dat_purge+0x20/0x70 net/batman-adv/distributed-arp-table.c:271
 process_one_work kernel/workqueue.c:3495 [inline]
 process_scheduled_works+0xc3d/0x1630 kernel/workqueue.c:3578
 worker_thread+0xa47/0xfb0 kernel/workqueue.c:3659
 kthread+0x38b/0x470 kernel/kthread.c:436
 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
 </TASK>

Crashes (1):
Time Kernel Commit Syzkaller Config Log Report Syz repro C repro VM info Assets (help?) Manager Title
2026/11/10 02:25 upstream b3dfbde62cf1 70ec0380 .config console log report info [disk image] [vmlinux] [kernel image] ci2-upstream-fs WARNING in __batadv_dat_purge
* Struck through repros no longer work on HEAD.