Hi,

I want to propose to use SOCK_CLOEXEC when opening sockets in the OpenJDK.  I 
ran into some issues when forking processes (in JNI/C/C++/native code) on Linux 
where OpenJDK had opened a socket (in Java code).  The child process ends up 
inheriting a file descriptor to the same socket, which is not ideal in certain 
circumstances (for example if the Java process restarts and tries to open the 
socket again while the child process is still running).  Of course, after 
forking the child process can close all those file descriptors as a workaround, 
but we use O_CLOEXEC when opening files, so I think it would be ideal to use 
the same for sockets.

Just some info about the patch:


1.       This is only for Linux (I don't believe SOCK_CLOEXEC exists on other 
platforms, I use a preprocessor guard for SOCK_CLOEXEC)

2.       I try twice if the first time attempting to open the socket fails with 
EINVAL because it is possible that the OpenJDK was compiled on a newer 
kernel/with newer headers that supports SOCK_CLOEXEC but runs on a lower 
version kernel (not sure if this is supported by the OpenJDK project)

Patch is attached below.  Let me know if you want me to make some changes.

(I did not add a unit test - it would probably need to be a functional test, 
one that involves a child process and forking, etc.  Let me know if you believe 
this is necessary to add)

Thanks,

-Andrew

diff -r 95c0644a1c47 src/java.base/unix/native/libnet/PlainSocketImpl.c
--- a/src/java.base/unix/native/libnet/PlainSocketImpl.c Fri Jun 15 17:34:01 
2018 -0700
+++ b/src/java.base/unix/native/libnet/PlainSocketImpl.c             Tue Jul 10 
01:30:08 2018 -0700
@@ -104,6 +104,34 @@
}
 /*
+ * Opens a socket
+ * On systems where supported, uses SOCK_CLOEXEC where possible
+ */
+static int openSocket(int domain, int type, int protocol) {
+#if defined(SOCK_CLOEXEC)
+    int typeToUse = type | SOCK_CLOEXEC;
+#else
+    int typeToUse = type;
+#endif
+
+    int socketFileDescriptor = socket(domain, typeToUse, protocol);
+#if defined(SOCK_CLOEXEC)
+    if ((socketFileDescriptor == -1) && (errno = EINVAL)) {
+        // Attempt to open the socket without SOCK_CLOEXEC
+        // May have been compiled on an OS with SOCK_CLOEXEC supported
+        // But runtime system might not have SOCK_CLOEXEC support
+        socketFileDescriptor = socket(domain, type, protocol);
+    }
+#else
+    // Best effort
+    // Return value is intentionally ignored since socket was successfully 
opened anyways
+    fcntl(socketFileDescriptor, F_SETFD, FD_CLOEXEC);
+#endif
+
+    return socketFileDescriptor;
+}
+
+/*
  * The initroto function is called whenever PlainSocketImpl is
  * loaded, to cache field IDs for efficiency. This is called every time
  * the Java class is loaded.
@@ -178,7 +206,8 @@
         return;
     }
-    if ((fd = socket(domain, type, 0)) == -1) {
+
+    if ((fd = openSocket(domain, type, 0)) == -1) {
         /* note: if you run out of fds, you may not be able to load
          * the exception class, and get a NoClassDefFoundError
          * instead.

Reply via email to