< 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 >