[Bug 296835] kern: coredump of a process with many EVFILT_VNODE knotes hangs for hours, process unkillable (SIGKILL pending), holds fd table lock (NT_PROCSTAT_KQUEUES note generation)
[email protected] Fri, 24 Jul 2026 14:18:47 +0000
| Newsgroups | gmane.os.freebsd.bugs |
|---|---|
| Message-ID | <[email protected]/bugzilla/> |
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=296835 --- Comment #2 from [email protected] --- Follow-up, same machine (dal, 15.1-RELEASE releng/15.1-n283562-96841ea08dcf, amd64, root and /home on ZFS). The original report speculated that the NT_PROCSTAT_FILES note "has the same shape" as NT_PROCSTAT_KQUEUES and had probably already consumed part of the elapsed time. I can now confirm that directly, and with a worse consequence than expected: **the per-fd path-resolution path is reachable without any coredump at all**, through the plain `kern.proc.filedesc` sysctl, and it is equally uninterruptible. ### What happened Same trigger as before: a `codex` process (codex-0.144.1, Rust, notify-rs kqueue backend) accumulated ~650,000 open fds, each with an EVFILT_VNODE knote. `kern.openfiles` was 668,507 and the KNOTE UMA zone showed 657,555 items in use. The process itself was idle (state `I+`, 0.0% CPU) and never crashed — no signal, no core dump. The victim was Konsole (konsole-26.04.3), which polls the foreground process's current directory to render the `%d` field of its default tab title. It does so via `KERN_PROC_FILEDESC`. One such call never completed: ``` PID %CPU %MEM TIME STAT COMMAND 37819 99.1 5.7 99:19.69 R /usr/local/bin/konsole ``` 99 minutes of CPU, of which 17 seconds user and 1h30m system. The GUI thread was wedged in the kernel the entire time, so the terminal window was frozen and unresponsive. procstat -kk, sampled repeatedly, was always in the same place: ``` 37819 123940 konsole - fzap_cursor_retrieve+0x8d zap_cursor_retrieve+0x1e7 zap_value_search+0x8f zfs_znode_parent_and_name+0xbf zfs_vptocnp+0x1cd vn_vptocnp+0x17f vn_fullpath_dir+0x102 vn_fullpath_any+0x59 vn_fullpath+0xd2 vn_fill_kinfo_vnode+0x41 vn_fill_kinfo+0x45 export_file_to_kinfo+0x144 kern_proc_filedesc_out+0x387 sysctl_kern_proc_filedesc+0x8d sysctl_root_handler_locked+0x91 sysctl_root+0x268 userland_sysctl+0x1a6 sys___sysctl+0x65 ``` The other two konsole threads (QDBusConnectionManager, QXcbEventQueue) were idle in `poll`. Nothing was looping in userspace. `procstat -f <codex-pid>` reproduced it exactly, and **`timeout 15 procstat -f` failed to kill it** — the command ran for ~105 seconds until the sysctl returned on its own. Every other process on the box returned instantly. Killing codex resolved it: `kern.openfiles` dropped 668,507 -> 2,720 and konsole returned to `S` at 0.5% CPU. ### Analysis In `sys/kern/kern_descrip.c` (checked against main, __FreeBSD_version 1600019), `kern_proc_filedesc_out()` walks the whole table: ```c FILEDESC_FOREACH_FP(fdp, i, fp) { ... error = export_file_to_sb(fp, i, &rights, efbuf); if (error != 0 || refcount_load(&fdp->fd_refcnt) == 0) break; } ``` Two properties make this pathological at 650k fds: 1. **No cancellation point.** The loop terminates only on sbuf error or on the fd table going away. There is no check for pending signals on `curthread`, so the calling process cannot be interrupted or killed for as long as the walk runs — minutes here, and it scales linearly with the victim's fd count. This is the same "SIGKILL pending but never delivered" property as the coredump case, reached by a much cheaper route. 2. **`vn_fullpath()` runs under `FILEDESC_SLOCK`.** `export_file_to_sb()` drops the lock only around `export_kinfo_to_sb()`; the preceding `export_file_to_kinfo()` -> `vn_fill_kinfo()` -> `vn_fullpath()` is called with the shared filedesc lock held. On ZFS a namecache miss falls back to `zfs_vptocnp()` -> `zap_value_search()`, a linear ZAP scan of the parent directory, so per-fd cost is unbounded and the lock hold time inherits it. Worth noting that no directory on this system is unusually large — the biggest is ~9,600 entries. The cost is entirely the fd count multiplied by an ordinary ZAP scan. ### Why this widens the impact of the original report The coredump variant requires the fd-heavy process to crash. This variant requires nothing of the victim at all — it just has to exist. Any process that reads `kern.proc.filedesc` on it inherits a multi-minute uninterruptible syscall, and at least one very common desktop application does this automatically, on its GUI thread, on a timer. This is not exotic. Any kqueue-based file watcher (notify-rs, watchman) is one fd per watched vnode by construction, and LLM coding agents watching large source trees are making fd counts in the 10^5-10^6 range routine. ### Suggested directions - A cancellation point in the `FILEDESC_FOREACH_FP` loop (and in the equivalent knote walk) so a pending signal aborts the walk with EINTR. This alone would turn both bugs from "unkillable for hours" into "interruptible and slow", which is a large practical difference. - Avoid holding `FILEDESC_SLOCK` across `vn_fullpath()`, or resolve paths in a second pass outside the lock. - Optionally a flag or sysctl to skip path resolution for consumers that do not need it, mirroring `kern.coredump_pack_fileinfo`. -- You are receiving this mail because: You are the assignee for the bug.