8211842: IPv6_supported wrongly returns false when unix domain socket is bound to fd 0

Reviewed-by: chegar, alanb
This commit is contained in:
Michael McMahon 2018-11-30 10:29:58 +00:00
parent 10d83827bc
commit 4a77a08ca3
7 changed files with 234 additions and 5 deletions

View File

@ -60,7 +60,7 @@ else
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

View File

@ -305,12 +305,12 @@ jint IPv6_supported()
}
/*
* 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;
}

View File

@ -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 @@ public class InheritedChannelTest {
@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()) },

View File

@ -62,6 +62,19 @@ public class Launcher {
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

View File

@ -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();
}

View File

@ -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);
}
}
}

View File

@ -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 @@ JNIEXPORT void JNICALL Java_Launcher_launch0
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);
}