INFO: task hung in vhost_task_stop (3)

Status: upstream: reported C repro on 2026/11/12 23:43
Subsystems: kernel
Labels: prio:low
[Documentation on labels]
Reported-by: syzbot+f59eab881ec20d423ab0@syzkaller.appspotmail.com
First crash: 4d02h, last: 4d02h
Cause bisection: failed (error log, bisect log)
  
✨ AI-generated root cause summary:

When a KVM VM is initialized, kvm_mmu_start_lpage_recovery() creates the "kvm-nx-lpage-recovery" worker thread via vhost_task_create(). Because vhost_task_create() clones the calling process with CLONE_THREAD and .user_worker = 1 (PF_USER_WORKER) without PF_NOFREEZE, the worker inherits the caller's thread group and cgroup freezer state. If the cgroup is subsequently frozen (e.g. via cgroup1 freezer.state), the worker thread is trapped in __refrigerator() in the TASK_FROZEN state. When the VM file descriptor is closed, kvm_mmu_pre_destroy_vm() calls vhost_task_stop(), which sets VHOST_TASK_FLAGS_STOP, attempts to wake the worker using wake_up_process() (which only targets TASK_NORMAL), and blocks in wait_for_completion(&vtsk->exited). Because wake_up_process() cannot wake a task in TASK_FROZEN and freezing(current) remains true, the worker never exits, causing vhost_task_stop() to hang indefinitely in uninterruptible sleep.

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

Technical Root Cause Analysis

Involved Functions, Structs, and Files

  • kernel/vhost_task.c:
    • vhost_task_create(): Creates a user worker thread (PF_USER_WORKER) via copy_process() using CLONE_THREAD | CLONE_VM | CLONE_SIGHAND | CLONE_FS.
    • vhost_task_fn(): The main loop for the worker thread.
    • vhost_task_stop() / vhost_task_wake(): Stops the worker by setting VHOST_TASK_FLAGS_STOP, waking the task via wake_up_process(), and waiting on wait_for_completion(&vtsk->exited).
    • struct vhost_task: Represents the worker thread, its completion struct (exited), task pointer (task), and flags (flags).
  • arch/x86/kvm/mmu/mmu.c:
    • kvm_mmu_start_lpage_recovery(): Creates and starts the "kvm-nx-lpage-recovery" thread using vhost_task_create().
    • kvm_mmu_pre_destroy_vm(): Stops the recovery thread using vhost_task_stop().
  • kernel/freezer.c and kernel/signal.c:
    • freezing_slow_path() / freezing(): Evaluates whether a task should freeze.
    • fake_signal_wake_up() / get_signal(): Delivers freeze notifications and diverts the task to try_to_freeze().
    • __refrigerator(): Puts the task into TASK_FROZEN and calls schedule() in a loop while freezing(current) is true.

Bug Mechanism and Triggering Sequence

  1. Thread Creation and Inheritance of Cgroup Freezer State: During KVM VM initialization (ioctl(KVM_CREATE_VM) -> kvm_mmu_post_init_vm() -> kvm_mmu_start_lpage_recovery()), KVM spawns a background NX huge page recovery thread (kvm->arch.nx_huge_page_recovery_thread) by calling vhost_task_create(). vhost_task_create() configures struct kernel_clone_args with:

    .flags       = CLONE_FS | CLONE_UNTRACED | CLONE_VM | CLONE_THREAD | CLONE_SIGHAND,
    .fn          = vhost_task_fn,
    .user_worker = 1,
    

    Because CLONE_THREAD is passed, the worker is attached to the calling userspace task's thread group and shares the caller's cgroups (including any cgroup freezer). Crucially, the worker is marked with PF_USER_WORKER (not PF_KTHREAD) and does not have PF_NOFREEZE set.

  2. Freezing the Worker: When the cgroup is frozen (e.g., writing "FROZEN" to cgroup1's freezer.state), freeze_cgroup() -> freeze_task() runs. Since the worker lacks PF_KTHREAD, freeze_task() calls fake_signal_wake_up(p), setting TIF_SIGPENDING. Inside vhost_task_fn(), the check if (signal_pending(current)) succeeds and calls get_signal(&ksig). Before processing normal signals, get_signal() calls try_to_freeze(). Because freezing(current) is true (due to cgroup1_freezing(current) and the absence of PF_NOFREEZE), the worker enters __refrigerator(false):

    for (;;) {
        raw_spin_lock_irq(&current->pi_lock);
        WRITE_ONCE(current->__state, TASK_FROZEN);
        current->saved_state = TASK_RUNNING;
        raw_spin_unlock_irq(&current->pi_lock);
    
        spin_lock_irq(&freezer_lock);
        freeze = freezing(current) && !(check_kthr_stop && kthread_should_stop());
        spin_unlock_irq(&freezer_lock);
    
        if (!freeze)
            break;
    
        schedule();
    }
    

    The recovery worker sets its task state to TASK_FROZEN and sleeps in schedule().

  3. Shutdown Deadlock: When the userspace process closes the VM file descriptor or exits (close_range / fput -> kvm_vm_release -> kvm_destroy_vm -> kvm_arch_pre_destroy_vm -> kvm_mmu_pre_destroy_vm), KVM attempts to cleanly shut down the recovery thread by calling vhost_task_stop(kvm->arch.nx_huge_page_recovery_thread):

    void vhost_task_stop(struct vhost_task *vtsk)
    {
        mutex_lock(&vtsk->exit_mutex);
        if (!test_bit(VHOST_TASK_FLAGS_KILLED, &vtsk->flags)) {
            set_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags);
            vhost_task_wake(vtsk);
        }
        mutex_unlock(&vtsk->exit_mutex);
    
        wait_for_completion(&vtsk->exited);
        ...
    }
    

    vhost_task_wake() invokes wake_up_process(vtsk->task), which calls try_to_wake_up(p, TASK_NORMAL, 0). Because the worker's task state is TASK_FROZEN (0x00008000), which is not covered by TASK_NORMAL (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE), try_to_wake_up() considers the task non-runnable and returns 0 without waking it. Furthermore, even if the task were temporarily woken, freezing(current) remains true, so __refrigerator() would immediately re-enter TASK_FROZEN and reschedule.

    As a result, the worker never returns to vhost_task_fn() to check VHOST_TASK_FLAGS_STOP, complete &vtsk->exited, and exit. The calling thread in vhost_task_stop() hangs indefinitely in wait_for_completion() in state D (TASK_UNINTERRUPTIBLE), eventually triggering the hung task watchdog (khungtaskd).

▶ ▼ ✨ AI Jobs (1)
ID Workflow Result Correct Bug Created Started Finished Revision
ddd4e4cb assessment-security DenialOfService: ✅ Exploitable: ❌ FilesystemTrigger: ❌ FuzzingArtifact: ❌ MemoryCorruption: ❌ NetworkNeedsSetup: ❌ NetworkTrigger: ❌ PeripheralTrigger: ❌ RemoteTrigger: ❌ Unprivileged: ❌ UserNamespace: ❌ VMGuestTrigger: ❌ VMHostTrigger: ❌ ❓ INFO: task hung in vhost_task_stop (3) 2026/11/08 22:48 2026/11/08 22:48 2026/11/08 23:30 5cecaf1f
▶ ▼ Similar bugs (3)
Kernel Title Rank 🛈 Repro Cause bisect Fix bisect Count Last Reported Patched Status
upstream INFO: task hung in vhost_task_stop kernel 1 8 675d 716d 0/29 auto-obsoleted due to no activity on 2025/04/06 23:10
upstream INFO: task hung in vhost_task_stop (2) kernel 1 4 534d 557d 0/29 auto-obsoleted due to no activity on 2025/08/25 04:17
android-6-12 INFO: task hung in vhost_task_stop 1 syz, C 2 13d 28d 0/1 premoderation: reported C repro on 2026/10/15 08:30

Sample crash report:
INFO: task syz.0.17:5888 blocked for more than 143 seconds.
      Not tainted syzkaller #0
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:syz.0.17        state:D stack:26216 pid:5888  tgid:5888  ppid:5825   task_flags:0x400040 flags:0x08080002
Call Trace:
 <TASK>
 __schedu
 context_switch kernel/sched/core.c:5583 [inline]
 __schedule+0x17dd/0x5830 kernel/sched/core.c:7382
 __schedule_loop kernel/sched/core.c:7459 [inline]
 schedule+0x164/0x2b0 kernel/sched/core.c:7474
 schedule_timeout+0xc0/0x2c0 kernel/time/sleep_timeout.c:75
 do_wait_for_common kernel/sched/completion.c:100 [inline]
 __wait_for_common kernel/sched/completion.c:121 [inline]
 wait_for_common kernel/sched/completion.c:132 [inline]
 wait_for_completion+0x2c5/0x5d0 kernel/sched/completion.c:153
 vhost_task_stop+0xc0/0x170 kernel/vhost_task.c:102
 kvm_arch_pre_destroy_vm+0x68/0x80 arch/x86/kvm/x86.c:10107
 kvm_destroy_vm virt/kvm/kvm_main.c:1284 [inline]
 kvm_put_kvm+0x377/0xb40 virt/kvm/kvm_main.c:1350
 kvm_vm_release+0x6b/0x80 virt/kvm/kvm_main.c:1375
 __fput+0x408/0xa40 fs/file_table.c:512
 fput_close_sync+0x11f/0x240 fs/file_table.c:618
 filp_close_sync+0x6e/0xa0 fs/open.c:1574
 __range_close fs/file.c:919 [inline]
 __do_sys_close_range fs/file.c:1003 [inline]
 __se_sys_close_range+0xa77/0xef0 fs/file.c:954
 do_syscall_32_irqs_on arch/x86/entry/syscall_32.c:79 [inline]
 __do_fast_syscall_32+0x28b/0x6c0 arch/x86/entry/syscall_32.c:282
 do_fast_syscall_32+0x33/0x70 arch/x86/entry/syscall_32.c:307
 entry_SYSENTER_compat_after_hwframe+0x84/0x8e
RIP: 0023:0xf7fb3f1c
RSP: 002b:00000000ffe837dc EFLAGS: 00000206 ORIG_RAX: 00000000000001b4
RAX: ffffffffffffffda RBX: 0000000000000003 RCX: 000000000000001e
RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000000
RBP: 0000000000000000 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000206 R12: 0000000000000000
R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
 </TASK>

Showing all locks held in the system:
locks held by khungtaskd/33: 1, last CPU#0:
 #0: ffffffff9015a260 (rcu_read_lock){....}-{1:3}, at: rcu_lock_acquire include/linux/rcupdate.h:309 [inline]
 #0: ffffffff9015a260 (rcu_read_lock){....}-{1:3}, at: rcu_read_lock include/linux/rcupdate.h:849 [inline]
 #0: ffffffff9015a260 (rcu_read_lock){....}-{1:3}, at: debug_show_all_locks+0x2e/0x180 kernel/locking/lockdep.c:6871
locks held by kworker/u8:9/1115: 4, last CPU#0:
 #0: ffff88813fe4c148 ((wq_completion)events_unbound){+.+.}-{0:0}, at: rcu_lock_acquire include/linux/rcupdate.h:309 [inline]
 #0: ffff88813fe4c148 ((wq_completion)events_unbound){+.+.}-{0:0}, at: rcu_read_lock include/linux/rcupdate.h:849 [inline]
 #0: ffff88813fe4c148 ((wq_completion)events_unbound){+.+.}-{0:0}, at: process_one_work kernel/workqueue.c:3460 [inline]
 #0: ffff88813fe4c148 ((wq_completion)events_unbound){+.+.}-{0:0}, at: process_scheduled_works+0x97a/0x1630 kernel/workqueue.c:3578
 #1: ffff8880b8224708 (psi_seq){-.-.}-{0:0}, at: psi_task_switch+0x57/0x7d0 kernel/sched/psi.c:928
 #2: ffff8880b8226498 (&base->lock){-.-.}-{2:2}, at: lock_timer_base kernel/time/timer.c:1005 [inline]
 #2: ffff8880b8226498 (&base->lock){-.-.}-{2:2}, at: __mod_timer+0x1a9/0xed0 kernel/time/timer.c:1086
 #3: ffffffff9c12ba00 (&obj_hash[i].lock){-.-.}-{2:2}, at: debug_object_activate+0xb7/0x420 lib/debugobjects.c:873
locks held by kworker/u8:12/1168: 3, last CPU#0:
 #0: ffff88813fe4c148 ((wq_completion)events_unbound){+.+.}-{0:0}, at: rcu_lock_acquire include/linux/rcupdate.h:309 [inline]
 #0: ffff88813fe4c148 ((wq_completion)events_unbound){+.+.}-{0:0}, at: rcu_read_lock include/linux/rcupdate.h:849 [inline]
 #0: ffff88813fe4c148 ((wq_completion)events_unbound){+.+.}-{0:0}, at: process_one_work kernel/workqueue.c:3460 [inline]
 #0: ffff88813fe4c148 ((wq_completion)events_unbound){+.+.}-{0:0}, at: process_scheduled_works+0x97a/0x1630 kernel/workqueue.c:3578
 #1: ffffc900050f7c40 ((work_completion)(&(&nsim_dev->trap_data->trap_report_dw)->work)){+.+.}-{0:0}, at: rcu_lock_acquire include/linux/rcupdate.h:309 [inline]
 #1: ffffc900050f7c40 ((work_completion)(&(&nsim_dev->trap_data->trap_report_dw)->work)){+.+.}-{0:0}, at: rcu_read_lock include/linux/rcupdate.h:849 [inline]
 #1: ffffc900050f7c40 ((work_completion)(&(&nsim_dev->trap_data->trap_report_dw)->work)){+.+.}-{0:0}, at: process_one_work kernel/workqueue.c:3460 [inline]
 #1: ffffc900050f7c40 ((work_completion)(&(&nsim_dev->trap_data->trap_report_dw)->work)){+.+.}-{0:0}, at: process_scheduled_works+0x97a/0x1630 kernel/workqueue.c:3578
 #2: ffff888037387258 (&devlink->lock_key#9){+.+.}-{4:4}, at: nsim_dev_trap_report_work+0x57/0xb30 drivers/net/netdevsim/dev.c:834
locks held by kworker/0:2/1216: 3, last CPU#0:
 #0: ffff88813fe0dd48 ((wq_completion)events_power_efficient){+.+.}-{0:0}, at: rcu_lock_acquire include/linux/rcupdate.h:309 [inline]
 #0: ffff88813fe0dd48 ((wq_completion)events_power_efficient){+.+.}-{0:0}, at: rcu_read_lock include/linux/rcupdate.h:849 [inline]
 #0: ffff88813fe0dd48 ((wq_completion)events_power_efficient){+.+.}-{0:0}, at: process_one_work kernel/workqueue.c:3460 [inline]
 #0: ffff88813fe0dd48 ((wq_completion)events_power_efficient){+.+.}-{0:0}, at: process_scheduled_works+0x97a/0x1630 kernel/workqueue.c:3578
 #1: ffffc90005487c40 ((work_completion)(&(&gc_work->dwork)->work)){+.+.}-{0:0}, at: rcu_lock_acquire include/linux/rcupdate.h:309 [inline]
 #1: ffffc90005487c40 ((work_completion)(&(&gc_work->dwork)->work)){+.+.}-{0:0}, at: rcu_read_lock include/linux/rcupdate.h:849 [inline]
 #1: ffffc90005487c40 ((work_completion)(&(&gc_work->dwork)->work)){+.+.}-{0:0}, at: process_one_work kernel/workqueue.c:3460 [inline]
 #1: ffffc90005487c40 ((work_completion)(&(&gc_work->dwork)->work)){+.+.}-{0:0}, at: process_scheduled_works+0x97a/0x1630 kernel/workqueue.c:3578
 #2: ffffffff9015a260 (rcu_read_lock){....}-{1:3}, at: rcu_lock_acquire include/linux/rcupdate.h:309 [inline]
 #2: ffffffff9015a260 (rcu_read_lock){....}-{1:3}, at: rcu_read_lock include/linux/rcupdate.h:849 [inline]
 #2: ffffffff9015a260 (rcu_read_lock){....}-{1:3}, at: gc_worker+0x274/0x1890 net/netfilter/nf_conntrack_core.c:1530
locks held by getty/5391: 2, on CPU#1:
 #0: ffff8880385230a0 (&tty->ldisc_sem){++++}-{0:0}, at: tty_ldisc_ref_wait+0x25/0x70 drivers/tty/tty_ldisc.c:243
 #1: ffffc900032362e8 (&ldata->atomic_read_lock){+.+.}-{4:4}, at: n_tty_read+0x45a/0x1360 drivers/tty/n_tty.c:2211

=============================================

NMI backtrace for cpu 1
CPU: 1 UID: 0 PID: 33 Comm: khungtaskd Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/15/2026
Call Trace:
 <TASK>
 dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
 nmi_cpu_backtrace+0x274/0x2d0 lib/nmi_backtrace.c:123
 nmi_trigger_cpumask_backtrace+0x17d/0x390 lib/nmi_backtrace.c:66
 trigger_all_cpu_backtrace include/linux/nmi.h:164 [inline]
 __sys_info lib/sys_info.c:157 [inline]
 sys_info+0x135/0x170 lib/sys_info.c:165
 check_hung_uninterruptible_tasks kernel/hung_task.c:377 [inline]
 watchdog+0x1073/0x10f0 kernel/hung_task.c:598
 kthread+0x38b/0x480 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>
Sending NMI from CPU 1 to CPUs 0:
NMI backtrace for cpu 0
CPU: 0 UID: 0 PID: 32 Comm: pr/ttyS0 Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/15/2026
RIP: 0010:io_serial_in+0x77/0xc0 drivers/tty/serial/8250/8250_port.c:425
Code: e8 2e 6a 43 fc 44 89 f9 d3 e3 49 83 c6 40 4c 89 f0 48 c1 e8 03 42 80 3c 20 00 74 08 4c 89 f7 e8 2f f3 ba fc 41 03 1e 89 da ec <0f> b6 c0 5b 41 5c 41 5e 41 5f e9 9a 14 83 06 cc 44 89 f9 80 e1 07
RSP: 0018:ffffc90000a878b8 EFLAGS: 00000002
RAX: 1ffffffff3840b00 RBX: 00000000000003fd RCX: 0000000000000000
RDX: 00000000000003fd RSI: 0000000000000000 RDI: 0000000000000020
RBP: dffffc0000000000 R08: 0000000000000003 R09: 0000000000000004
R10: dffffc0000000000 R11: ffffffff8585a440 R12: dffffc0000000000
R13: 0000000000000000 R14: ffffffff9c205ec0 R15: 0000000000000000
FS:  0000000000000000(0000) GS:ffff888123409000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000563e7aa97d08 CR3: 000000000ff48000 CR4: 00000000003526f0
Call Trace:
 <TASK>
 serial_in drivers/tty/serial/8250/8250.h:134 [inline]
 serial_lsr_in drivers/tty/serial/8250/8250.h:156 [inline]
 wait_for_lsr+0x184/0x2f0 drivers/tty/serial/8250/8250_port.c:2036
 serial8250_fifo_wait_for_lsr_thre drivers/tty/serial/8250/8250_port.c:3294 [inline]
 __serial8250_console_fifo_write+0xcd/0x3e0 drivers/tty/serial/8250/8250_port.c:3369
 serial8250_console_fifo_write drivers/tty/serial/8250/8250_port.c:3402 [inline]
 __serial8250_console_write drivers/tty/serial/8250/8250_port.c:3456 [inline]
 serial8250_console_write+0xa7e/0x1200 drivers/tty/serial/8250/8250_port.c:3520
 nbcon_emit_next_record+0xef5/0x1a90 kernel/printk/nbcon.c:-1
 nbcon_emit_one kernel/printk/nbcon.c:1157 [inline]
 nbcon_kthread_func+0x6d9/0x8d0 kernel/printk/nbcon.c:1271
 kthread+0x38b/0x480 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/08 22:48 upstream fa4ad798f6f6 5cecaf1f .config console log report syz / log C [disk image] [vmlinux] [kernel image] ci-upstream-kasan-gce-386 INFO: task hung in vhost_task_stop
* Struck through repros no longer work on HEAD.