8211842: IPv6_supported wrongly returns false when unix domain socket is bound to fd 0
Reviewed-by: chegar, alanb
--- a/make/test/JtregNativeJdk.gmk Fri Nov 30 10:37:48 2018 +0530
+++ b/make/test/JtregNativeJdk.gmk Fri Nov 30 10:29:58 2018 +0000
@@ -60,7 +60,7 @@
ifeq ($(OPENJDK_TARGET_OS), linux)
BUILD_JDK_JTREG_LIBRARIES_LIBS_libInheritedChannel := -ljava
else ifeq ($(OPENJDK_TARGET_OS), solaris)
- BUILD_JDK_JTREG_LIBRARIES_LIBS_libInheritedChannel := -ljava
+ BUILD_JDK_JTREG_LIBRARIES_LIBS_libInheritedChannel := -ljava -lsocket -lnsl
endif
endif
--- a/src/java.base/unix/native/libnet/net_util_md.c Fri Nov 30 10:37:48 2018 +0530
+++ b/src/java.base/unix/native/libnet/net_util_md.c Fri Nov 30 10:29:58 2018 +0000
@@ -305,12 +305,12 @@
}
/*
- * If fd 0 is a socket it means we've been launched from inetd or
+ * If fd 0 is a socket it means we may have been launched from inetd or
* xinetd. If it's a socket then check the family - if it's an
* IPv4 socket then we need to disable IPv6.
*/
if (getsockname(0, &sa.sa, &sa_len) == 0) {
- if (sa.sa.sa_family != AF_INET6) {
+ if (sa.sa.sa_family == AF_INET) {
close(fd);
return JNI_FALSE;
}
--- a/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/InheritedChannelTest.java Fri Nov 30 10:37:48 2018 +0530
+++ b/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/InheritedChannelTest.java Fri Nov 30 10:29:58 2018 +0000
@@ -23,7 +23,7 @@
/*
* @test
- * @bug 4673940 4930794
+ * @bug 4673940 4930794 8211842
* @summary Unit tests for inetd feature
* @requires (os.family == "linux" | os.family == "solaris")
* @library /test/lib
@@ -33,7 +33,7 @@
* jdk.test.lib.JDKToolLauncher
* jdk.test.lib.Platform
* jdk.test.lib.process.*
- * StateTest StateTestService EchoTest EchoService CloseTest Launcher Util
+ * UnixSocketTest StateTest StateTestService EchoTest EchoService CloseTest Launcher Util
* @run testng/othervm/native InheritedChannelTest
* @key intermittent
*/
@@ -73,6 +73,7 @@
@DataProvider
public Object[][] testCases() {
return new Object[][]{
+ { "UnixSocketTest", List.of(UnixSocketTest.class.getName())},
{ "StateTest", List.of(StateTest.class.getName()) },
{ "EchoTest", List.of(EchoTest.class.getName()) },
{ "CloseTest", List.of(CloseTest.class.getName()) },
--- a/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/Launcher.java Fri Nov 30 10:37:48 2018 +0530
+++ b/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/Launcher.java Fri Nov 30 10:29:58 2018 +0000
@@ -62,6 +62,19 @@
launch0(cmdarray, fd);
}
+
+ /**
+ * Launch 'java' with specified class using a UnixDomainSocket pair linking calling
+ * process to the child VM. UnixDomainSocket is a simplified interface to PF_UNIX sockets
+ * which supports byte a time reads and writes.
+ */
+ public static UnixDomainSocket launchWithUnixDomainSocket(String className) throws IOException {
+ UnixDomainSocket[] socks = UnixDomainSocket.socketpair();
+ launch(className, null, null, socks[0].fd());
+ socks[0].close();
+ return socks[1];
+ }
+
/*
* Launch 'java' with specified class with the specified arguments (may be null).
* The launched process will inherit a connected TCP socket. The remote endpoint
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/UnixDomainSocket.java Fri Nov 30 10:29:58 2018 +0000
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * A simplified Unix domain socket which can read and write bytes at a time
+ * used for simulating external launchers which use UNIX sockets to talk
+ * the VM.
+ */
+
+import java.io.IOException;
+
+public class UnixDomainSocket {
+
+ static {
+ System.loadLibrary("InheritedChannel");
+ init();
+ }
+
+ private final int fd;
+
+ public UnixDomainSocket(int fd) {
+ this.fd = fd;
+ }
+
+ public int read() throws IOException {
+ return read0(fd);
+ }
+
+ public void write(int w) throws IOException {
+ write0(fd, w);
+ }
+
+ public void close() {
+ close0(fd);
+ }
+
+ public int fd() {
+ return fd;
+ }
+
+ public String toString() {
+ return "UnixDomainSocket: fd=" + Integer.toString(fd);
+ }
+
+ /* read and write bytes with UNIX domain sockets */
+
+ private static native int read0(int fd) throws IOException;
+ private static native void write0(int fd, int w) throws IOException;
+ private static native void close0(int fd);
+ private static native void init();
+ public static native UnixDomainSocket[] socketpair();
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/UnixSocketTest.java Fri Nov 30 10:29:58 2018 +0000
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * If the platform has IPv6 we spawn a child process simulating the
+ * effect of being launched from node.js. We check that IPv6 is available in the child
+ * and report back as appropriate.
+ */
+
+import jdk.test.lib.Utils;
+import java.io.*;
+import java.net.InetAddress;
+import java.net.Inet6Address;
+import java.net.NetworkInterface;
+import java.util.Collections;
+import java.util.Enumeration;
+
+public class UnixSocketTest {
+
+ static boolean hasIPv6() throws Exception {
+ Enumeration<NetworkInterface> nets = NetworkInterface.getNetworkInterfaces();
+ for (NetworkInterface netint : Collections.list(nets)) {
+ Enumeration<InetAddress> inetAddresses = netint.getInetAddresses();
+ for (InetAddress inetAddress : Collections.list(inetAddresses)) {
+ if (inetAddress instanceof Inet6Address) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public static class Child {
+ public static void main(String[] args) throws Exception {
+ System.out.write('X');
+ System.out.flush();
+ if (hasIPv6()) {
+ System.out.println("Y"); // GOOD
+ } else
+ System.out.println("N"); // BAD
+ }
+ }
+
+ public static void main(String args[]) throws Exception {
+
+ if (!hasIPv6()) {
+ return; // can only test if IPv6 is present
+ }
+ UnixDomainSocket sock = Launcher.launchWithUnixDomainSocket("UnixSocketTest$Child");
+ if (sock.read() != 'X') {
+ System.exit(-2);
+ }
+ if (sock.read() != 'Y') {
+ System.exit(-2);
+ }
+ }
+}
--- a/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/libInheritedChannel.c Fri Nov 30 10:37:48 2018 +0530
+++ b/test/jdk/java/nio/channels/spi/SelectorProvider/inheritedChannel/libInheritedChannel.c Fri Nov 30 10:29:58 2018 +0000
@@ -36,6 +36,11 @@
#include "jni.h"
+#define CHECK(X) if ((X) == 0) {printf("JNI init error line %d\n", __LINE__); _exit(1);}
+
+static jclass unixSocketClass;
+static jmethodID unixSocketCtor;
+
/*
* Throws the exception of the given class name and detail message
*/
@@ -182,3 +187,63 @@
execvp(cmdv[0], cmdv);
_exit(-1);
}
+
+JNIEXPORT void JNICALL Java_UnixDomainSocket_init(JNIEnv *env, jclass cls) {
+ CHECK(unixSocketClass = (*env)->FindClass(env, "UnixDomainSocket"));
+ CHECK(unixSocketClass = (*env)->NewGlobalRef(env, unixSocketClass));
+ CHECK(unixSocketCtor = (*env)->GetMethodID(env, unixSocketClass, "<init>", "(I)V"));
+}
+
+/*
+ * Class: UnixDomainSocket
+ * Method: socketpair
+ * Signature: ()[LUnixDomainSocket
+ */
+JNIEXPORT jobjectArray JNICALL Java_UnixDomainSocket_socketpair
+ (JNIEnv *env, jclass cls)
+{
+ int fds[2];
+ jobject socket;
+ jobjectArray result = (*env)->NewObjectArray(env, 2, unixSocketClass, 0);
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0) {
+ perror("socketpair");
+ return result;
+ }
+ socket = (*env)->NewObject(env, unixSocketClass, unixSocketCtor, fds[0]);
+ (*env)->SetObjectArrayElement(env, result, 0, socket);
+ socket = (*env)->NewObject(env, unixSocketClass, unixSocketCtor, fds[1]);
+ (*env)->SetObjectArrayElement(env, result, 1, socket);
+ return result;
+}
+
+JNIEXPORT jint JNICALL Java_UnixDomainSocket_read0
+ (JNIEnv *env, jclass cls, jint fd)
+{
+ int ret;
+ unsigned char res;
+ ret = read(fd, &res, 1);
+ if (ret == 0)
+ return -1; /* EOF */
+ else if (ret < 0) {
+ ThrowException(env, "java/io/IOException", "read error");
+ return -1;
+ }
+ return res;
+}
+
+JNIEXPORT void JNICALL Java_UnixDomainSocket_write0
+ (JNIEnv *env, jclass cls, jint fd, jint byte)
+{
+ int ret;
+ unsigned char w = (unsigned char)byte;
+ ret = write(fd, &w, 1);
+ if (ret < 0) {
+ ThrowException(env, "java/io/IOException", "write error");
+ }
+}
+
+JNIEXPORT void JNICALL Java_UnixDomainSocket_close0
+ (JNIEnv *env, jclass cls, jint fd)
+{
+ close(fd);
+}