3434#include <string.h>
3535#include <fcntl.h>
3636#include <pthread.h>
37-
37+ #include <dirent.h>
3838#include <stdio.h>
3939
40+ #if __has_include (< linux /close_range .h > )
41+ #include <linux/close_range.h>
42+ #endif
43+
44+ #endif // TARGET_OS_WINDOWS
45+
4046#if __has_include (< crt_externs .h > )
4147#include <crt_externs.h>
4248#elif defined(_WIN32)
@@ -50,6 +56,7 @@ extern char **environ;
5056#include <mach/vm_page_size.h>
5157#endif
5258
59+ #if !TARGET_OS_WINDOWS
5360int _was_process_exited (int status ) {
5461 return WIFEXITED (status );
5562}
@@ -70,24 +77,8 @@ int _was_process_suspended(int status) {
7077 return WIFSTOPPED (status );
7178}
7279
73- #if TARGET_OS_LINUX
74- #include <stdio.h>
75-
76- int _shims_snprintf (
77- char * _Nonnull str ,
78- int len ,
79- const char * _Nonnull format ,
80- char * _Nonnull str1 ,
81- char * _Nonnull str2
82- ) {
83- return snprintf (str , len , format , str1 , str2 );
84- }
85-
86- int _pidfd_send_signal (int pidfd , int signal ) {
87- return syscall (SYS_pidfd_send_signal , pidfd , signal , NULL , 0 );
88- }
80+ #endif // !TARGET_OS_WINDOWS
8981
90- #endif
9182
9283#if __has_include (< mach /vm_page_size .h > )
9384vm_size_t _subprocess_vm_size (void ) {
@@ -96,40 +87,6 @@ vm_size_t _subprocess_vm_size(void) {
9687}
9788#endif
9889
99- // MARK: - Private Helpers
100- static pthread_mutex_t _subprocess_fork_lock = PTHREAD_MUTEX_INITIALIZER ;
101-
102- static int _subprocess_block_everything_but_something_went_seriously_wrong_signals (sigset_t * old_mask ) {
103- sigset_t mask ;
104- int r = 0 ;
105- r |= sigfillset (& mask );
106- r |= sigdelset (& mask , SIGABRT );
107- r |= sigdelset (& mask , SIGBUS );
108- r |= sigdelset (& mask , SIGFPE );
109- r |= sigdelset (& mask , SIGILL );
110- r |= sigdelset (& mask , SIGKILL );
111- r |= sigdelset (& mask , SIGSEGV );
112- r |= sigdelset (& mask , SIGSTOP );
113- r |= sigdelset (& mask , SIGSYS );
114- r |= sigdelset (& mask , SIGTRAP );
115-
116- r |= pthread_sigmask (SIG_BLOCK , & mask , old_mask );
117- return r ;
118- }
119-
120- #define _subprocess_precondition (__cond ) do { \
121- int eval = (__cond); \
122- if (!eval) { \
123- __builtin_trap(); \
124- } \
125- } while(0)
126-
127- #if __DARWIN_NSIG
128- # define _SUBPROCESS_SIG_MAX __DARWIN_NSIG
129- #else
130- # define _SUBPROCESS_SIG_MAX 32
131- #endif
132-
13390
13491// MARK: - Darwin (posix_spawn)
13592#if TARGET_OS_MAC
@@ -351,6 +308,100 @@ static int _clone3(int *pidfd) {
351308 return syscall (SYS_clone3 , & args , sizeof (args ));
352309}
353310
311+ static pthread_mutex_t _subprocess_fork_lock = PTHREAD_MUTEX_INITIALIZER ;
312+
313+ static int _subprocess_make_critical_mask (sigset_t * old_mask ) {
314+ sigset_t mask ;
315+ int r = 0 ;
316+ r |= sigfillset (& mask );
317+ r |= sigdelset (& mask , SIGABRT );
318+ r |= sigdelset (& mask , SIGBUS );
319+ r |= sigdelset (& mask , SIGFPE );
320+ r |= sigdelset (& mask , SIGILL );
321+ r |= sigdelset (& mask , SIGKILL );
322+ r |= sigdelset (& mask , SIGSEGV );
323+ r |= sigdelset (& mask , SIGSTOP );
324+ r |= sigdelset (& mask , SIGSYS );
325+ r |= sigdelset (& mask , SIGTRAP );
326+
327+ r |= pthread_sigmask (SIG_BLOCK , & mask , old_mask );
328+ return r ;
329+ }
330+
331+ #define _subprocess_precondition (__cond ) do { \
332+ int eval = (__cond); \
333+ if (!eval) { \
334+ __builtin_trap(); \
335+ } \
336+ } while(0)
337+
338+ #if __DARWIN_NSIG
339+ # define _SUBPROCESS_SIG_MAX __DARWIN_NSIG
340+ #else
341+ # define _SUBPROCESS_SIG_MAX 32
342+ #endif
343+
344+ int _shims_snprintf (
345+ char * _Nonnull str ,
346+ int len ,
347+ const char * _Nonnull format ,
348+ char * _Nonnull str1 ,
349+ char * _Nonnull str2
350+ ) {
351+ return snprintf (str , len , format , str1 , str2 );
352+ }
353+
354+ static int _positive_int_parse (const char * str ) {
355+ char * end ;
356+ long value = strtol (str , & end , 10 );
357+ if (end == str ) {
358+ // No digits found
359+ return -1 ;
360+ }
361+ if (errno == ERANGE || val <= 0 || val > INT_MAX ) {
362+ // Out of range
363+ return -1 ;
364+ }
365+ return (int )value ;
366+ }
367+
368+ static int _highest_possibly_open_fd_dir (const char * fd_dir ) {
369+ int highest_fd_so_far = 0 ;
370+ DIR * dir_ptr = opendir (fd_dir );
371+ if (dir_ptr == NULL ) {
372+ return -1 ;
373+ }
374+
375+ struct dirent * dir_entry = NULL ;
376+ while ((dir_entry = readdir (dir_ptr )) != NULL ) {
377+ char * entry_name = dir_entry -> d_name ;
378+ int number = _positive_int_parse (entry_name );
379+ if (number > (long )highest_fd_so_far ) {
380+ highest_fd_so_far = number ;
381+ }
382+ }
383+
384+ closedir (dir_ptr );
385+ return highest_fd_so_far ;
386+ }
387+
388+ static int _highest_possibly_open_fd (void ) {
389+ #if defined(__APPLE__ )
390+ int hi = _highest_possibly_open_fd_dir ("/dev/fd" );
391+ if (hi < 0 ) {
392+ hi = getdtablesize ();
393+ }
394+ #elif defined(__linux__ )
395+ int hi = _highest_possibly_open_fd_dir ("/proc/self/fd" );
396+ if (hi < 0 ) {
397+ hi = getdtablesize ();
398+ }
399+ #else
400+ int hi = getdtablesize ();
401+ #endif
402+ return hi ;
403+ }
404+
354405int _subprocess_fork_exec (
355406 pid_t * _Nonnull pid ,
356407 int * _Nonnull pidfd ,
@@ -410,7 +461,7 @@ int _subprocess_fork_exec(
410461 _subprocess_precondition (rc == 0 );
411462 // Block all signals on this thread
412463 sigset_t old_sigmask ;
413- rc = _subprocess_block_everything_but_something_went_seriously_wrong_signals (& old_sigmask );
464+ rc = _subprocess_make_critical_mask (& old_sigmask );
414465 if (rc != 0 ) {
415466 close (pipefd [0 ]);
416467 close (pipefd [1 ]);
@@ -530,20 +581,22 @@ int _subprocess_fork_exec(
530581 if (rc < 0 ) {
531582 write_error_and_exit ;
532583 }
533-
534- // Close parent side
535- if (file_descriptors [1 ] >= 0 ) {
536- rc = close (file_descriptors [1 ]);
537- }
538- if (file_descriptors [3 ] >= 0 ) {
539- rc = close (file_descriptors [3 ]);
540- }
541- if (file_descriptors [5 ] >= 0 ) {
542- rc = close (file_descriptors [5 ]);
543- }
544-
545- if (rc < 0 ) {
546- write_error_and_exit ;
584+ // Close all other file descriptors
585+ rc = -1 ;
586+ errno = ENOSYS ;
587+ #if __has_include (< linux /close_range .h > ) || defined(__FreeBSD__ )
588+ // We must NOT close pipefd[1] for writing errors
589+ rc = close_range (STDERR_FILENO + 1 , pipefd [1 ] - 1 , 0 );
590+ rc |= close_range (pipefd [1 ] + 1 , ~0U , 0 );
591+ #endif
592+ if (rc != 0 ) {
593+ // close_range failed (or doesn't exist), fall back to close()
594+ for (int fd = STDERR_FILENO + 1 ; fd < _highest_possibly_open_fd (); fd ++ ) {
595+ // We must NOT close pipefd[1] for writing errors
596+ if (fd != pipefd [1 ]) {
597+ close (fd );
598+ }
599+ }
547600 }
548601
549602 // Run custom configuratior
@@ -621,8 +674,6 @@ int _subprocess_fork_exec(
621674
622675#endif // TARGET_OS_LINUX
623676
624- #endif // !TARGET_OS_WINDOWS
625-
626677#pragma mark - Environment Locking
627678
628679#if __has_include (< libc_private .h > )
0 commit comments