< prev index next >

src/java.base/unix/native/libjava/ProcessImpl_md.c

Print this page

        

*** 323,333 **** } #define IOE_FORMAT "error=%d, %s" static void ! throwIOException(JNIEnv *env, int errnum, const char *defaultDetail) { const char *detail = defaultDetail; char *errmsg; size_t fmtsize; char tmpbuf[1024]; --- 323,333 ---- } #define IOE_FORMAT "error=%d, %s" static void ! throwIOException(JNIEnv *env, int errnum, const char *defaultDetail, const char* addinfo) { const char *detail = defaultDetail; char *errmsg; size_t fmtsize; char tmpbuf[1024];
*** 337,352 **** int ret = getErrorString(errnum, tmpbuf, sizeof(tmpbuf)); if (ret != EINVAL) detail = tmpbuf; } /* ASCII Decimal representation uses 2.4 times as many bits as binary. */ ! fmtsize = sizeof(IOE_FORMAT) + strlen(detail) + 3 * sizeof(errnum); errmsg = NEW(char, fmtsize); if (errmsg == NULL) return; snprintf(errmsg, fmtsize, IOE_FORMAT, errnum, detail); s = JNU_NewStringPlatform(env, errmsg); if (s != NULL) { jobject x = JNU_NewObjectByName(env, "java/io/IOException", "(Ljava/lang/String;)V", s); if (x != NULL) --- 337,359 ---- int ret = getErrorString(errnum, tmpbuf, sizeof(tmpbuf)); if (ret != EINVAL) detail = tmpbuf; } /* ASCII Decimal representation uses 2.4 times as many bits as binary. */ ! fmtsize = sizeof(IOE_FORMAT) + strlen(detail) + 3 * sizeof(errnum) + ! (addinfo == NULL ? 0 : 1 + strlen(addinfo) + 2); // " (<info>)" errmsg = NEW(char, fmtsize); if (errmsg == NULL) return; snprintf(errmsg, fmtsize, IOE_FORMAT, errnum, detail); + if (addinfo != NULL) { + strcat(errmsg, " ("); + strcat(errmsg, addinfo); + strcat(errmsg, ")"); + } + s = JNU_NewStringPlatform(env, errmsg); if (s != NULL) { jobject x = JNU_NewObjectByName(env, "java/io/IOException", "(Ljava/lang/String;)V", s); if (x != NULL)
*** 470,517 **** } assert(resultPid != 0); /* childProcess never returns */ return resultPid; } ! /* Returns 1 if spawnhelper executable bits are set as expected. */ ! static int check_helper(const char* helperpath) { struct stat s; int rc = stat(helperpath, &s); if (rc == 0) { ! /* Require all bits set since this is how jspawnhelper ! * is set up in a canonical installation */ ! if (s.st_mode & S_IXUSR && ! s.st_mode & S_IXGRP && s.st_mode & S_IXOTH) { ! return 1; ! } else { return 0; } - } else { - return 0; } } static pid_t spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) { ! pid_t resultPid; jboolean isCopy; int i, offset, rval, bufsize, magic; char *buf, buf1[16]; char *hlpargs[2]; SpawnInfo sp; /* Some posix_spawn() implementations do not report exec() errors back to the caller; * POSIX leaves that up to the implementor, it only requires the child process to fail with * error code 127. ! * Since we cannot use that information (the payload binary may also use exit code 127 for ! * its own purposes), lets do at least a basic guess if exec(jspawnhelper) will succeed. ! * There are many reasons why it could fail, most of which one cannot be tested without ! * exec'ing, but we can at least check permissions. Since jspawnhelper is part of the JDK ! * it should have the execute permissions set correctly. */ ! if (check_helper(helperpath) == 0) { ! throwIOException(env, 0, "jspawnhelper is not accessible."); return -1; } /* need to tell helper which fd is for receiving the childstuff * and which fd to send response back on --- 477,544 ---- } assert(resultPid != 0); /* childProcess never returns */ return resultPid; } ! /* Returns 0 if at least one executable bit is set on the spawn helper binary, ! * -1 otherwise. */ ! static int isHelperExecutable(const char* helperpath) { ! /* Note that this check will never be sufficient; exec() may still fail ! * for many reasons and it is impossible to predict them all. The point of ! * this test is to catch the most common failure reason and give the caller ! * a clear diagnostic message. ! * But never shall this test give us false positives (make us avoid exec()ing ! * where it would have actually worked.) So since exec() success cannot be ! * completely predicted, we err on the side of false negatives - when in doubt, ! * try it. ! */ ! static int cached_rc = -2; ! if (cached_rc == 0 || cached_rc == -1) { ! /* The helper binary is part of the JDK and should have been set up ! * correctly. This is very unlikely to happen intermittently. Therefore ! * it should be okay to cache the result. */ ! return cached_rc; ! } ! struct stat s; int rc = stat(helperpath, &s); if (rc == 0) { ! /* Require at least one exec bit set since this is how the spawn helper ! * is set up in a canonical installation. */ ! if (s.st_mode & S_IXUSR || ! s.st_mode & S_IXGRP || s.st_mode & S_IXOTH) { ! cached_rc = 0; return 0; } } + cached_rc = -1; + return -1; } static pid_t spawnChild(JNIEnv *env, jobject process, ChildStuff *c, const char *helperpath) { ! pid_t resultPid = -1; jboolean isCopy; int i, offset, rval, bufsize, magic; char *buf, buf1[16]; char *hlpargs[2]; SpawnInfo sp; /* Some posix_spawn() implementations do not report exec() errors back to the caller; * POSIX leaves that up to the implementor, it only requires the child process to fail with * error code 127. ! * The problem with that is that posix_spawn() will seemingly succeed, child process will ! * have been started but will immediately die with exit code 127. This is impossible to ! * tell apart from cases where we successfully exec()'d the target program and it died ! * with 127. In the former case we want to throw an IOException, the latter case is fine ! * and should be handled by the java program. ! * There is no perfect solution, but here we try - before starting the child - to catch ! * obvious cases which will make the first exec() fail. */ ! if (isHelperExecutable(helperpath) != 0) { ! errno = EACCES; ! c->error_detail = "No execute permission for spawn helper."; return -1; } /* need to tell helper which fd is for receiving the childstuff * and which fd to send response back on
*** 670,680 **** if ((fds[0] == -1 && pipe(in) < 0) || (fds[1] == -1 && pipe(out) < 0) || (fds[2] == -1 && pipe(err) < 0) || (pipe(childenv) < 0) || (pipe(fail) < 0)) { ! throwIOException(env, errno, "Bad file descriptor"); goto Catch; } c->fds[0] = fds[0]; c->fds[1] = fds[1]; c->fds[2] = fds[2]; --- 697,707 ---- if ((fds[0] == -1 && pipe(in) < 0) || (fds[1] == -1 && pipe(out) < 0) || (fds[2] == -1 && pipe(err) < 0) || (pipe(childenv) < 0) || (pipe(fail) < 0)) { ! throwIOException(env, errno, "Bad file descriptor", NULL); goto Catch; } c->fds[0] = fds[0]; c->fds[1] = fds[1]; c->fds[2] = fds[2];
*** 685,725 **** copyPipe(fail, c->fail); copyPipe(childenv, c->childenv); c->redirectErrorStream = redirectErrorStream; c->mode = mode; resultPid = startChild(env, process, c, phelperpath); assert(resultPid != 0); if (resultPid < 0) { - /* Throw an IOException with a generic text unless an exception was already raised. */ - if (!(*env)->ExceptionCheck(env)) { switch (c->mode) { case MODE_VFORK: ! throwIOException(env, errno, "vfork failed"); break; case MODE_FORK: ! throwIOException(env, errno, "fork failed"); break; case MODE_POSIX_SPAWN: ! throwIOException(env, errno, "posix_spawn failed"); break; } - } goto Catch; } close(fail[1]); fail[1] = -1; /* See: WhyCantJohnnyExec (childproc.c) */ switch (readFully(fail[0], &errnum, sizeof(errnum))) { case 0: break; /* Exec succeeded */ case sizeof(errnum): waitpid(resultPid, NULL, 0); ! throwIOException(env, errnum, "Exec failed"); goto Catch; default: ! throwIOException(env, errno, "Read failed"); goto Catch; } fds[0] = (in [1] != -1) ? in [1] : -1; fds[1] = (out[0] != -1) ? out[0] : -1; --- 712,750 ---- copyPipe(fail, c->fail); copyPipe(childenv, c->childenv); c->redirectErrorStream = redirectErrorStream; c->mode = mode; + c->error_detail = NULL; resultPid = startChild(env, process, c, phelperpath); assert(resultPid != 0); if (resultPid < 0) { switch (c->mode) { case MODE_VFORK: ! throwIOException(env, errno, "vfork failed", c->error_detail); break; case MODE_FORK: ! throwIOException(env, errno, "fork failed", c->error_detail); break; case MODE_POSIX_SPAWN: ! throwIOException(env, errno, "posix_spawn failed", c->error_detail); break; } goto Catch; } close(fail[1]); fail[1] = -1; /* See: WhyCantJohnnyExec (childproc.c) */ switch (readFully(fail[0], &errnum, sizeof(errnum))) { case 0: break; /* Exec succeeded */ case sizeof(errnum): waitpid(resultPid, NULL, 0); ! throwIOException(env, errnum, "Exec failed", NULL); goto Catch; default: ! throwIOException(env, errno, "Read failed", NULL); goto Catch; } fds[0] = (in [1] != -1) ? in [1] : -1; fds[1] = (out[0] != -1) ? out[0] : -1;
< prev index next >