JDK-8210549 : Runtime.exec: in closeDescriptors(), use FD_CLOEXEC instead of close()
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 12
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2018-09-10
  • Updated: 2019-05-23
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdUnresolved
Related Reports
Relates :  
Description
After fork()/vfork() and before exec(), the child process needs to close all inherited file descriptors apart from the stdin/out/err pipe ends.

We do this by iterating thru all file descriptors in /proc/<pid>/fd or whatever the equivalent is on that platform. This is done using opendir(), readdir().

We then close all these file descriptors using close().

The problem with that technique is that we may accidentally close the file descriptor opendir() is using internally to read the directory content of /proc/<pid>/fd, thereby sawing off the branch we are sitting on. The coding does some magic to attempt to prevent this:

<quote>
86    /* We're trying to close all file descriptors, but opendir() might
87     * itself be implemented using a file descriptor, and we certainly
88     * don't want to close that while it's in use.  We assume that if
89     * opendir() is implemented using a file descriptor, then it uses
90     * the lowest numbered file descriptor, just like open().  So we
91     * close a couple explicitly.  */
92
93    close(from_fd);          /* for possible use by opendir() */
94    close(from_fd + 1);      /* another one for good luck */

...
108    while ((dirp = readdir64(dp)) != NULL) {
109        int fd;
110        if (isAsciiDigit(dirp->d_name[0]) &&
111            (fd = strtol(dirp->d_name, NULL, 10)) >= from_fd + 2)
112            close(fd);
113    }

</quote>

This workaround can be removed if, instead of outright closing the file descriptor in the loop, we were to set the file descriptor to FD_CLOEXEC. The file descriptor underlying the opendir()/readdir() call would continue to function. Closing all descriptors would be defered to the actual exec() call some milliseconds later.

For further information, please see discussion: http://mail.openjdk.java.net/pipermail/core-libs-dev/2018-September/055173.html

---

A second issue which was brought up is that there is no sufficient error handling in the readdir() loop: readdir() may fail, which would leave the rest of the file descriptors unclosed in the child process, leading to potential difficult file io errors further down the road. There was no error handling for this kind of error.


Comments
not that important right now; hence put on hold.
20-05-2019

An alternative, more conservative approach to make closeDescriptors() more robust is to extract dpfd = dirfd(dp) and then only close(fd) if (fd != dpfd). I think, it can be simply added to the current implementation.
02-03-2019