Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

[darwin] pty_posix_spawn leaks the pty master+slave when posix_spawn fails, and one ptmx fd on every successful spawn

Đang mở
#950 3 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
4/5
Thời gian dự kiến
3-5 ngày
Mức phù hợp với người mới
68/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
cpp, macos
Lĩnh vực
operating-systems

Hướng nghiên cứu

Bắt đầu trong src/unix/pty.cc tại pty_posix_spawn và kiểm tra phần mở đầu low-fd, các giá trị trả về lỗi, đường dẫn done: và đường dẫn lỗi của PtyFork. Tái hiện các spawn thất bại và thành công bằng script oversized-argv được cung cấp, sau đó xác minh bằng lsof rằng tất cả descriptor của master, slave và low_fds đều đã được đóng, đồng thời hành vi spawn hiện có vẫn hoạt động.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Version: node-pty 1.1.0, macOS (arm64, also reproduced on macOS 26), Node 22 / Electron 42.

On darwin, pty_posix_spawn (src/unix/pty.cc) allocates a pty before spawning and never releases it on any error path — and its low-fd prologue leaks one fd even when everything succeeds.

Leak 1 — failed spawn leaks the master and the slave (2 pty devices per failure)
  • *master = posix_openpt(O_RDWR) (pty.cc:707) and slave = open(slave_pty_name, …) (pty.cc:725) have no matching parent-side close() anywhere: every early return (L709, 714, 722, 727, 733, 740) and the done: path (L777) leave both fds open. The posix_spawn_file_actions_addclose calls at L749-750 apply only to the child.
  • PtyFork then throws (pty.cc:372-374, "posix_spawnp failed.") without cleanup, and since the JS ctor never receives term.fd, the caller cannot close anything either.
Leak 2 — successful spawn leaks one ptmx fd (off-by-one)

The low-fd prologue (L694-701) opens ptys until one lands at fd >= STDERR_FILENO, then breaks. Cleanup is:

for (; count > 0; count--) close(low_fds[count]);

In the common case the loop breaks with count == 0, so the body never runs and low_fds[0] leaks on every spawn. It also skips index 0 whenever count > 0, and reads low_fds[3] out of bounds if the prologue loop completes without breaking.

Reproduction

Forcing a real posix_spawn failure needs more than a nonexistent binary (on macOS argv[0] is the spawn-helper, which exists — the failure then happens in the child). E2BIG via an oversized argv works:

const pty = require('node-pty')
const huge = 'x'.repeat(3 * 1024 * 1024)
for (let i = 0; i < 25; i++) {
  try { pty.spawn('/bin/echo', [huge], { cwd: '/tmp' }) } catch (e) { /* posix_spawnp failed. */ }
}
// lsof -p <pid>: 50 /dev/ptmx + 25 /dev/ttysNNN still open

Measured on macOS: 25 failed spawns → +50 /dev/ptmx fds, +25 /dev/ttys* fds, system-wide device count 84 → 134 (2 devices per failure). 25 successful spawns, each fully exited and destroy()ed, still left 25 /dev/ptmx fds open (1 device each).

Impact

macOS caps pty devices system-wide at kern.tty.ptmx_max (default 511). Because failures leak two devices each, exhaustion is self-amplifying: at the ceiling every spawn fails, every failure consumes two more devices, and the process never recovers. An Electron app of ours accumulated 479 open masters in a 31-minute session against 28 real terminals, after which every spawn on the machine failed until the process exited.

Suggested fix
  1. In pty_posix_spawn, close *master (and slave once opened) on every error path, setting *master = -1; close slave in the parent unconditionally after posix_spawn, and close *master too when *err != 0.
  2. Track how many low_fds were actually opened and close all of them, e.g. size_t opened = count < 3 ? count + 1 : 3; for (size_t i = 0; i < opened; i++) if (low_fds[i] >= 0) close(low_fds[i]);
  3. Ideally include the posix_spawn errno in the thrown message — "posix_spawnp failed." currently discards the one datum (EMFILE vs EAGAIN vs ENOENT) that would let applications diagnose this.
Ngôn ngữ chính
TypeScript
Star
2k
Fork
341
Merge trung bình
13 giờ 17 phút
Pull request đã merge (30 ngày)
5

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của microsoft/node-pty

Tất cả issue của microsoft/node-pty

Issue tương tự

Thêm issue về TypeScript

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.