FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

[GR-76019] Propagate vfork option to fork_exec · oracle/graalpython@97f5003 · GitHub

Commit 97f5003

Browse files
committed
[GR-76019] Propagate vfork option to fork_exec
1 parent f0c0d69 commit 97f5003

7 files changed

Lines changed: 199 additions & 28 deletions

File tree

‎graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/PosixSubprocessModuleBuiltins.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -303,7 +303,7 @@ static int forkExec(VirtualFrame frame, Object[] args, Object executableList, bo
303303
gil.release(true);
304304
try {
305305
return posixLib.forkExec(context.getPosixSupport(), executables, processArgs, cwd, env == null ? null : (Object[]) env, stdinRead, stdinWrite, stdoutRead, stdoutWrite, stderrRead,
306-
stderrWrite, errPipeRead, errPipeWrite, closeFds, restoreSignals, callSetsid, fdsToKeep);
306+
stderrWrite, errPipeRead, errPipeWrite, closeFds, restoreSignals, callSetsid, fdsToKeep, allowVFork);
307307
} catch (PosixException e) {
308308
gil.acquire();
309309
throw constructAndRaiseNode.get(inliningTarget).raiseOSErrorFromPosixException(frame, e);

‎graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/EmulatedPosixSupport.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2445,7 +2445,7 @@ public void unsetenv(Object name) {
24452445
@ExportMessage
24462446
@TruffleBoundary
24472447
public int forkExec(Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd, int stderrReadFd, int stderrWriteFd,
2448-
int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep,
2448+
int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep, boolean allowVFork,
24492449
@Shared("js2ts") @Cached TruffleString.FromJavaStringNode fromJavaStringNode) throws PosixException {
24502450
if (PythonImageBuildOptions.WITHOUT_PLATFORM_ACCESS) {
24512451
throw new UnsupportedPosixFeatureException("forkExec was excluded");

‎graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/LoggingPosixSupport.java‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,13 +1114,13 @@ public void mmapWriteBytes(Object mmap, long index, byte[] bytes, int length,
11141114

11151115
@ExportMessage
11161116
final int forkExec(Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd, int stderrReadFd, int stderrWriteFd,
1117-
int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep,
1117+
int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep, boolean allowVFork,
11181118
@CachedLibrary("this.delegate") PosixSupportLibrary lib) throws PosixException {
1119-
logEnter("forkExec", "%s, %s, %s, %s, %d, %d, %d, %d, %d, %d, %d, %d, %b, %b, %b, %s", executables, args, cwd, env, stdinReadFd, stdinWriteFd, stdoutReadFd, stdoutWriteFd, stderrReadFd,
1120-
stderrWriteFd, errPipeReadFd, errPipeWriteFd, closeFds, restoreSignals, callSetsid, fdsToKeep);
1119+
logEnter("forkExec", "%s, %s, %s, %s, %d, %d, %d, %d, %d, %d, %d, %d, %b, %b, %b, %s, %b", executables, args, cwd, env, stdinReadFd, stdinWriteFd, stdoutReadFd, stdoutWriteFd, stderrReadFd,
1120+
stderrWriteFd, errPipeReadFd, errPipeWriteFd, closeFds, restoreSignals, callSetsid, fdsToKeep, allowVFork);
11211121
try {
11221122
return logExit("forkExec", "%d", lib.forkExec(delegate, executables, args, cwd, env, stdinReadFd, stdinWriteFd, stdoutReadFd, stdoutWriteFd, stderrReadFd, stderrWriteFd, errPipeReadFd,
1123-
errPipeWriteFd, closeFds, restoreSignals, callSetsid, fdsToKeep));
1123+
errPipeWriteFd, closeFds, restoreSignals, callSetsid, fdsToKeep, allowVFork));
11241124
} catch (PosixException e) {
11251125
throw logException("forkExec", e);
11261126
}

‎graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/NativePosixSupport.java‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -442,10 +442,10 @@ abstract static class PosixNativeFunctionInvoker {
442442

443443
@DowncallSignature(returnType = SINT32, argumentTypes = {
444444
POINTER, POINTER, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, SINT32,
445-
SINT32, SINT32, SINT32, SINT32, SINT32, POINTER, SINT64
445+
SINT32, SINT32, SINT32, SINT32, SINT32, SINT32, POINTER, SINT64
446446
})
447447
abstract int fork_exec(long data, long offsets, int offsetsLen, int argsPos, int envPos, int cwdPos, int stdinRdFd, int stdinWrFd, int stdoutRdFd, int stdoutWrFd, int stderrRdFd,
448-
int stderrWrFd, int errPipeRdFd, int errPipeWrFd, int closeFds, int restoreSignals, int callSetsid, long fdsToKeep, long fdsToKeepLen);
448+
int stderrWrFd, int errPipeRdFd, int errPipeWrFd, int closeFds, int restoreSignals, int callSetsid, int allowVFork, long fdsToKeep, long fdsToKeepLen);
449449

450450
@DowncallSignature(returnType = VOID, argumentTypes = {POINTER, POINTER, SINT32})
451451
abstract void call_execv(long data, long offsets, int offsetsLen);
@@ -1845,7 +1845,7 @@ public void unsetenv(Object name) throws PosixException {
18451845

18461846
@ExportMessage
18471847
public int forkExec(Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd, int stderrReadFd, int stderrWriteFd,
1848-
int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep) throws PosixException {
1848+
int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep, boolean allowVFork) throws PosixException {
18491849

18501850
// The following strings and string arrays need to be present in the native function:
18511851
// - char** of executable names ('\0'-terminated strings with an extra NULL at the end)
@@ -1943,6 +1943,7 @@ public int forkExec(Object[] executables, Object[] args, Object cwd, Object[] en
19431943
closeFds ? 1 : 0,
19441944
restoreSignals ? 1 : 0,
19451945
callSetsid ? 1 : 0,
1946+
allowVFork ? 1 : 0,
19461947
nativeFdsToKeep, fdsToKeep.length);
19471948
if (res == -1) {
19481949
throw getErrnoAndThrowPosixException();

‎graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PosixSupportLibrary.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,8 @@ public record OpenPtyResult(int masterFd, int slaveFd) {
355355
public abstract void unsetenv(Object receiver, Object name) throws PosixException;
356356

357357
public abstract int forkExec(Object receiver, Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd,
358-
int stderrReadFd, int stderrWriteFd, int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep) throws PosixException;
358+
int stderrReadFd, int stderrWriteFd, int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep,
359+
boolean allowVFork) throws PosixException;
359360

360361
// args.length must be > 0
361362
public abstract void execv(Object receiver, Object pathname, Object[] args) throws PosixException;

‎graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PreInitPosixSupport.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -807,11 +807,11 @@ final void unsetenv(Object name,
807807

808808
@ExportMessage
809809
final int forkExec(Object[] executables, Object[] args, Object cwd, Object[] env, int stdinReadFd, int stdinWriteFd, int stdoutReadFd, int stdoutWriteFd, int stderrReadFd, int stderrWriteFd,
810-
int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep,
810+
int errPipeReadFd, int errPipeWriteFd, boolean closeFds, boolean restoreSignals, boolean callSetsid, int[] fdsToKeep, boolean allowVFork,
811811
@CachedLibrary("this.nativePosixSupport") PosixSupportLibrary nativeLib) throws PosixException {
812812
checkNotInPreInitialization();
813813
return nativeLib.forkExec(nativePosixSupport, executables, args, cwd, env, stdinReadFd, stdinWriteFd, stdoutReadFd, stdoutWriteFd, stderrReadFd, stderrWriteFd, errPipeReadFd, errPipeWriteFd,
814-
closeFds, restoreSignals, callSetsid, fdsToKeep);
814+
closeFds, restoreSignals, callSetsid, fdsToKeep, allowVFork);
815815
}
816816

817817
@ExportMessage

‎graalpython/python-libposix/src/fork_exec.c‎

Lines changed: 184 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,34 @@
1111
#include <fcntl.h>
1212
#include <sys/types.h>
1313
#include <sys/stat.h>
14+
#if defined(__linux__)
15+
#include <sys/syscall.h>
16+
#endif
1417
#include <dirent.h>
1518
#include <errno.h>
1619
#include <assert.h>
1720
#include <string.h>
21+
#include <limits.h>
22+
#include <signal.h>
1823

1924
// These definitions emulate CPython's equivalents so that the copy&pasted code below works without too many changes
2025
#define HAVE_DIRFD 1
2126
#define HAVE_SETSID 1
2227

28+
#if defined(__GNUC__) || defined(__clang__)
29+
#define Py_NO_INLINE __attribute__((noinline))
30+
#elif defined(_MSC_VER)
31+
#define Py_NO_INLINE __declspec(noinline)
32+
#else
33+
#define Py_NO_INLINE
34+
#endif
35+
36+
#if defined(__linux__)
37+
/* If this is ever expanded to non-Linux platforms, verify what calls are
38+
* allowed after vfork(). Ex: setsid() may be disallowed on macOS? */
39+
#define VFORK_USABLE 1
40+
#endif
41+
2342
int32_t set_inheritable(int32_t fd, int32_t inheritable);
2443

2544
// TODO JDK also handles EINTR for dup2 calls
@@ -176,6 +195,47 @@ _close_fds_by_brute_force(long start_fd, int *fds_to_keep, ssize_t num_fds_to_ke
176195
}
177196
}
178197

198+
#if defined(__linux__) && defined(SYS_getdents64)
199+
struct linux_dirent64 {
200+
unsigned long long d_ino;
201+
long long d_off;
202+
unsigned short d_reclen;
203+
unsigned char d_type;
204+
char d_name[256];
205+
};
206+
207+
static void
208+
_close_open_fds_safe(long start_fd, int *fds_to_keep, ssize_t fds_to_keep_len)
209+
{
210+
int fd_dir_fd = open(FD_DIR, O_RDONLY);
211+
if (fd_dir_fd == -1) {
212+
_close_fds_by_brute_force(start_fd, fds_to_keep, fds_to_keep_len);
213+
return;
214+
}
215+
216+
char buffer[sizeof(struct linux_dirent64)];
217+
int bytes;
218+
while ((bytes = syscall(SYS_getdents64, fd_dir_fd, (struct linux_dirent64 *)buffer, sizeof(buffer))) > 0) {
219+
struct linux_dirent64 *entry;
220+
int offset;
221+
for (offset = 0; offset < bytes; offset += entry->d_reclen) {
222+
int fd;
223+
entry = (struct linux_dirent64 *)(buffer + offset);
224+
if ((fd = _pos_int_from_ascii(entry->d_name)) < 0)
225+
continue;
226+
if (fd != fd_dir_fd && fd >= start_fd &&
227+
!_is_fd_in_sorted_fd_sequence(fd, fds_to_keep, fds_to_keep_len)) {
228+
close(fd);
229+
}
230+
}
231+
}
232+
close(fd_dir_fd);
233+
}
234+
235+
#define _close_open_fds _close_open_fds_safe
236+
237+
#else
238+
179239
/* Close all open file descriptors from start_fd and higher.
180240
* Do not close any in the sorted py_fds_to_keep tuple.
181241
*
@@ -242,6 +302,36 @@ _close_open_fds_maybe_unsafe(long start_fd, int* fds_to_keep, ssize_t fds_to_kee
242302

243303
#define _close_open_fds _close_open_fds_maybe_unsafe
244304

305+
#endif
306+
307+
#ifdef VFORK_USABLE
308+
static void
309+
reset_signal_handlers(const sigset_t *child_sigmask)
310+
{
311+
struct sigaction sa_dfl = {.sa_handler = SIG_DFL};
312+
for (int sig = 1; sig < _NSIG; sig++) {
313+
if (sig == SIGKILL || sig == SIGSTOP) {
314+
continue;
315+
}
316+
if (sigismember(child_sigmask, sig) == 1) {
317+
continue;
318+
}
319+
320+
struct sigaction sa;
321+
if (sigaction(sig, NULL, &sa) == -1) {
322+
continue;
323+
}
324+
325+
void *h = (sa.sa_flags & SA_SIGINFO ? (void *)sa.sa_sigaction :
326+
(void *)sa.sa_handler);
327+
if (h == SIG_IGN || h == SIG_DFL) {
328+
continue;
329+
}
330+
(void) sigaction(sig, &sa_dfl, NULL);
331+
}
332+
}
333+
#endif
334+
245335
/*
246336
* This function is code executed in the child process immediately after fork
247337
* to set things up and call exec().
@@ -252,8 +342,11 @@ _close_open_fds_maybe_unsafe(long start_fd, int* fds_to_keep, ssize_t fds_to_kee
252342
*
253343
* This restriction is documented at
254344
* http://www.opengroup.org/onlinepubs/009695399/functions/fork.html.
345+
*
346+
* If this function is called after vfork(), even more care must be taken.
347+
* The child shares the parent's address space until execve() or _exit().
255348
*/
256-
static void
349+
Py_NO_INLINE static void
257350
child_exec(char *const exec_array[],
258351
char *const argv[],
259352
char *const envp[],
@@ -264,6 +357,7 @@ child_exec(char *const exec_array[],
264357
int errpipe_read, int errpipe_write,
265358
int close_fds, int restore_signals,
266359
int call_setsid,
360+
const void *child_sigmask,
267361
int *fds_to_keep,
268362
ssize_t fds_to_keep_len)
269363
{
@@ -338,6 +432,15 @@ child_exec(char *const exec_array[],
338432
if (restore_signals)
339433
_Py_RestoreSignals();
340434

435+
#ifdef VFORK_USABLE
436+
if (child_sigmask) {
437+
reset_signal_handlers((const sigset_t *)child_sigmask);
438+
if ((errno = pthread_sigmask(SIG_SETMASK, child_sigmask, NULL))) {
439+
goto error;
440+
}
441+
}
442+
#endif
443+
341444
#ifdef HAVE_SETSID
342445
if (call_setsid)
343446
POSIX_CALL(setsid());
@@ -394,6 +497,49 @@ child_exec(char *const exec_array[],
394497
_Py_write_noraise(errpipe_write, err_msg, strlen(err_msg));
395498
}
396499

500+
Py_NO_INLINE static pid_t
501+
do_fork_exec(char *const exec_list[],
502+
char *const argv[],
503+
char *const envp[],
504+
const char *cwd,
505+
int stdinRdFd, int stdinWrFd,
506+
int stdoutRdFd, int stdoutWrFd,
507+
int stderrRdFd, int stderrWrFd,
508+
int errPipeRdFd, int errPipeWrFd,
509+
int closeFds, int restoreSignals,
510+
int callSetsid,
511+
const void *childSigmask,
512+
int *fdsToKeep, int64_t fdsToKeepLen)
513+
{
514+
pid_t pid;
515+
#ifdef VFORK_USABLE
516+
if (childSigmask) {
517+
pid = vfork();
518+
} else
519+
#endif
520+
{
521+
pid = fork();
522+
}
523+
524+
if (pid != 0) {
525+
return pid;
526+
}
527+
528+
child_exec(
529+
exec_list, argv, envp, cwd,
530+
stdinRdFd, stdinWrFd,
531+
stdoutRdFd, stdoutWrFd,
532+
stderrRdFd, stderrWrFd,
533+
errPipeRdFd, errPipeWrFd,
534+
closeFds,
535+
restoreSignals,
536+
callSetsid,
537+
childSigmask,
538+
fdsToKeep, fdsToKeepLen
539+
);
540+
_exit(255);
541+
}
542+
397543

398544
/*
399545
* data, offsets, offsetsLen, argsPos, envPos, cwdPos - see comment in NativePosixSupport.forkExec()
@@ -412,6 +558,7 @@ child_exec(char *const exec_array[],
412558
* (if nonzero, then errPipeWrFd must be in fdsToKeep)
413559
* restoreSignals - currently not used
414560
* callSetsid - if nonzero, the child calls setsid before exec()
561+
* allowVFork - if nonzero, use vfork() instead of fork() where it is safe and supported
415562
* fdsToKeep, fdsToKeepLen - a sorted list of fds to keep open (the child clears their O_CLOEXEC)
416563
*/
417564
int32_t fork_exec(
@@ -423,6 +570,7 @@ int32_t fork_exec(
423570
int32_t closeFds,
424571
int32_t restoreSignals,
425572
int32_t callSetsid,
573+
int32_t allowVFork,
426574
int32_t *fdsToKeep, int64_t fdsToKeepLen
427575
) {
428576

@@ -437,20 +585,41 @@ int32_t fork_exec(
437585
char **envp = envPos == -1 ? NULL : strings + envPos;
438586
char *cwd = cwdPos == -1 ? NULL : strings[cwdPos];
439587

440-
pid_t pid = fork();
441-
if (pid == 0) {
442-
child_exec(
443-
exec_list, argv, envp, cwd,
444-
stdinRdFd, stdinWrFd,
445-
stdoutRdFd, stdoutWrFd,
446-
stderrRdFd, stderrWrFd,
447-
errPipeRdFd, errPipeWrFd,
448-
closeFds,
449-
restoreSignals,
450-
callSetsid,
451-
fdsToKeep, fdsToKeepLen
452-
);
453-
_exit(255);
588+
#ifdef VFORK_USABLE
589+
const void *oldSigmask = NULL;
590+
sigset_t oldSigs;
591+
if (allowVFork) {
592+
sigset_t allSigs;
593+
sigfillset(&allSigs);
594+
int err = pthread_sigmask(SIG_BLOCK, &allSigs, &oldSigs);
595+
if (err) {
596+
errno = err;
597+
return -1;
598+
}
599+
oldSigmask = &oldSigs;
600+
}
601+
#else
602+
const void *oldSigmask = NULL;
603+
#endif
604+
605+
pid_t pid = do_fork_exec(
606+
exec_list, argv, envp, cwd,
607+
stdinRdFd, stdinWrFd,
608+
stdoutRdFd, stdoutWrFd,
609+
stderrRdFd, stderrWrFd,
610+
errPipeRdFd, errPipeWrFd,
611+
closeFds,
612+
restoreSignals,
613+
callSetsid,
614+
oldSigmask,
615+
fdsToKeep, fdsToKeepLen
616+
);
617+
618+
#ifdef VFORK_USABLE
619+
if (oldSigmask) {
620+
(void) pthread_sigmask(SIG_SETMASK, oldSigmask, NULL);
454621
}
622+
#endif
623+
455624
return pid;
456625
}

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL