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
@@ -336,6 +293,100 @@ static int _clone3(int *pidfd) {
336293 return syscall (SYS_clone3 , & args , sizeof (args ));
337294}
338295
296+ static pthread_mutex_t _subprocess_fork_lock = PTHREAD_MUTEX_INITIALIZER ;
297+
298+ static int _subprocess_make_critical_mask (sigset_t * old_mask ) {
299+ sigset_t mask ;
300+ int r = 0 ;
301+ r |= sigfillset (& mask );
302+ r |= sigdelset (& mask , SIGABRT );
303+ r |= sigdelset (& mask , SIGBUS );
304+ r |= sigdelset (& mask , SIGFPE );
305+ r |= sigdelset (& mask , SIGILL );
306+ r |= sigdelset (& mask , SIGKILL );
307+ r |= sigdelset (& mask , SIGSEGV );
308+ r |= sigdelset (& mask , SIGSTOP );
309+ r |= sigdelset (& mask , SIGSYS );
310+ r |= sigdelset (& mask , SIGTRAP );
311+
312+ r |= pthread_sigmask (SIG_BLOCK , & mask , old_mask );
313+ return r ;
314+ }
315+
316+ #define _subprocess_precondition (__cond ) do { \
317+ int eval = (__cond); \
318+ if (!eval) { \
319+ __builtin_trap(); \
320+ } \
321+ } while(0)
322+
323+ #if __DARWIN_NSIG
324+ # define _SUBPROCESS_SIG_MAX __DARWIN_NSIG
325+ #else
326+ # define _SUBPROCESS_SIG_MAX 32
327+ #endif
328+
329+ int _shims_snprintf (
330+ char * _Nonnull str ,
331+ int len ,
332+ const char * _Nonnull format ,
333+ char * _Nonnull str1 ,
334+ char * _Nonnull str2
335+ ) {
336+ return snprintf (str , len , format , str1 , str2 );
337+ }
338+
339+ static int _positive_int_parse (const char * str ) {
340+ char * end ;
341+ long value = strtol (str , & end , 10 );
342+ if (end == str ) {
343+ // No digits found
344+ return -1 ;
345+ }
346+ if (errno == ERANGE || val <= 0 || val > INT_MAX ) {
347+ // Out of range
348+ return -1 ;
349+ }
350+ return (int )value ;
351+ }
352+
353+ static int _highest_possibly_open_fd_dir (const char * fd_dir ) {
354+ int highest_fd_so_far = 0 ;
355+ DIR * dir_ptr = opendir (fd_dir );
356+ if (dir_ptr == NULL ) {
357+ return -1 ;
358+ }
359+
360+ struct dirent * dir_entry = NULL ;
361+ while ((dir_entry = readdir (dir_ptr )) != NULL ) {
362+ char * entry_name = dir_entry -> d_name ;
363+ int number = _positive_int_parse (entry_name );
364+ if (number > (long )highest_fd_so_far ) {
365+ highest_fd_so_far = number ;
366+ }
367+ }
368+
369+ closedir (dir_ptr );
370+ return highest_fd_so_far ;
371+ }
372+
373+ static int _highest_possibly_open_fd (void ) {
374+ #if defined(__APPLE__ )
375+ int hi = _highest_possibly_open_fd_dir ("/dev/fd" );
376+ if (hi < 0 ) {
377+ hi = getdtablesize ();
378+ }
379+ #elif defined(__linux__ )
380+ int hi = _highest_possibly_open_fd_dir ("/proc/self/fd" );
381+ if (hi < 0 ) {
382+ hi = getdtablesize ();
383+ }
384+ #else
385+ int hi = getdtablesize ();
386+ #endif
387+ return hi ;
388+ }
389+
339390int _subprocess_fork_exec (
340391 pid_t * _Nonnull pid ,
341392 int * _Nonnull pidfd ,
@@ -395,7 +446,7 @@ int _subprocess_fork_exec(
395446 _subprocess_precondition (rc == 0 );
396447 // Block all signals on this thread
397448 sigset_t old_sigmask ;
398- rc = _subprocess_block_everything_but_something_went_seriously_wrong_signals (& old_sigmask );
449+ rc = _subprocess_make_critical_mask (& old_sigmask );
399450 if (rc != 0 ) {
400451 close (pipefd [0 ]);
401452 close (pipefd [1 ]);
@@ -515,20 +566,22 @@ int _subprocess_fork_exec(
515566 if (rc < 0 ) {
516567 write_error_and_exit ;
517568 }
518-
519- // Close parent side
520- if (file_descriptors [1 ] >= 0 ) {
521- rc = close (file_descriptors [1 ]);
522- }
523- if (file_descriptors [3 ] >= 0 ) {
524- rc = close (file_descriptors [3 ]);
525- }
526- if (file_descriptors [5 ] >= 0 ) {
527- rc = close (file_descriptors [5 ]);
528- }
529-
530- if (rc < 0 ) {
531- write_error_and_exit ;
569+ // Close all other file descriptors
570+ rc = -1 ;
571+ errno = ENOSYS ;
572+ #if __has_include (< linux /close_range .h > ) || defined(__FreeBSD__ )
573+ // We must NOT close pipefd[1] for writing errors
574+ rc = close_range (STDERR_FILENO + 1 , pipefd [1 ] - 1 , 0 );
575+ rc |= close_range (pipefd [1 ] + 1 , ~0U , 0 );
576+ #endif
577+ if (rc != 0 ) {
578+ // close_range failed (or doesn't exist), fall back to close()
579+ for (int fd = STDERR_FILENO + 1 ; fd < _highest_possibly_open_fd (); fd ++ ) {
580+ // We must NOT close pipefd[1] for writing errors
581+ if (fd != pipefd [1 ]) {
582+ close (fd );
583+ }
584+ }
532585 }
533586
534587 // Run custom configuratior
@@ -606,8 +659,6 @@ int _subprocess_fork_exec(
606659
607660#endif // TARGET_OS_LINUX
608661
609- #endif // !TARGET_OS_WINDOWS
610-
611662#pragma mark - Environment Locking
612663
613664#if __has_include (< libc_private .h > )
0 commit comments