On Fri, 12 May 2023 15:24:19 GMT, Volker Simonis <simo...@openjdk.org> wrote:

> Since JDK13, executing commands in a sub-process defaults to the so called 
> `POSIX_SPAWN` launching mechanism (i.e. 
> `-Djdk.lang.Process.launchMechanism=POSIX_SPAWN`) on Linux. This works by 
> using `posix_spawn(3)` to firstly start a tiny helper program called 
> `jspawnhelper` in a subprocess. In a second step, `jspawnhelper` reads the 
> command data from the parent Java process over a Unix pipe and finally 
> executes (i.e. `execvp(3)`) the requested command.
> 
> In cases where the parent process terminates abnormally before `jspawnhelper` 
> has read all the expected data from the pipe, `jspawnhelper` will block 
> indefinitely on the reading end of the pipe. This is especially harmful if 
> the parent process had open sockets, because in that case, the forked 
> `jspawnhelper` process will inherit them and keep all the corresponding ports 
> open effectively preventing other processes to bind to them. Notice that this 
> is not an abstract scenario. We've observed this regularly in production with 
> services which couldn't be restarted after a crash after migrating to JDK 17.
> 
> The fix of the issue is rather trivial. `jspawnhelper` has to close its 
> writing end of the pipe which connects it with the parent Java process 
> *before* starting to read from that pipe such that reads from the pipe can 
> immediately return with EOF if the parent process terminates abnormally.
> 
> Also did some cleanup:
>  - in `jspawnhelper.c` correctly chek the result of `readFully()`
>  - in `ProcessImpl_md.c` use `restartableWrite()` instead of `write()` to 
> write the command data from the parent process to `jspawnhelper`
>  - in `childproc.{c,h}` remove remaining traces of `MODE_CLONE` because 
> that's long gone.

Trying to understand this, and the scope of the problem:

- Parent process opens "in" pipe. 
- Now owns read and write end of pipe. 
- Parent forks jspawnhelper. 
- jspawnhelper inherits handles. Now there are four open file descriptors to 
this pipe.
- Parent dies and releases its handles
- jspawnhelper gets thrown out of its first "readFully", but enters the next 
"readFully". It still owns both read and write end of the pipe. So it will wait 
endlessly. We only get an EOF from a pipe read when all write ends are closed.

Is this the problem?

I wonder whether the parent may not have the same problem at the other end. It 
may be prudent to close the child's ends of the three pipes right after calling 
startChild() 
(https://github.com/openjdk/jdk/blob/020a60e6449749ca979c1ace3af7db57f4c53a5f/src/java.base/unix/native/libjava/ProcessImpl_md.c#LL681C17-L681C27).

-------------

PR Comment: https://git.openjdk.org/jdk/pull/13956#issuecomment-1546954075

Reply via email to