Skip to contentSkip to conversation

WARNING in stub_timer (2)

syzbot Applied syzbot 3 participants 4 messages Jun 15 – Jun 22

Subsystems (3)
lore
Share & tools
lore

syzbot

author syzbot
#0 lore raw

Hello,

syzbot found the following issue on:

HEAD commit:    9716c086c8e8 Merge tag 'pm-7.1-rc8' of git://git.kernel.or..
git tree:       upstream
console output: https://syzkaller.appspot.com/x/log.txt?x=120533d2580000
kernel config:  https://syzkaller.appspot.com/x/.config?x=b4166e8ea5fbf7e3
dashboard link: https://syzkaller.appspot.com/bug?extid=5e8dda76ca21dae314b6
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 (non-bootable): https://storage.googleapis.com/syzbot-assets/d900f083ada3/non_bootable_disk-9716c086.raw.xz
vmlinux: https://storage.googleapis.com/syzbot-assets/c42e123755c8/vmlinux-9716c086.xz
kernel image: https://storage.googleapis.com/syzbot-assets/4eb934549042/bzImage-9716c086.xz

IMPORTANT: if you fix the issue, please add the following tag to the commit:

Reported-by [email protected]
------------[ cut here ]------------
1
WARNING: kernel/time/hrtimer.c:443 at stub_timer+0xa/0x20 kernel/time/timer.c:716, CPU#0: udevd/4706
Modules linked in:
CPU: 0 UID: 0 PID: 4706 Comm: udevd Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:stub_timer+0xa/0x20 kernel/time/timer.c:716
Code: cc 66 66 66 66 66 66 2e 0f 1f 84 00 00 00 00 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa e8 d7 1a 13 00 90 <0f> 0b 90 31 c0 c3 cc cc cc cc cc 66 66 2e 0f 1f 84 00 00 00 00 00
RSP: 0018:ffffc90000007dd0 EFLAGS: 00010006
RAX: ffffffff81b2ac39 RBX: 1ffff11000046617 RCX: ffff888000232540
RDX: 0000000000010000 RSI: ffffffff8c28d0c0 RDI: ffffc9000255fb20
RBP: ffffc9000255fb20 R08: ffffffff90305bf7 R09: 1ffffffff2060b7e
R10: dffffc0000000000 R11: ffffffff81b2ac30 R12: ffff8880002330b8
R13: 1ffff11003f8506a R14: dffffc0000000000 R15: ffffffff81b2ac30
FS:  00007f6e41fb1880(0000) GS:ffff88808c894000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00005589b464e660 CR3: 00000000128d0000 CR4: 0000000000352ef0
Call Trace:
 <IRQ>
 __run_hrtimer kernel/time/hrtimer.c:1930 [inline]
 __hrtimer_run_queues+0x3c0/0xa20 kernel/time/hrtimer.c:1994
 hrtimer_interrupt+0x44b/0x950 kernel/time/hrtimer.c:2113
 local_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1050 [inline]
 __sysvec_apic_timer_interrupt+0x102/0x430 arch/x86/kernel/apic/apic.c:1067
 instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1061 [inline]
 sysvec_apic_timer_interrupt+0xa1/0xc0 arch/x86/kernel/apic/apic.c:1061
 </IRQ>
 <TASK>
 asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:697
RIP: 0010:__raw_spin_unlock_irqrestore include/linux/spinlock_api_smp.h:179 [inline]
RIP: 0010:_raw_spin_unlock_irqrestore+0x47/0x80 kernel/locking/spinlock.c:198
Code: f7 e8 ad 28 f6 f5 f7 c3 00 02 00 00 74 05 e8 20 d2 21 f6 9c 58 a9 00 02 00 00 75 27 f7 c3 00 02 00 00 74 01 fb bf 01 00 00 00 <e8> c4 a7 e7 f5 65 8b 05 8d 9d 8c 07 85 c0 74 18 5b 41 5e c3 cc cc
RSP: 0018:ffffc9000255fac8 EFLAGS: 00000206
RAX: 0000000000000006 RBX: 0000000000000246 RCX: 0000000080000001
RDX: 0000000000000000 RSI: ffffffff8dfa9d91 RDI: 0000000000000001
RBP: ffffc9000255fbf0 R08: ffffffff90305bf7 R09: 1ffffffff2060b7e
R10: dffffc0000000000 R11: fffffbfff2060b7f R12: 1ffff920004abfae
R13: 1ffff920004abf60 R14: ffff88801fc28280 R15: dffffc0000000000
 schedule_hrtimeout_range_clock+0x142/0x330 kernel/time/sleep_timeout.c:213
 ep_poll fs/eventpoll.c:2030 [inline]
 do_epoll_wait+0xd36/0xf60 fs/eventpoll.c:2464
 __do_sys_epoll_wait fs/eventpoll.c:2472 [inline]
 __se_sys_epoll_wait fs/eventpoll.c:2467 [inline]
 __x64_sys_epoll_wait+0x1d7/0x230 fs/eventpoll.c:2467
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f6e418a7407
Code: 48 89 fa 4c 89 df e8 38 aa 00 00 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 1a 5b c3 0f 1f 84 00 00 00 00 00 48 8b 44 24 10 0f 05 <5b> c3 0f 1f 80 00 00 00 00 83 e2 39 83 fa 08 75 de e8 23 ff ff ff
RSP: 002b:00007ffe8bec5c00 EFLAGS: 00000202 ORIG_RAX: 00000000000000e8
RAX: ffffffffffffffda RBX: 00007f6e41fb1880 RCX: 00007f6e418a7407
RDX: 0000000000000008 RSI: 00007ffe8bec5d60 RDI: 000000000000000b
RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000bb8 R11: 0000000000000202 R12: 0000000000000000
R13: 0000556a1ef48100 R14: 0000000000000000 R15: 0000000000000000
 </TASK>
----------------
Code disassembly (best guess):
   0:	f7 e8                	imul   %eax
   2:	ad                   	lods   %ds:(%rsi),%eax
   3:	28 f6                	sub    %dh,%dh
   5:	f5                   	cmc
   6:	f7 c3 00 02 00 00    	test   $0x200,%ebx
   c:	74 05                	je     0x13
   e:	e8 20 d2 21 f6       	call   0xf621d233
  13:	9c                   	pushf
  14:	58                   	pop    %rax
  15:	a9 00 02 00 00       	test   $0x200,%eax
  1a:	75 27                	jne    0x43
  1c:	f7 c3 00 02 00 00    	test   $0x200,%ebx
  22:	74 01                	je     0x25
  24:	fb                   	sti
  25:	bf 01 00 00 00       	mov    $0x1,%edi
* 2a:	e8 c4 a7 e7 f5       	call   0xf5e7a7f3 <-- trapping instruction
  2f:	65 8b 05 8d 9d 8c 07 	mov    %gs:0x78c9d8d(%rip),%eax        # 0x78c9dc3
  36:	85 c0                	test   %eax,%eax
  38:	74 18                	je     0x52
  3a:	5b                   	pop    %rbx
  3b:	41 5e                	pop    %r14
  3d:	c3                   	ret
  3e:	cc                   	int3
  3f:	cc                   	int3

---
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 [email protected].

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

maintainer
#1 lore raw
Quoted 1 line · syzbot
WARNING: kernel/time/hrtimer.c:443 at stub_timer+0xa/0x20 kernel/time/timer.c:716, CPU#0: udevd/4706

So this puzzled me a bit as the stub_timer callback is only installed when the hrtimer object is not initialized according to the debug objects tracking. But that would cause a debug objects warning splat, which is not there.

Then I discovered this in the console log a few seconds before the warning:

ODEBUG: Out of memory. ODEBUG disabled

So that made me dig into debug_object_assert_init() and I discovered the following issue:

debug_assert_init()

        if (!enabled)
        	return;                         obj = alloc();
                				if (!obj)
                                                	enabled = false;
                                                        free_objects();
        obj = lookup_or_alloc();

        // Lookup failed because the other side
        // removed the objects, but it returns
        // an error code as the object in question
        // is not statically initialized

	if (!IS_ERR_OR_NULL(obj))
        	return;
        if (!obj) {
        	debug_oom();
                return;
        }

        print(...)
           if (!enabled)
                return;

        fixup(...)

So invoking the fixup callback in that case without checking again whether debug_objects is still enabled causes the above problem.

Fix below.

Thanks,

tglx
---

lib/debugobjects.c +17 -0
index 6fb00e08a4e2..d7a02a943ac9 100644
@@ -894,6 +894,14 @@ int debug_object_activate(void *addr, const struct debug_obj_descr *descr)
 	}
 
 	raw_spin_unlock_irqrestore(&db->lock, flags);
+
+	/*
+	 * lookup_object_or_alloc() might have raced with a concurrent
+	 * allocation failure which disabled debug objects.
+	 */
+	if (!debug_objects_enabled)
+		return 0;
+
 	debug_print_object(&o, "activate");
 
 	switch (o.state) {
@@ -1071,6 +1079,15 @@ void debug_object_assert_init(void *addr, const struct debug_obj_descr *descr)
 		return;
 	}
 
+	/*
+	 * lookup_object_or_alloc() might have raced with a concurrent
+	 * allocation failure which disabled debug objects. Don't run the fixup
+	 * as it might turn a valid object useless. See for example
+	 * hrtimer_fixup_assert_init().
+	 */
+	if (!debug_objects_enabled)
+		return;
+
 	/* Object is neither tracked nor static. It's not initialized. */
 	debug_print_object(&o, "assert_init");
 	debug_object_fixup(descr->fixup_assert_init, addr, ODEBUG_STATE_NOTAVAILABLE);
maintainer
#2 lore raw

PATCH 1/1 debugobjects: Plug race against a concurrent OOM disable

syzbot reported a puzzling splat:

   WARNING: kernel/time/hrtimer.c:443 at stub_timer+0xa/0x20 

stub_timer() is installed as timer callback function in hrtimer_fixup_assert_init(), which is invoked when debug_object_assert_init() can't find a shadow object. In that case debug objects emits a warning about it before invoking the fixup.

Though the provided console log lacks this warning and instead has the following a few seconds before the splat:

ODEBUG: Out of memory. ODEBUG disabled

So the object was looked up in debug_object_assert_init() and the lookup failed due a concurrent out of memory situation which disabled debug objects and freed the shadow objects:

debug_object_assert_init()

        if (!debug_objects_enabled)
        	return;                         obj = alloc();
                				if (!obj) {
							// Out of memory
                                                	debug_objects_enabled = false;
                                                        free_objects();
        obj = lookup_or_alloc();

        // The lookup failed because the other side
        // removed the objects, so this returns
        // an error code as the object in question
        // is not statically initialized

	if (!IS_ERR_OR_NULL(obj))
        	return;
        if (!obj) {
        	debug_oom();
                return;
        }

        print(...)
           if (!debug_objects_enabled)
                return;

        fixup(...)

The debug object splat is skipped because debug_objects_enabled is false, but the fixup callback is invoked unconditionally, which makes the timer disfunctional.

This is only a problem in debug_object_assert_init() and debug_object_activate() as both have to handle statically initialized objects and therefore must handle the error pointer return case gracefully. All other places only handle the found/not found case and the NULL pointer return is a signal for OOM. Otherwise they get a valid shadow object.

Plug the hole by checking whether debug objects are still enabled before invoking the print and fixup function in those two places.

Fixes b84d435cc228 ("debugobjects: Extend to assert that an object is initialized")
Reported-by [email protected]
---
 lib/debugobjects.c |   17 +++++++++++++++++
 1 file changed, 17 insertions(+)
lib/debugobjects.c +17 -0
@@ -894,6 +894,14 @@ int debug_object_activate(void *addr, co
 	}
 
 	raw_spin_unlock_irqrestore(&db->lock, flags);
+
+	/*
+	 * lookup_object_or_alloc() might have raced with a concurrent
+	 * allocation failure which disabled debug objects.
+	 */
+	if (!debug_objects_enabled)
+		return 0;
+
 	debug_print_object(&o, "activate");
 
 	switch (o.state) {
@@ -1071,6 +1079,15 @@ void debug_object_assert_init(void *addr
 		return;
 	}
 
+	/*
+	 * lookup_object_or_alloc() might have raced with a concurrent
+	 * allocation failure which disabled debug objects. Don't run the fixup
+	 * as it might turn a valid object useless. See for example
+	 * hrtimer_fixup_assert_init().
+	 */
+	if (!debug_objects_enabled)
+		return;
+
 	/* Object is neither tracked nor static. It's not initialized. */
 	debug_print_object(&o, "assert_init");
 	debug_object_fixup(descr->fixup_assert_init, addr, ODEBUG_STATE_NOTAVAILABLE);
#3 lore raw

[tip: core/urgent] debugobjects: Plug race against a concurrent OOM disable

The following commit has been merged into the core/urgent branch of tip:

The following commit has been merged into the core/urgent branch of tip:

Commit-ID:     b81dde13cc163450dcb402dcc915ef13ba241e01
Gitweb:        https://git.kernel.org/tip/b81dde13cc163450dcb402dcc915ef13ba241e01
Author:        Thomas Gleixner <[email protected]>
AuthorDate:    Sun, 21 Jun 2026 16:47:44 +02:00
Committer:     Thomas Gleixner <[email protected]>
CommitterDate: Mon, 22 Jun 2026 16:38:57 +02:00

debugobjects: Plug race against a concurrent OOM disable

syzbot reported a puzzling splat:

   WARNING: kernel/time/hrtimer.c:443 at stub_timer+0xa/0x20

stub_timer() is installed as timer callback function in hrtimer_fixup_assert_init(), which is invoked when debug_object_assert_init() can't find a shadow object. In that case debug objects emits a warning about it before invoking the fixup.

Though the provided console log lacks this warning and instead has the following a few seconds before the splat:

ODEBUG: Out of memory. ODEBUG disabled

So the object was looked up in debug_object_assert_init() and the lookup failed due a concurrent out of memory situation which disabled debug objects and freed the shadow objects:

debug_object_assert_init()

        if (!debug_objects_enabled)
        	return;                         obj = alloc();
                				if (!obj) {
							// Out of memory
                                                	debug_objects_enabled = false;
                                                        free_objects();
        obj = lookup_or_alloc();

        // The lookup failed because the other side
        // removed the objects, so this returns
        // an error code as the object in question
        // is not statically initialized

	if (!IS_ERR_OR_NULL(obj))
        	return;
        if (!obj) {
        	debug_oom();
                return;
        }

        print(...)
           if (!debug_objects_enabled)
                return;

        fixup(...)

The debug object splat is skipped because debug_objects_enabled is false, but the fixup callback is invoked unconditionally, which makes the timer disfunctional.

This is only a problem in debug_object_assert_init() and debug_object_activate() as both have to handle statically initialized objects and therefore must handle the error pointer return case gracefully. All other places only handle the found/not found case and the NULL pointer return is a signal for OOM. Otherwise they get a valid shadow object.

Plug the hole by checking whether debug objects are still enabled before invoking the print and fixup function in those two places.

Fixes b84d435cc228 ("debugobjects: Extend to assert that an object is initialized")
Reported-by [email protected]
---
 lib/debugobjects.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)
lib/debugobjects.c +17 -0
index 6fb00e0..877f767 100644
@@ -894,6 +894,14 @@ int debug_object_activate(void *addr, const struct debug_obj_descr *descr)
 	}
 
 	raw_spin_unlock_irqrestore(&db->lock, flags);
+
+	/*
+	 * lookup_object_or_alloc() might have raced with a concurrent
+	 * allocation failure which disabled debug objects.
+	 */
+	if (!debug_objects_enabled)
+		return 0;
+
 	debug_print_object(&o, "activate");
 
 	switch (o.state) {
@@ -1071,6 +1079,15 @@ void debug_object_assert_init(void *addr, const struct debug_obj_descr *descr)
 		return;
 	}
 
+	/*
+	 * lookup_object_or_alloc() might have raced with a concurrent
+	 * allocation failure which disabled debug objects. Don't run the fixup
+	 * as it might turn a valid object useless. See for example
+	 * hrtimer_fixup_assert_init().
+	 */
+	if (!debug_objects_enabled)
+		return;
+
 	/* Object is neither tracked nor static. It's not initialized. */
 	debug_print_object(&o, "assert_init");
 	debug_object_fixup(descr->fixup_assert_init, addr, ODEBUG_STATE_NOTAVAILABLE);