ssh/tailssh: pty master 复制而没有 CLOEXEC, 导致每个后续 SSH 会话的子进程都泄露; 使死的 ttys 保持活动状态, 并导致 tmux 卡死
作者: sachmaan创建于 2026年9月17日更新于 2026年9月17日
标签OS-linuxbugssh
问题是什么?
ssh/tailssh/incubator.go 使用原始的 dup(2) 复制 pty 主机:
// ssh/tailssh/incubator.go:935 (v1.102.4; same on main)
// 我们需要能够在以后单独关闭 stdin 和 stdout,因此要创建一个 dup。
ptyDup, err := syscall.Dup(int(pty.Fd()))syscall.Dup 从来没有设置 FD_CLOEXEC (Go 自己的注释在 syscall/exec_unix.go 中:"Dup。如果可用,请使用 F_DUPFD_CLOEXEC 或 dup3")。creack/pty 中的原始主机是正确的(os.OpenFile 添加了 O_CLOEXEC),但 ptyDup 在 tailscaled 中保持打开状态,直到 stdout 复制器在会话结束时关闭它。在该窗口期间,任何其他 SSH 会话的 cmd.Start() 都会继承 ptyDup,因为 os/exec 依赖于 CLOEXEC 来删除 fd。该 fd 然后在 be-child → login -f → 用户 shell → shell 运行的所有内容中存活下来。
内容来源: tailscale/tailscale