8053479: (dc) DatagramChannel.read() throws exception instead of discarding data when buffer too small

Reviewed-by: redestad, dfuchs
This commit is contained in:
Alan Bateman 2020-01-18 19:11:28 +00:00
parent c6da6681d4
commit 6ef474a4f4
3 changed files with 185 additions and 45 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2020, 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
@ -23,32 +23,30 @@
* questions.
*/
/*
*/
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <string.h>
#include <limits.h>
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
#include "sun_nio_ch_DatagramDispatcher.h"
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/socket.h>
#include <string.h>
#include "nio.h"
#include "nio_util.h"
#include <limits.h>
#include "sun_nio_ch_DatagramDispatcher.h"
JNIEXPORT jint JNICALL
Java_sun_nio_ch_DatagramDispatcher_read0(JNIEnv *env, jclass clazz,
jobject fdo, jlong address, jint len)
jobject fdo, jlong address, jint len)
{
jint fd = fdval(env, fdo);
void *buf = (void *)jlong_to_ptr(address);
int result = recv(fd, buf, len, 0);
if (result < 0 && errno == ECONNREFUSED) {
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
return -2;
return IOS_THROWN;
}
return convertReturnVal(env, result, JNI_TRUE);
}
@ -56,7 +54,7 @@ Java_sun_nio_ch_DatagramDispatcher_read0(JNIEnv *env, jclass clazz,
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_DatagramDispatcher_readv0(JNIEnv *env, jclass clazz,
jobject fdo, jlong address, jint len)
jobject fdo, jlong address, jint len)
{
jint fd = fdval(env, fdo);
ssize_t result = 0;
@ -74,28 +72,28 @@ Java_sun_nio_ch_DatagramDispatcher_readv0(JNIEnv *env, jclass clazz,
result = recvmsg(fd, &m, 0);
if (result < 0 && errno == ECONNREFUSED) {
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
return -2;
return IOS_THROWN;
}
return convertLongReturnVal(env, (jlong)result, JNI_TRUE);
}
JNIEXPORT jint JNICALL
Java_sun_nio_ch_DatagramDispatcher_write0(JNIEnv *env, jclass clazz,
jobject fdo, jlong address, jint len)
jobject fdo, jlong address, jint len)
{
jint fd = fdval(env, fdo);
void *buf = (void *)jlong_to_ptr(address);
int result = send(fd, buf, len, 0);
if (result < 0 && errno == ECONNREFUSED) {
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
return -2;
return IOS_THROWN;
}
return convertReturnVal(env, result, JNI_FALSE);
}
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_DatagramDispatcher_writev0(JNIEnv *env, jclass clazz,
jobject fdo, jlong address, jint len)
jobject fdo, jlong address, jint len)
{
jint fd = fdval(env, fdo);
struct iovec *iov = (struct iovec *)jlong_to_ptr(address);
@ -113,7 +111,7 @@ Java_sun_nio_ch_DatagramDispatcher_writev0(JNIEnv *env, jclass clazz,
result = sendmsg(fd, &m, 0);
if (result < 0 && errno == ECONNREFUSED) {
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
return -2;
return IOS_THROWN;
}
return convertLongReturnVal(env, (jlong)result, JNI_FALSE);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2020, 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
@ -23,21 +23,19 @@
* questions.
*/
/*
*/
#include <windows.h>
#include <winsock2.h>
#include <ctype.h>
#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
#include "sun_nio_ch_DatagramDispatcher.h"
#include "nio.h"
#include "nio_util.h"
#include "sun_nio_ch_DatagramDispatcher.h"
/**************************************************************
* DatagramDispatcher.c
@ -45,7 +43,7 @@
JNIEXPORT jint JNICALL
Java_sun_nio_ch_DatagramDispatcher_read0(JNIEnv *env, jclass clazz, jobject fdo,
jlong address, jint len)
jlong address, jint len)
{
/* set up */
int i = 0;
@ -69,16 +67,18 @@ Java_sun_nio_ch_DatagramDispatcher_read0(JNIEnv *env, jclass clazz, jobject fdo,
if (i == SOCKET_ERROR) {
int theErr = (jint)WSAGetLastError();
if (theErr == WSAEWOULDBLOCK) {
return IOS_UNAVAILABLE;
}
if (theErr == WSAECONNRESET) {
purgeOutstandingICMP(env, clazz, fd);
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
if (theErr != WSAEMSGSIZE) {
if (theErr == WSAEWOULDBLOCK) {
return IOS_UNAVAILABLE;
}
if (theErr == WSAECONNRESET) {
purgeOutstandingICMP(env, clazz, fd);
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
return IOS_THROWN;
}
JNU_ThrowIOExceptionWithLastError(env, "WSARecv failed");
return IOS_THROWN;
}
JNU_ThrowIOExceptionWithLastError(env, "Write failed");
return IOS_THROWN;
}
return convertReturnVal(env, (jint)read, JNI_TRUE);
@ -104,7 +104,7 @@ Java_sun_nio_ch_DatagramDispatcher_readv0(JNIEnv *env, jclass clazz,
for(i=0; i<len; i++) {
bufs[i].buf = (char *)iovp[i].iov_base;
bufs[i].len = (u_long)iovp[i].iov_len;
}
}
/* read into the buffers */
i = WSARecv((SOCKET)fd, /* Socket */
@ -120,16 +120,18 @@ Java_sun_nio_ch_DatagramDispatcher_readv0(JNIEnv *env, jclass clazz,
if (i != 0) {
int theErr = (jint)WSAGetLastError();
if (theErr == WSAEWOULDBLOCK) {
return IOS_UNAVAILABLE;
}
if (theErr == WSAECONNRESET) {
purgeOutstandingICMP(env, clazz, fd);
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
if (theErr != WSAEMSGSIZE) {
if (theErr == WSAEWOULDBLOCK) {
return IOS_UNAVAILABLE;
}
if (theErr == WSAECONNRESET) {
purgeOutstandingICMP(env, clazz, fd);
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
return IOS_THROWN;
}
JNU_ThrowIOExceptionWithLastError(env, "WSARecv failed");
return IOS_THROWN;
}
JNU_ThrowIOExceptionWithLastError(env, "Write failed");
return IOS_THROWN;
}
return convertLongReturnVal(env, (jlong)read, JNI_TRUE);
@ -169,7 +171,7 @@ Java_sun_nio_ch_DatagramDispatcher_write0(JNIEnv *env, jclass clazz,
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
return IOS_THROWN;
}
JNU_ThrowIOExceptionWithLastError(env, "Write failed");
JNU_ThrowIOExceptionWithLastError(env, "WSASend failed");
return IOS_THROWN;
}
@ -178,7 +180,7 @@ Java_sun_nio_ch_DatagramDispatcher_write0(JNIEnv *env, jclass clazz,
JNIEXPORT jlong JNICALL
Java_sun_nio_ch_DatagramDispatcher_writev0(JNIEnv *env, jclass clazz,
jobject fdo, jlong address, jint len)
jobject fdo, jlong address, jint len)
{
/* set up */
int i = 0;
@ -219,7 +221,7 @@ Java_sun_nio_ch_DatagramDispatcher_writev0(JNIEnv *env, jclass clazz,
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
return IOS_THROWN;
}
JNU_ThrowIOExceptionWithLastError(env, "Write failed");
JNU_ThrowIOExceptionWithLastError(env, "WSASend failed");
return IOS_THROWN;
}

View File

@ -0,0 +1,140 @@
/*
* Copyright (c) 2020, 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.
*/
/* @test
* @bug 8053479
* @run main Truncate
* @summary Test DatagramChannel receive/read where there are fewer bytes remaining
* in the buffer than are required to hold the datagram. The remainder of the
* datagram should be silently discarded.
*/
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.util.Arrays;
import java.util.stream.IntStream;
public class Truncate {
static final int LARGE_SIZE = 1000;
static final int SMALL_SIZE = 100;
public static void main(String[] args) throws Exception {
try (DatagramChannel dc = DatagramChannel.open()) {
dc.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
// not connected
testReceiveDiscards(dc);
// connected
dc.connect(dc.getLocalAddress());
testReceiveDiscards(dc);
testReadDiscards(dc);
testScatteringReadDiscards(dc);
}
}
/**
* Receive a datagram with a buffer that has fewer bytes remaining than are
* required to hold the datagram.
*/
static void testReceiveDiscards(DatagramChannel dc) throws IOException {
ByteBuffer largeBuffer = send(dc, LARGE_SIZE, dc.getLocalAddress());
ByteBuffer smallBuffer = ByteBuffer.allocate(SMALL_SIZE);
SocketAddress sender = dc.receive(smallBuffer);
assertTrue(sender.equals(dc.getLocalAddress()));
// check buffer/contents
smallBuffer.flip();
assertTrue(smallBuffer.remaining() == SMALL_SIZE);
assertTrue(Arrays.equals(smallBuffer.array(), 0, SMALL_SIZE,
largeBuffer.array(), 0, SMALL_SIZE));
}
/**
* Read a datagram with a buffer that has fewer bytes remaining than are
* required to hold the datagram.
*/
static void testReadDiscards(DatagramChannel dc) throws IOException {
ByteBuffer largeBuffer = send(dc, LARGE_SIZE, dc.getRemoteAddress());
ByteBuffer smallBuffer = ByteBuffer.allocate(SMALL_SIZE);
int n = dc.read(smallBuffer);
assertTrue(n == SMALL_SIZE);
// check buffer/contents
smallBuffer.flip();
assertTrue(smallBuffer.remaining() == SMALL_SIZE);
assertTrue(Arrays.equals(smallBuffer.array(), 0, SMALL_SIZE,
largeBuffer.array(), 0, SMALL_SIZE));
}
/**
* Read a datagram with an array of buffers that have fewer bytes remaining
* than are required to hold the datagram.
*/
static void testScatteringReadDiscards(DatagramChannel dc) throws IOException {
ByteBuffer largeBuffer = send(dc, LARGE_SIZE, dc.getRemoteAddress());
ByteBuffer smallBuffer1 = ByteBuffer.allocate(SMALL_SIZE);
ByteBuffer smallBuffer2 = ByteBuffer.allocate(SMALL_SIZE);
ByteBuffer[] bufs = new ByteBuffer[] { smallBuffer1, smallBuffer2 };
long n = dc.read(bufs);
assertTrue(n == (SMALL_SIZE * bufs.length));
// check buffer/contents
smallBuffer1.flip();
assertTrue(smallBuffer1.remaining() == SMALL_SIZE);
assertTrue(Arrays.equals(smallBuffer1.array(), 0, SMALL_SIZE,
largeBuffer.array(), 0, SMALL_SIZE));
smallBuffer2.flip();
assertTrue(smallBuffer2.remaining() == SMALL_SIZE);
assertTrue(Arrays.equals(smallBuffer2.array(), 0, SMALL_SIZE,
largeBuffer.array(), SMALL_SIZE, SMALL_SIZE << 1));
}
/**
* Send a datagram of the given size to the given target address.
* @return the buffer with the datagram sent to the target address
*/
static ByteBuffer send(DatagramChannel dc, int size, SocketAddress target)
throws IOException
{
ByteBuffer buffer = ByteBuffer.allocate(size);
IntStream.range(0, size).forEach(i -> buffer.put((byte)i));
buffer.flip();
int n = dc.send(buffer, target);
assertTrue(n == size);
buffer.flip();
return buffer;
}
static void assertTrue(boolean e) {
if (!e) throw new RuntimeException();
}
}