Skip to content

Linux/WSL2 下 kimi CLI 0.42.0 随机硬死锁,SIGTERM/SIGQUIT 无法终止,并拖死 SSH 会话 || Linux/WSL2 kimi CLI 0.42.0 random hard deadlock, SIGTERM/SIGQUIT cannot terminate, and drags down the SSH session #2640

Description

@jinruyan02

What version of Kimi Code CLI is running?

0.42.0

Which open platform/subscription were you using?

Kimi Code (kimi.com/code)

Which model were you using?

kimi-for-coding

What platform is your computer?

Linux/WSL2

What issue are you seeing?

  1. 长时间运行后 TUI 偶发卡死,无任何响应。
  2. 卡死后无法正常退出:实测对进程发 SIGTERM、SIGQUIT 均无效,进程原地不动,只能 SIGKILL。
  3. 由于进程不死且持有 pty 的 stdout/stderr,sshd 会话通道无法关闭,SSH 窗口跟着卡死,只能强制关窗。
  4. 强关窗口后 kimi 不响应 SIGHUP,变成孤儿进程残留(TTY=?,fd 指向 /dev/pts/N (deleted)),每个残留 430~690MB 内存。一天内残留了 3 个(PID 6197/13517/23013),共约1.7GB。

What steps can reproduce the bug?

Linux WSL,windows主机开代理,长时间运行后 TUI

What is the expected behavior?

  1. 主线程任何阻塞等待都应有兜底超时/看门狗,避免永久 wedge;
  2. 信号处理不依赖主线程事件循环(如 sigaction 直接强制退出),保证 SIGTERM/SIGQUIT 总能终止进程;
  3. 网络流式请求增加读超时/keepalive,避免半开连接永久挂起。

Additional information

• 卡死进程主线程 wchan = futex_do_wait(阻塞在原生锁等待上,而非 epoll 事件循环),因此 JS 层信号处理器永远得不到执行 —— 这解释了为什么所有信号都无效。
• 线程快照:主线程 futex_do_wait,tokio-runtime 工作线程全体 futex_do_wait,1 个 libuv-worker 停在 wait_woken。
• 残留进程上仍挂着到 127.0.0.1:7897(代理)的 ESTABLISHED 连接,疑似半开连接(对端已消失但本端无感知)。
• 0.3.0 曾修复"终端消失时退出"(SIGHUP/EIO 处理),但在这种主线程原生层死锁的场景下该修复不生效。

推测
主线程在原生(Rust/tokio?)层发生死锁或在等待一个永不返回的操作;可能与经代理的长连接流式请求在半开状态下无读超时有关。运行时间越长出现概率越高。

更新:再次在没有开代理的情况下复现:
用 gdb 定位根因:进程退出时 node::Environment::Exit() → uv__threadpool_cleanup() → pthread_join 永久阻塞,因为 libuv 线程池 worker 卡在 glibc getaddrinfo("telemetry-logs.kimi.com") 的 TCP DNS read()(无超时)上。触发环境是 WSL2 dnsTunneling 的半开连接,但 kimi 侧有三个可改进点:

  1. 退出路径不应因线程池里残留的 getaddrinfo 而永久阻塞(libuv 无法取消进行中的 getaddrinfo,建议退出时不要 join 等待,或 telemetry 改用 c-ares 异步 DNS 并带超时);
  2. telemetry 域名的解析建议进程启动时解析并缓存,避免运行期/退出期反复触发 glibc 同步解析;
  3. 信号处理应保证在主线程阻塞时仍能终止进程(如 sigaction 直退或看门狗)。

What version of Kimi Code CLI is running?

0.42.0

Which open platform/subscription were you using?

Kimi Code (kimi.com/code)

Which model were you using?

kimi-for-coding

What platform is your computer?

Linux/WSL2

What issue are you seeing?

  1. After running for a long time, the TUI occasionally freezes without any response.
  2. Unable to exit normally after being stuck: According to actual tests, sending SIGTERM and SIGQUIT to the process is invalid. The process stays in place and can only SIGKILL.
  3. Since the process is alive and holds pty's stdout/stderr, the sshd session channel cannot be closed, and the SSH window is stuck, and the window can only be forced to close.
  4. After closing the window, kimi does not respond to SIGHUP and becomes an orphan process residue (TTY=?, fd points to /dev/pts/N (deleted)), each remaining 430~690MB of memory. There are 3 remaining in one day (PID 6197/13517/23013), totaling about 1.7GB.

What steps can reproduce the bug?

Linux WSL, open proxy on Windows host, TUI after running for a long time

What is the expected behavior?

  1. Any blocking wait of the main thread should have a timeout/watchdog to avoid permanent wedge;
  2. Signal processing does not rely on the main thread event loop (such as sigaction directly forcing exit), ensuring that SIGTERM/SIGQUIT can always terminate the process;
  3. Add read timeout/keepalive for network streaming requests to avoid permanent suspension of half-open connections.

Additional information

• The main thread of the process is stuck wchan = futex_do_wait (blocked on native lock wait, not epoll event loop), so the JS layer signal handler never gets executed - this explains why all signals are invalid.
• Thread snapshot: main thread futex_do_wait, all tokio-runtime worker threads futex_do_wait, 1 libuv-worker stops at wait_woken.
• The ESTABLISHED connection to 127.0.0.1:7897 (proxy) is still hanging on the residual process, which is suspected to be a half-open connection (the peer has disappeared but the local end is unaware).
• 0.3.0 has fixed "Exit when terminal disappears" (SIGHUP/EIO processing), but the fix does not take effect in this main thread native layer deadlock scenario.

Speculate
The main thread is deadlocked at the native (Rust/tokio?) layer or waiting for an operation that never returns; it may be related to the no-read timeout of the proxied long connection streaming request in the half-open state. The longer the running time, the higher the probability of occurrence.

Update: Reproduced again without proxy:
Use gdb to locate the root cause: node::Environment::Exit() → uv__threadpool_cleanup() → pthread_join blocks permanently when the process exits because the libuv thread pool worker is stuck on TCP DNS read() (no timeout) of glibc getaddrinfo("telemetry-logs.kimi.com"). The triggering environment is a half-open connection for WSL2 dnsTunneling, but there are three improvements on the kimi side:

  1. The exit path should not be permanently blocked by the remaining getaddrinfo in the thread pool (libuv cannot cancel the ongoing getaddrinfo. It is recommended not to wait for join when exiting, or telemetry uses c-ares asynchronous DNS with timeout);
  2. It is recommended that the telemetry domain name be parsed and cached when the process starts to avoid repeatedly triggering glibc synchronous parsing during the running/exit period;
  3. Signal processing should ensure that the process can still be terminated when the main thread is blocked (such as sigaction retreat or watchdog).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions