-
Notifications
You must be signed in to change notification settings - Fork 6.1k
8357089: Remove VFORK launch mechanism from Process implementation (linux) #25768
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -57,7 +57,7 @@ | |
* changing paths... | ||
* - then exec(2) the target binary | ||
* | ||
* There are three ways to fork off: | ||
* On the OS-side are four ways to fork off: | ||
* | ||
* A) fork(2). Portable and safe (no side effects) but may fail with ENOMEM on | ||
* all Unices when invoked from a VM with a high memory footprint. On Unices | ||
|
@@ -74,34 +74,23 @@ | |
* | ||
* B) vfork(2): Portable and fast but very unsafe. It bypasses the memory | ||
* problems related to fork(2) by starting the child in the memory image of | ||
* the parent. Things that can go wrong include: | ||
* - Programming errors in the child process before the exec(2) call may | ||
* trash memory in the parent process, most commonly the stack of the | ||
* thread invoking vfork. | ||
* - Signals received by the child before the exec(2) call may be at best | ||
* misdirected to the parent, at worst immediately kill child and parent. | ||
* | ||
* This is mitigated by very strict rules about what one is allowed to do in | ||
* the child process between vfork(2) and exec(2), which is basically nothing. | ||
* However, we always broke this rule by doing the pre-exec work between | ||
* vfork(2) and exec(2). | ||
* | ||
* Also note that vfork(2) has been deprecated by the OpenGroup, presumably | ||
* because of its many dangers. | ||
* the parent. | ||
* *** This mode is inherently dangerous, and the danger partly outside the control | ||
* of the programmer (for details, see JDK-8357090). Therefore, we deprecated | ||
* the vfork mode with JDK 25 and removed if with JDK 26. *** | ||
* | ||
* C) clone(2): This is a Linux specific call which gives the caller fine | ||
* grained control about how exactly the process fork is executed. It is | ||
* powerful, but Linux-specific. | ||
* | ||
* Aside from these three possibilities there is a forth option: posix_spawn(3). | ||
* Where fork/vfork/clone all fork off the process and leave pre-exec work and | ||
* calling exec(2) to the user, posix_spawn(3) offers the user fork+exec-like | ||
* functionality in one package, similar to CreateProcess() on Windows. | ||
* | ||
* It is not a system call in itself, but usually a wrapper implemented within | ||
* the libc in terms of one of (fork|vfork|clone)+exec - so whether or not it | ||
* has advantages over calling the naked (fork|vfork|clone) functions depends | ||
* on how posix_spawn(3) is implemented. | ||
* D) posix_spawn(3): Where fork/vfork/clone all fork off the process and leave | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would downplay the OS launch options we don't use, only saying that vfork is dangerous (and a bit as to why) but there's no need to mention clone. And there's no need to mention the the deprecation or removal, when this is integrated its gone. |
||
* pre-exec work and calling exec(2) to the user, posix_spawn(3) offers the user | ||
* fork+exec-like functionality in one package, similar to CreateProcess() on Windows. | ||
* It is not a system call, but usually a wrapper implemented within the libc in terms | ||
* of one of (fork|vfork|clone)+exec - so whether or not it has advantages over calling | ||
* the naked (fork|vfork|clone) functions depends on how posix_spawn(3) is implemented. | ||
* Note, however, that even if posix_spawn(3) uses vfork(2) internally, that is still fine - | ||
* the assumption here is that the libc developers know how to mitigate the vfork problems. | ||
* | ||
* Note that when using posix_spawn(3), we exec twice: first a tiny binary called | ||
* the jspawnhelper, then in the jspawnhelper we do the pre-exec work and exec a | ||
|
@@ -486,28 +475,6 @@ static int copystrings(char *buf, int offset, const char * const *arg) { | |
__attribute_noinline__ | ||
#endif | ||
|
||
/* vfork(2) is deprecated on Darwin */ | ||
#ifndef __APPLE__ | ||
static pid_t | ||
vforkChild(ChildStuff *c) { | ||
volatile pid_t resultPid; | ||
|
||
/* | ||
* We separate the call to vfork into a separate function to make | ||
* very sure to keep stack of child from corrupting stack of parent, | ||
* as suggested by the scary gcc warning: | ||
* warning: variable 'foo' might be clobbered by 'longjmp' or 'vfork' | ||
*/ | ||
resultPid = vfork(); | ||
|
||
if (resultPid == 0) { | ||
childProcess(c); | ||
} | ||
assert(resultPid != 0); /* childProcess never returns */ | ||
return resultPid; | ||
} | ||
#endif | ||
|
||
static pid_t | ||
forkChild(ChildStuff *c) { | ||
pid_t resultPid; | ||
|
@@ -652,11 +619,6 @@ spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) | |
static pid_t | ||
startChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) { | ||
switch (c->mode) { | ||
/* vfork(2) is deprecated on Darwin*/ | ||
#ifndef __APPLE__ | ||
case MODE_VFORK: | ||
return vforkChild(c); | ||
#endif | ||
case MODE_FORK: | ||
return forkChild(c); | ||
case MODE_POSIX_SPAWN: | ||
|
@@ -765,9 +727,6 @@ Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env, | |
|
||
if (resultPid < 0) { | ||
switch (c->mode) { | ||
case MODE_VFORK: | ||
throwInternalIOException(env, errno, "vfork failed", c->mode); | ||
break; | ||
case MODE_FORK: | ||
throwInternalIOException(env, errno, "fork failed", c->mode); | ||
break; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -205,31 +205,6 @@ initVectorFromBlock(const char**vector, const char* block, int count) | |
vector[count] = NULL; | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This mode should be mentioned in the CSR, even if no-one knew it was there. |
||
/** | ||
* Exec FILE as a traditional Bourne shell script (i.e. one without #!). | ||
* If we could do it over again, we would probably not support such an ancient | ||
* misfeature, but compatibility wins over sanity. The original support for | ||
* this was imported accidentally from execvp(). | ||
*/ | ||
static void | ||
execve_as_traditional_shell_script(const char *file, | ||
const char *argv[], | ||
const char *const envp[]) | ||
{ | ||
/* Use the extra word of space provided for us in argv by caller. */ | ||
const char *argv0 = argv[0]; | ||
const char *const *end = argv; | ||
while (*end != NULL) | ||
++end; | ||
memmove(argv+2, argv+1, (end-argv) * sizeof(*end)); | ||
argv[0] = "/bin/sh"; | ||
argv[1] = file; | ||
execve(argv[0], (char **) argv, (char **) envp); | ||
/* Can't even exec /bin/sh? Big trouble, but let's soldier on... */ | ||
memmove(argv+1, argv+2, (end-argv) * sizeof(*end)); | ||
argv[0] = argv0; | ||
} | ||
|
||
/** | ||
* Like execve(2), except that in case of ENOEXEC, FILE is assumed to | ||
* be a shell script and the system default shell is invoked to run it. | ||
|
@@ -239,16 +214,9 @@ execve_with_shell_fallback(int mode, const char *file, | |
const char *argv[], | ||
const char *const envp[]) | ||
{ | ||
if (mode == MODE_VFORK) { | ||
/* shared address space; be very careful. */ | ||
execve(file, (char **) argv, (char **) envp); | ||
if (errno == ENOEXEC) | ||
execve_as_traditional_shell_script(file, argv, envp); | ||
} else { | ||
/* unshared address space; we can mutate environ. */ | ||
environ = (char **) envp; | ||
execvp(file, (char **) argv); | ||
} | ||
/* unshared address space; we can mutate environ. */ | ||
environ = (char **) envp; | ||
execvp(file, (char **) argv); | ||
} | ||
|
||
/** | ||
|
@@ -406,12 +374,10 @@ childProcess(void *arg) | |
if (p->pdir != NULL && chdir(p->pdir) < 0) | ||
goto WhyCantJohnnyExec; | ||
|
||
// Reset any mask signals from parent, but not in VFORK mode | ||
if (p->mode != MODE_VFORK) { | ||
sigset_t unblock_signals; | ||
sigemptyset(&unblock_signals); | ||
sigprocmask(SIG_SETMASK, &unblock_signals, NULL); | ||
} | ||
// Reset any mask signals from parent | ||
sigset_t unblock_signals; | ||
sigemptyset(&unblock_signals); | ||
sigprocmask(SIG_SETMASK, &unblock_signals, NULL); | ||
|
||
if (fcntl(FAIL_FILENO, F_SETFD, FD_CLOEXEC) == -1) | ||
goto WhyCantJohnnyExec; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would mention the two ways that are implemented, only mentioning vfork after the supported mechanisms to inhibit someone wanting to go backwards.