|
37 | 37 | /* http://www.gnu.org/software/gnulib/manual/html_node/forkpty.html */ |
38 | 38 | #if defined(__linux__) |
39 | 39 | #include <pty.h> |
| 40 | +#include <dirent.h> |
| 41 | +#include <sys/syscall.h> |
40 | 42 | #elif defined(__APPLE__) |
41 | 43 | #include <util.h> |
42 | 44 | #elif defined(__FreeBSD__) |
@@ -110,6 +112,40 @@ struct ExitEvent { |
110 | 112 | int exit_code = 0, signal_code = 0; |
111 | 113 | }; |
112 | 114 |
|
| 115 | +#if defined(__linux__) |
| 116 | + |
| 117 | +static int |
| 118 | +SetCloseOnExec(int fd) { |
| 119 | + int flags = fcntl(fd, F_GETFD, 0); |
| 120 | + if (flags == -1) |
| 121 | + return flags; |
| 122 | + if (flags & FD_CLOEXEC) |
| 123 | + return 0; |
| 124 | + return fcntl(fd, F_SETFD, flags | FD_CLOEXEC); |
| 125 | +} |
| 126 | + |
| 127 | +/** |
| 128 | + * Close all file descriptors >= 3 to prevent FD leakage to child processes. |
| 129 | + * Uses close_range() syscall on Linux 5.9+, falls back to /proc/self/fd iteration. |
| 130 | + */ |
| 131 | +static void |
| 132 | +pty_close_inherited_fds() { |
| 133 | + // Try close_range() first (Linux 5.9+, glibc 2.34+) |
| 134 | + #if defined(SYS_close_range) |
| 135 | + if (syscall(SYS_close_range, 3, ~0U, CLOSE_RANGE_CLOEXEC) == 0) { |
| 136 | + return; |
| 137 | + } |
| 138 | + #endif |
| 139 | + |
| 140 | + int fd; |
| 141 | + // Set the CLOEXEC flag on all open descriptors. Unconditionally try the first |
| 142 | + // 16 file descriptors. After that, bail out after the first error. |
| 143 | + for (fd = 3; ; fd++) |
| 144 | + if (SetCloseOnExec(fd) && fd > 15) |
| 145 | + break; |
| 146 | +} |
| 147 | +#endif |
| 148 | + |
113 | 149 | void SetupExitCallback(Napi::Env env, Napi::Function cb, pid_t pid) { |
114 | 150 | std::thread *th = new std::thread; |
115 | 151 | // Don't use Napi::AsyncWorker which is limited by UV_THREADPOOL_SIZE. |
@@ -433,6 +469,9 @@ Napi::Value PtyFork(const Napi::CallbackInfo& info) { |
433 | 469 | } |
434 | 470 | } |
435 | 471 |
|
| 472 | + // Close inherited FDs to prevent leaking pty master FDs to child |
| 473 | + pty_close_inherited_fds(); |
| 474 | + |
436 | 475 | { |
437 | 476 | char **old = environ; |
438 | 477 | environ = env; |
|
0 commit comments