Path: blob/master/src/java.base/unix/native/libjava/ProcessImpl_md.c
41119 views
/*1* Copyright (c) 1995, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425#undef _LARGEFILE64_SOURCE26#define _LARGEFILE64_SOURCE 12728#include "jni.h"29#include "jvm.h"30#include "jvm_md.h"31#include "jni_util.h"32#include "io_util.h"3334/*35* Platform-specific support for java.lang.Process36*/37#include <assert.h>38#include <stddef.h>39#include <stdlib.h>40#include <sys/types.h>41#include <ctype.h>42#include <sys/wait.h>43#include <signal.h>44#include <string.h>4546#include <spawn.h>4748#include "childproc.h"4950/*51*52* When starting a child on Unix, we need to do three things:53* - fork off54* - in the child process, do some pre-exec work: duping/closing file55* descriptors to set up stdio-redirection, setting environment variables,56* changing paths...57* - then exec(2) the target binary58*59* There are three ways to fork off:60*61* A) fork(2). Portable and safe (no side effects) but may fail with ENOMEM on62* all Unices when invoked from a VM with a high memory footprint. On Unices63* with strict no-overcommit policy this problem is most visible.64*65* This is because forking the VM will first create a child process with66* theoretically the same memory footprint as the parent - even if you plan67* to follow up with exec'ing a tiny binary. In reality techniques like68* copy-on-write etc mitigate the problem somewhat but we still run the risk69* of hitting system limits.70*71* For a Linux centric description of this problem, see the documentation on72* /proc/sys/vm/overcommit_memory in Linux proc(5).73*74* B) vfork(2): Portable and fast but very unsafe. It bypasses the memory75* problems related to fork(2) by starting the child in the memory image of76* the parent. Things that can go wrong include:77* - Programming errors in the child process before the exec(2) call may78* trash memory in the parent process, most commonly the stack of the79* thread invoking vfork.80* - Signals received by the child before the exec(2) call may be at best81* misdirected to the parent, at worst immediately kill child and parent.82*83* This is mitigated by very strict rules about what one is allowed to do in84* the child process between vfork(2) and exec(2), which is basically nothing.85* However, we always broke this rule by doing the pre-exec work between86* vfork(2) and exec(2).87*88* Also note that vfork(2) has been deprecated by the OpenGroup, presumably89* because of its many dangers.90*91* C) clone(2): This is a Linux specific call which gives the caller fine92* grained control about how exactly the process fork is executed. It is93* powerful, but Linux-specific.94*95* Aside from these three possibilities there is a forth option: posix_spawn(3).96* Where fork/vfork/clone all fork off the process and leave pre-exec work and97* calling exec(2) to the user, posix_spawn(3) offers the user fork+exec-like98* functionality in one package, similar to CreateProcess() on Windows.99*100* It is not a system call in itself, but usually a wrapper implemented within101* the libc in terms of one of (fork|vfork|clone)+exec - so whether or not it102* has advantages over calling the naked (fork|vfork|clone) functions depends103* on how posix_spawn(3) is implemented.104*105* Note that when using posix_spawn(3), we exec twice: first a tiny binary called106* the jspawnhelper, then in the jspawnhelper we do the pre-exec work and exec a107* second time, this time the target binary (similar to the "exec-twice-technique"108* described in http://mail.openjdk.java.net/pipermail/core-libs-dev/2018-September/055333.html).109*110* This is a JDK-specific implementation detail which just happens to be111* implemented for jdk.lang.Process.launchMechanism=POSIX_SPAWN.112*113* --- Linux-specific ---114*115* How does glibc implement posix_spawn?116* (see: sysdeps/posix/spawni.c for glibc < 2.24,117* sysdeps/unix/sysv/linux/spawni.c for glibc >= 2.24):118*119* 1) Before glibc 2.4 (released 2006), posix_spawn(3) used just fork(2)/exec(2).120* This would be bad for the JDK since we would risk the known memory issues with121* fork(2). But since this only affects glibc variants which have long been122* phased out by modern distributions, this is irrelevant.123*124* 2) Between glibc 2.4 and glibc 2.23, posix_spawn uses either fork(2) or125* vfork(2) depending on how exactly the user called posix_spawn(3):126*127* <quote>128* The child process is created using vfork(2) instead of fork(2) when129* either of the following is true:130*131* * the spawn-flags element of the attributes object pointed to by132* attrp contains the GNU-specific flag POSIX_SPAWN_USEVFORK; or133*134* * file_actions is NULL and the spawn-flags element of the attributes135* object pointed to by attrp does not contain136* POSIX_SPAWN_SETSIGMASK, POSIX_SPAWN_SETSIGDEF,137* POSIX_SPAWN_SETSCHEDPARAM, POSIX_SPAWN_SETSCHEDULER,138* POSIX_SPAWN_SETPGROUP, or POSIX_SPAWN_RESETIDS.139* </quote>140*141* Due to the way the JDK calls posix_spawn(3), it would therefore call vfork(2).142* So we would avoid the fork(2) memory problems. However, there still remains the143* risk associated with vfork(2). But it is smaller than were we to call vfork(2)144* directly since we use the jspawnhelper, moving all pre-exec work off to after145* the first exec, thereby reducing the vulnerable time window.146*147* 3) Since glibc >= 2.24, glibc uses clone+exec:148*149* new_pid = CLONE (__spawni_child, STACK (stack, stack_size), stack_size,150* CLONE_VM | CLONE_VFORK | SIGCHLD, &args);151*152* This is even better than (2):153*154* CLONE_VM means we run in the parent's memory image, as with (2)155* CLONE_VFORK means parent waits until we exec, as with (2)156*157* However, error possibilities are further reduced since:158* - posix_spawn(3) passes a separate stack for the child to run on, eliminating159* the danger of trashing the forking thread's stack in the parent process.160* - posix_spawn(3) takes care to temporarily block all incoming signals to the161* child process until the first exec(2) has been called,162*163* TL;DR164* Calling posix_spawn(3) for glibc165* (2) < 2.24 is not perfect but still better than using plain vfork(2), since166* the chance of an error happening is greatly reduced167* (3) >= 2.24 is the best option - portable, fast and as safe as possible.168*169* ---170*171* How does muslc implement posix_spawn?172*173* They always did use the clone (.. CLONE_VM | CLONE_VFORK ...)174* technique. So we are safe to use posix_spawn() here regardless of muslc175* version.176*177* </Linux-specific>178*179*180* Based on the above analysis, we are currently defaulting to posix_spawn()181* on all Unices including Linux.182*/183184static void185setSIGCHLDHandler(JNIEnv *env)186{187/* There is a subtle difference between having the signal handler188* for SIGCHLD be SIG_DFL and SIG_IGN. We cannot obtain process189* termination information for child processes if the signal190* handler is SIG_IGN. It must be SIG_DFL.191*192* We used to set the SIGCHLD handler only on Linux, but it's193* safest to set it unconditionally.194*195* Consider what happens if java's parent process sets the SIGCHLD196* handler to SIG_IGN. Normally signal handlers are inherited by197* children, but SIGCHLD is a controversial case. Solaris appears198* to always reset it to SIG_DFL, but this behavior may be199* non-standard-compliant, and we shouldn't rely on it.200*201* References:202* http://www.opengroup.org/onlinepubs/7908799/xsh/exec.html203* http://www.pasc.org/interps/unofficial/db/p1003.1/pasc-1003.1-132.html204*/205struct sigaction sa;206sa.sa_handler = SIG_DFL;207sigemptyset(&sa.sa_mask);208sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;209if (sigaction(SIGCHLD, &sa, NULL) < 0)210JNU_ThrowInternalError(env, "Can't set SIGCHLD handler");211}212213static void*214xmalloc(JNIEnv *env, size_t size)215{216void *p = malloc(size);217if (p == NULL)218JNU_ThrowOutOfMemoryError(env, NULL);219return p;220}221222#define NEW(type, n) ((type *) xmalloc(env, (n) * sizeof(type)))223224/**225* If PATH is not defined, the OS provides some default value.226* Unfortunately, there's no portable way to get this value.227* Fortunately, it's only needed if the child has PATH while we do not.228*/229static const char*230defaultPath(void)231{232return ":/bin:/usr/bin";233}234235static const char*236effectivePath(void)237{238const char *s = getenv("PATH");239return (s != NULL) ? s : defaultPath();240}241242static int243countOccurrences(const char *s, char c)244{245int count;246for (count = 0; *s != '\0'; s++)247count += (*s == c);248return count;249}250251static const char * const *252effectivePathv(JNIEnv *env)253{254char *p;255int i;256const char *path = effectivePath();257int count = countOccurrences(path, ':') + 1;258size_t pathvsize = sizeof(const char *) * (count+1);259size_t pathsize = strlen(path) + 1;260const char **pathv = (const char **) xmalloc(env, pathvsize + pathsize);261262if (pathv == NULL)263return NULL;264p = (char *) pathv + pathvsize;265memcpy(p, path, pathsize);266/* split PATH by replacing ':' with NULs; empty components => "." */267for (i = 0; i < count; i++) {268char *q = p + strcspn(p, ":");269pathv[i] = (p == q) ? "." : p;270*q = '\0';271p = q + 1;272}273pathv[count] = NULL;274return pathv;275}276277JNIEXPORT void JNICALL278Java_java_lang_ProcessImpl_init(JNIEnv *env, jclass clazz)279{280parentPathv = effectivePathv(env);281CHECK_NULL(parentPathv);282setSIGCHLDHandler(env);283}284285286#ifndef WIFEXITED287#define WIFEXITED(status) (((status)&0xFF) == 0)288#endif289290#ifndef WEXITSTATUS291#define WEXITSTATUS(status) (((status)>>8)&0xFF)292#endif293294#ifndef WIFSIGNALED295#define WIFSIGNALED(status) (((status)&0xFF) > 0 && ((status)&0xFF00) == 0)296#endif297298#ifndef WTERMSIG299#define WTERMSIG(status) ((status)&0x7F)300#endif301302static const char *303getBytes(JNIEnv *env, jbyteArray arr)304{305return arr == NULL ? NULL :306(const char*) (*env)->GetByteArrayElements(env, arr, NULL);307}308309static void310releaseBytes(JNIEnv *env, jbyteArray arr, const char* parr)311{312if (parr != NULL)313(*env)->ReleaseByteArrayElements(env, arr, (jbyte*) parr, JNI_ABORT);314}315316#define IOE_FORMAT "error=%d, %s"317318static void319throwIOException(JNIEnv *env, int errnum, const char *defaultDetail)320{321const char *detail = defaultDetail;322char *errmsg;323size_t fmtsize;324char tmpbuf[1024];325jstring s;326327if (errnum != 0) {328int ret = getErrorString(errnum, tmpbuf, sizeof(tmpbuf));329if (ret != EINVAL)330detail = tmpbuf;331}332/* ASCII Decimal representation uses 2.4 times as many bits as binary. */333fmtsize = sizeof(IOE_FORMAT) + strlen(detail) + 3 * sizeof(errnum);334errmsg = NEW(char, fmtsize);335if (errmsg == NULL)336return;337338snprintf(errmsg, fmtsize, IOE_FORMAT, errnum, detail);339s = JNU_NewStringPlatform(env, errmsg);340if (s != NULL) {341jobject x = JNU_NewObjectByName(env, "java/io/IOException",342"(Ljava/lang/String;)V", s);343if (x != NULL)344(*env)->Throw(env, x);345}346free(errmsg);347}348349/**350* Throws an IOException with a message composed from the result of waitpid status.351*/352static void throwExitCause(JNIEnv *env, int pid, int status) {353char ebuf[128];354if (WIFEXITED(status)) {355snprintf(ebuf, sizeof ebuf,356"Failed to exec spawn helper: pid: %d, exit value: %d",357pid, WEXITSTATUS(status));358} else if (WIFSIGNALED(status)) {359snprintf(ebuf, sizeof ebuf,360"Failed to exec spawn helper: pid: %d, signal: %d",361pid, WTERMSIG(status));362} else {363snprintf(ebuf, sizeof ebuf,364"Failed to exec spawn helper: pid: %d, status: 0x%08x",365pid, status);366}367throwIOException(env, 0, ebuf);368}369370#ifdef DEBUG_PROCESS371/* Debugging process code is difficult; where to write debug output? */372static void373debugPrint(char *format, ...)374{375FILE *tty = fopen("/dev/tty", "w");376va_list ap;377va_start(ap, format);378vfprintf(tty, format, ap);379va_end(ap);380fclose(tty);381}382#endif /* DEBUG_PROCESS */383384static void385copyPipe(int from[2], int to[2])386{387to[0] = from[0];388to[1] = from[1];389}390391/* arg is an array of pointers to 0 terminated strings. array is terminated392* by a null element.393*394* *nelems and *nbytes receive the number of elements of array (incl 0)395* and total number of bytes (incl. 0)396* Note. An empty array will have one null element397* But if arg is null, then *nelems set to 0, and *nbytes to 0398*/399static void arraysize(const char * const *arg, int *nelems, int *nbytes)400{401int i, bytes, count;402const char * const *a = arg;403char *p;404int *q;405if (arg == 0) {406*nelems = 0;407*nbytes = 0;408return;409}410/* count the array elements and number of bytes */411for (count=0, bytes=0; *a != 0; count++, a++) {412bytes += strlen(*a)+1;413}414*nbytes = bytes;415*nelems = count+1;416}417418/* copy the strings from arg[] into buf, starting at given offset419* return new offset to next free byte420*/421static int copystrings(char *buf, int offset, const char * const *arg) {422char *p;423const char * const *a;424int count=0;425426if (arg == 0) {427return offset;428}429for (p=buf+offset, a=arg; *a != 0; a++) {430int len = strlen(*a) +1;431memcpy(p, *a, len);432p += len;433count += len;434}435return offset+count;436}437438/**439* We are unusually paranoid; use of vfork is440* especially likely to tickle gcc/glibc bugs.441*/442#ifdef __attribute_noinline__ /* See: sys/cdefs.h */443__attribute_noinline__444#endif445446/* vfork(2) is deprecated on Solaris */447static pid_t448vforkChild(ChildStuff *c) {449volatile pid_t resultPid;450451/*452* We separate the call to vfork into a separate function to make453* very sure to keep stack of child from corrupting stack of parent,454* as suggested by the scary gcc warning:455* warning: variable 'foo' might be clobbered by 'longjmp' or 'vfork'456*/457resultPid = vfork();458459if (resultPid == 0) {460childProcess(c);461}462assert(resultPid != 0); /* childProcess never returns */463return resultPid;464}465466static pid_t467forkChild(ChildStuff *c) {468pid_t resultPid;469470/*471* From Solaris fork(2): In Solaris 10, a call to fork() is472* identical to a call to fork1(); only the calling thread is473* replicated in the child process. This is the POSIX-specified474* behavior for fork().475*/476resultPid = fork();477478if (resultPid == 0) {479childProcess(c);480}481assert(resultPid != 0); /* childProcess never returns */482return resultPid;483}484485static pid_t486spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) {487pid_t resultPid;488jboolean isCopy;489int i, offset, rval, bufsize, magic;490char *buf, buf1[16];491char *hlpargs[2];492SpawnInfo sp;493494/* need to tell helper which fd is for receiving the childstuff495* and which fd to send response back on496*/497snprintf(buf1, sizeof(buf1), "%d:%d", c->childenv[0], c->fail[1]);498/* put the fd string as argument to the helper cmd */499hlpargs[0] = buf1;500hlpargs[1] = 0;501502/* Following items are sent down the pipe to the helper503* after it is spawned.504* All strings are null terminated. All arrays of strings505* have an empty string for termination.506* - the ChildStuff struct507* - the SpawnInfo struct508* - the argv strings array509* - the envv strings array510* - the home directory string511* - the parentPath string512* - the parentPathv array513*/514/* First calculate the sizes */515arraysize(c->argv, &sp.nargv, &sp.argvBytes);516bufsize = sp.argvBytes;517arraysize(c->envv, &sp.nenvv, &sp.envvBytes);518bufsize += sp.envvBytes;519sp.dirlen = c->pdir == 0 ? 0 : strlen(c->pdir)+1;520bufsize += sp.dirlen;521arraysize(parentPathv, &sp.nparentPathv, &sp.parentPathvBytes);522bufsize += sp.parentPathvBytes;523/* We need to clear FD_CLOEXEC if set in the fds[].524* Files are created FD_CLOEXEC in Java.525* Otherwise, they will be closed when the target gets exec'd */526for (i=0; i<3; i++) {527if (c->fds[i] != -1) {528int flags = fcntl(c->fds[i], F_GETFD);529if (flags & FD_CLOEXEC) {530fcntl(c->fds[i], F_SETFD, flags & (~1));531}532}533}534535rval = posix_spawn(&resultPid, helperpath, 0, 0, (char * const *) hlpargs, environ);536537if (rval != 0) {538return -1;539}540541/* now the lengths are known, copy the data */542buf = NEW(char, bufsize);543if (buf == 0) {544return -1;545}546offset = copystrings(buf, 0, &c->argv[0]);547offset = copystrings(buf, offset, &c->envv[0]);548memcpy(buf+offset, c->pdir, sp.dirlen);549offset += sp.dirlen;550offset = copystrings(buf, offset, parentPathv);551assert(offset == bufsize);552553magic = magicNumber();554555/* write the two structs and the data buffer */556write(c->childenv[1], (char *)&magic, sizeof(magic)); // magic number first557write(c->childenv[1], (char *)c, sizeof(*c));558write(c->childenv[1], (char *)&sp, sizeof(sp));559write(c->childenv[1], buf, bufsize);560free(buf);561562/* In this mode an external main() in invoked which calls back into563* childProcess() in this file, rather than directly564* via the statement below */565return resultPid;566}567568/*569* Start a child process running function childProcess.570* This function only returns in the parent.571*/572static pid_t573startChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) {574switch (c->mode) {575/* vfork(2) is deprecated on Solaris */576case MODE_VFORK:577return vforkChild(c);578case MODE_FORK:579return forkChild(c);580case MODE_POSIX_SPAWN:581return spawnChild(env, process, c, helperpath);582default:583return -1;584}585}586587JNIEXPORT jint JNICALL588Java_java_lang_ProcessImpl_forkAndExec(JNIEnv *env,589jobject process,590jint mode,591jbyteArray helperpath,592jbyteArray prog,593jbyteArray argBlock, jint argc,594jbyteArray envBlock, jint envc,595jbyteArray dir,596jintArray std_fds,597jboolean redirectErrorStream)598{599int errnum;600int resultPid = -1;601int in[2], out[2], err[2], fail[2], childenv[2];602jint *fds = NULL;603const char *phelperpath = NULL;604const char *pprog = NULL;605const char *pargBlock = NULL;606const char *penvBlock = NULL;607ChildStuff *c;608609in[0] = in[1] = out[0] = out[1] = err[0] = err[1] = fail[0] = fail[1] = -1;610childenv[0] = childenv[1] = -1;611612if ((c = NEW(ChildStuff, 1)) == NULL) return -1;613c->argv = NULL;614c->envv = NULL;615c->pdir = NULL;616617/* Convert prog + argBlock into a char ** argv.618* Add one word room for expansion of argv for use by619* execve_as_traditional_shell_script.620* This word is also used when using posix_spawn mode621*/622assert(prog != NULL && argBlock != NULL);623if ((phelperpath = getBytes(env, helperpath)) == NULL) goto Catch;624if ((pprog = getBytes(env, prog)) == NULL) goto Catch;625if ((pargBlock = getBytes(env, argBlock)) == NULL) goto Catch;626if ((c->argv = NEW(const char *, argc + 3)) == NULL) goto Catch;627c->argv[0] = pprog;628c->argc = argc + 2;629initVectorFromBlock(c->argv+1, pargBlock, argc);630631if (envBlock != NULL) {632/* Convert envBlock into a char ** envv */633if ((penvBlock = getBytes(env, envBlock)) == NULL) goto Catch;634if ((c->envv = NEW(const char *, envc + 1)) == NULL) goto Catch;635initVectorFromBlock(c->envv, penvBlock, envc);636}637638if (dir != NULL) {639if ((c->pdir = getBytes(env, dir)) == NULL) goto Catch;640}641642assert(std_fds != NULL);643fds = (*env)->GetIntArrayElements(env, std_fds, NULL);644if (fds == NULL) goto Catch;645646if ((fds[0] == -1 && pipe(in) < 0) ||647(fds[1] == -1 && pipe(out) < 0) ||648(fds[2] == -1 && pipe(err) < 0) ||649(pipe(childenv) < 0) ||650(pipe(fail) < 0)) {651throwIOException(env, errno, "Bad file descriptor");652goto Catch;653}654c->fds[0] = fds[0];655c->fds[1] = fds[1];656c->fds[2] = fds[2];657658copyPipe(in, c->in);659copyPipe(out, c->out);660copyPipe(err, c->err);661copyPipe(fail, c->fail);662copyPipe(childenv, c->childenv);663664c->redirectErrorStream = redirectErrorStream;665c->mode = mode;666667/* In posix_spawn mode, require the child process to signal aliveness668* right after it comes up. This is because there are implementations of669* posix_spawn() which do not report failed exec()s back to the caller670* (e.g. glibc, see JDK-8223777). In those cases, the fork() will have671* worked and successfully started the child process, but the exec() will672* have failed. There is no way for us to distinguish this from a target673* binary just exiting right after start.674*675* Note that we could do this additional handshake in all modes but for676* prudence only do it when it is needed (in posix_spawn mode). */677c->sendAlivePing = (mode == MODE_POSIX_SPAWN) ? 1 : 0;678679resultPid = startChild(env, process, c, phelperpath);680assert(resultPid != 0);681682if (resultPid < 0) {683switch (c->mode) {684case MODE_VFORK:685throwIOException(env, errno, "vfork failed");686break;687case MODE_FORK:688throwIOException(env, errno, "fork failed");689break;690case MODE_POSIX_SPAWN:691throwIOException(env, errno, "posix_spawn failed");692break;693}694goto Catch;695}696close(fail[1]); fail[1] = -1; /* See: WhyCantJohnnyExec (childproc.c) */697698/* If we expect the child to ping aliveness, wait for it. */699if (c->sendAlivePing) {700switch(readFully(fail[0], &errnum, sizeof(errnum))) {701case 0: /* First exec failed; */702{703int tmpStatus = 0;704int p = waitpid(resultPid, &tmpStatus, 0);705throwExitCause(env, p, tmpStatus);706goto Catch;707}708case sizeof(errnum):709assert(errnum == CHILD_IS_ALIVE);710if (errnum != CHILD_IS_ALIVE) {711/* Should never happen since the first thing the spawn712* helper should do is to send an alive ping to the parent,713* before doing any subsequent work. */714throwIOException(env, 0, "Bad code from spawn helper "715"(Failed to exec spawn helper)");716goto Catch;717}718break;719default:720throwIOException(env, errno, "Read failed");721goto Catch;722}723}724725switch (readFully(fail[0], &errnum, sizeof(errnum))) {726case 0: break; /* Exec succeeded */727case sizeof(errnum):728waitpid(resultPid, NULL, 0);729throwIOException(env, errnum, "Exec failed");730goto Catch;731default:732throwIOException(env, errno, "Read failed");733goto Catch;734}735736fds[0] = (in [1] != -1) ? in [1] : -1;737fds[1] = (out[0] != -1) ? out[0] : -1;738fds[2] = (err[0] != -1) ? err[0] : -1;739740Finally:741/* Always clean up the child's side of the pipes */742closeSafely(in [0]);743closeSafely(out[1]);744closeSafely(err[1]);745746/* Always clean up fail and childEnv descriptors */747closeSafely(fail[0]);748closeSafely(fail[1]);749closeSafely(childenv[0]);750closeSafely(childenv[1]);751752releaseBytes(env, helperpath, phelperpath);753releaseBytes(env, prog, pprog);754releaseBytes(env, argBlock, pargBlock);755releaseBytes(env, envBlock, penvBlock);756releaseBytes(env, dir, c->pdir);757758free(c->argv);759free(c->envv);760free(c);761762if (fds != NULL)763(*env)->ReleaseIntArrayElements(env, std_fds, fds, 0);764765return resultPid;766767Catch:768/* Clean up the parent's side of the pipes in case of failure only */769closeSafely(in [1]); in[1] = -1;770closeSafely(out[0]); out[0] = -1;771closeSafely(err[0]); err[0] = -1;772goto Finally;773}774775776777