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+
4044#if __has_include (< crt_externs .h > )
4145#include <crt_externs.h>
4246#elif defined(_WIN32)
@@ -50,6 +54,7 @@ extern char **environ;
5054#include <mach/vm_page_size.h>
5155#endif
5256
57+ #if !TARGET_OS_WINDOWS
5358int _was_process_exited (int status ) {
5459 return WIFEXITED (status );
5560}
@@ -70,8 +75,7 @@ int _was_process_suspended(int status) {
7075 return WIFSTOPPED (status );
7176}
7277
73- #if TARGET_OS_LINUX
74- #include <stdio.h>
78+ #endif // !TARGET_OS_WINDOWS
7579
7680#ifndef SYS_pidfd_send_signal
7781#define SYS_pidfd_send_signal 424
@@ -100,40 +104,6 @@ vm_size_t _subprocess_vm_size(void) {
100104}
101105#endif
102106
103- // MARK: - Private Helpers
104- static pthread_mutex_t _subprocess_fork_lock = PTHREAD_MUTEX_INITIALIZER ;
105-
106- static int _subprocess_block_everything_but_something_went_seriously_wrong_signals (sigset_t * old_mask ) {
107- sigset_t mask ;
108- int r = 0 ;
109- r |= sigfillset (& mask );
110- r |= sigdelset (& mask , SIGABRT );
111- r |= sigdelset (& mask , SIGBUS );
112- r |= sigdelset (& mask , SIGFPE );
113- r |= sigdelset (& mask , SIGILL );
114- r |= sigdelset (& mask , SIGKILL );
115- r |= sigdelset (& mask , SIGSEGV );
116- r |= sigdelset (& mask , SIGSTOP );
117- r |= sigdelset (& mask , SIGSYS );
118- r |= sigdelset (& mask , SIGTRAP );
119-
120- r |= pthread_sigmask (SIG_BLOCK , & mask , old_mask );
121- return r ;
122- }
123-
124- #define _subprocess_precondition (__cond ) do { \
125- int eval = (__cond); \
126- if (!eval) { \
127- __builtin_trap(); \
128- } \
129- } while(0)
130-
131- #if __DARWIN_NSIG
132- # define _SUBPROCESS_SIG_MAX __DARWIN_NSIG
133- #else
134- # define _SUBPROCESS_SIG_MAX 32
135- #endif
136-
137107
138108// MARK: - Darwin (posix_spawn)
139109#if TARGET_OS_MAC
@@ -374,6 +344,100 @@ static int _clone3(int *pidfd) {
374344 return syscall (SYS_clone3 , & args , sizeof (args ));
375345}
376346
347+ static pthread_mutex_t _subprocess_fork_lock = PTHREAD_MUTEX_INITIALIZER ;
348+
349+ static int _subprocess_make_critical_mask (sigset_t * old_mask ) {
350+ sigset_t mask ;
351+ int r = 0 ;
352+ r |= sigfillset (& mask );
353+ r |= sigdelset (& mask , SIGABRT );
354+ r |= sigdelset (& mask , SIGBUS );
355+ r |= sigdelset (& mask , SIGFPE );
356+ r |= sigdelset (& mask , SIGILL );
357+ r |= sigdelset (& mask , SIGKILL );
358+ r |= sigdelset (& mask , SIGSEGV );
359+ r |= sigdelset (& mask , SIGSTOP );
360+ r |= sigdelset (& mask , SIGSYS );
361+ r |= sigdelset (& mask , SIGTRAP );
362+
363+ r |= pthread_sigmask (SIG_BLOCK , & mask , old_mask );
364+ return r ;
365+ }
366+
367+ #define _subprocess_precondition (__cond ) do { \
368+ int eval = (__cond); \
369+ if (!eval) { \
370+ __builtin_trap(); \
371+ } \
372+ } while(0)
373+
374+ #if __DARWIN_NSIG
375+ # define _SUBPROCESS_SIG_MAX __DARWIN_NSIG
376+ #else
377+ # define _SUBPROCESS_SIG_MAX 32
378+ #endif
379+
380+ int _shims_snprintf (
381+ char * _Nonnull str ,
382+ int len ,
383+ const char * _Nonnull format ,
384+ char * _Nonnull str1 ,
385+ char * _Nonnull str2
386+ ) {
387+ return snprintf (str , len , format , str1 , str2 );
388+ }
389+
390+ static int _positive_int_parse (const char * str ) {
391+ char * end ;
392+ long value = strtol (str , & end , 10 );
393+ if (end == str ) {
394+ // No digits found
395+ return -1 ;
396+ }
397+ if (errno == ERANGE || val <= 0 || val > INT_MAX ) {
398+ // Out of range
399+ return -1 ;
400+ }
401+ return (int )value ;
402+ }
403+
404+ static int _highest_possibly_open_fd_dir (const char * fd_dir ) {
405+ int highest_fd_so_far = 0 ;
406+ DIR * dir_ptr = opendir (fd_dir );
407+ if (dir_ptr == NULL ) {
408+ return -1 ;
409+ }
410+
411+ struct dirent * dir_entry = NULL ;
412+ while ((dir_entry = readdir (dir_ptr )) != NULL ) {
413+ char * entry_name = dir_entry -> d_name ;
414+ int number = _positive_int_parse (entry_name );
415+ if (number > (long )highest_fd_so_far ) {
416+ highest_fd_so_far = number ;
417+ }
418+ }
419+
420+ closedir (dir_ptr );
421+ return highest_fd_so_far ;
422+ }
423+
424+ static int _highest_possibly_open_fd (void ) {
425+ #if defined(__APPLE__ )
426+ int hi = _highest_possibly_open_fd_dir ("/dev/fd" );
427+ if (hi < 0 ) {
428+ hi = getdtablesize ();
429+ }
430+ #elif defined(__linux__ )
431+ int hi = _highest_possibly_open_fd_dir ("/proc/self/fd" );
432+ if (hi < 0 ) {
433+ hi = getdtablesize ();
434+ }
435+ #else
436+ int hi = getdtablesize ();
437+ #endif
438+ return hi ;
439+ }
440+
377441int _subprocess_fork_exec (
378442 pid_t * _Nonnull pid ,
379443 int * _Nonnull pidfd ,
@@ -433,7 +497,7 @@ int _subprocess_fork_exec(
433497 _subprocess_precondition (rc == 0 );
434498 // Block all signals on this thread
435499 sigset_t old_sigmask ;
436- rc = _subprocess_block_everything_but_something_went_seriously_wrong_signals (& old_sigmask );
500+ rc = _subprocess_make_critical_mask (& old_sigmask );
437501 if (rc != 0 ) {
438502 close (pipefd [0 ]);
439503 close (pipefd [1 ]);
@@ -556,20 +620,22 @@ int _subprocess_fork_exec(
556620 if (rc < 0 ) {
557621 write_error_and_exit ;
558622 }
559-
560- // Close parent side
561- if (file_descriptors [1 ] >= 0 ) {
562- rc = close (file_descriptors [1 ]);
563- }
564- if (file_descriptors [3 ] >= 0 ) {
565- rc = close (file_descriptors [3 ]);
566- }
567- if (file_descriptors [5 ] >= 0 ) {
568- rc = close (file_descriptors [5 ]);
569- }
570-
571- if (rc < 0 ) {
572- write_error_and_exit ;
623+ // Close all other file descriptors
624+ rc = -1 ;
625+ errno = ENOSYS ;
626+ #if __has_include (< linux /close_range .h > ) || defined(__FreeBSD__ )
627+ // We must NOT close pipefd[1] for writing errors
628+ rc = close_range (STDERR_FILENO + 1 , pipefd [1 ] - 1 , 0 );
629+ rc |= close_range (pipefd [1 ] + 1 , ~0U , 0 );
630+ #endif
631+ if (rc != 0 ) {
632+ // close_range failed (or doesn't exist), fall back to close()
633+ for (int fd = STDERR_FILENO + 1 ; fd < _highest_possibly_open_fd (); fd ++ ) {
634+ // We must NOT close pipefd[1] for writing errors
635+ if (fd != pipefd [1 ]) {
636+ close (fd );
637+ }
638+ }
573639 }
574640
575641 // Run custom configuratior
@@ -647,8 +713,6 @@ int _subprocess_fork_exec(
647713
648714#endif // TARGET_OS_LINUX
649715
650- #endif // !TARGET_OS_WINDOWS
651-
652716#pragma mark - Environment Locking
653717
654718#if __has_include (< libc_private .h > )
0 commit comments