Skip to content

Commit dfd5a0f

Browse files
committed
Emulate POSIX_SPAWN_CLOEXEC_DEFAULT in fork/exec.
POSIX_SPAWN_CLOEXEC_DEFAULT is only available on Darwin. Emulate POSIX_SPAWN_CLOEXEC_DEFAULT on other platforms by calling close after fork, before exec. This commit also removes _subprocess_posix_spawn_fallback because we can't emulate POSIX_SPAWN_CLOEXEC_DEFAULT in a thread-safe manner while using posix_spawn.
1 parent f9881e5 commit dfd5a0f

File tree

1 file changed

+118
-54
lines changed

1 file changed

+118
-54
lines changed

Sources/_SubprocessCShims/process_shims.c

Lines changed: 118 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,13 @@
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
5358
int _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+
377441
int _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

Comments
 (0)