Skip to content

Commit f65058e

Browse files
tabjyjerboaa
authored andcommitted
8053479: (dc) DatagramChannel.read() throws exception instead of discarding data when buffer too small
Backport-of: 6ef474a
1 parent a956ba6 commit f65058e

File tree

3 files changed

+185
-45
lines changed

3 files changed

+185
-45
lines changed

src/java.base/unix/native/libnio/ch/DatagramDispatcher.c

+16-18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -23,40 +23,38 @@
2323
* questions.
2424
*/
2525

26-
/*
27-
*/
28-
29-
#include "jni.h"
30-
#include "jni_util.h"
31-
#include "jvm.h"
32-
#include "jlong.h"
33-
#include "sun_nio_ch_DatagramDispatcher.h"
3426
#include <sys/types.h>
3527
#include <sys/uio.h>
3628
#include <sys/socket.h>
3729
#include <string.h>
30+
#include <limits.h>
3831

32+
#include "jni.h"
33+
#include "jni_util.h"
34+
#include "jvm.h"
35+
#include "jlong.h"
36+
#include "nio.h"
3937
#include "nio_util.h"
40-
#include <limits.h>
38+
#include "sun_nio_ch_DatagramDispatcher.h"
4139

4240
JNIEXPORT jint JNICALL
4341
Java_sun_nio_ch_DatagramDispatcher_read0(JNIEnv *env, jclass clazz,
44-
jobject fdo, jlong address, jint len)
42+
jobject fdo, jlong address, jint len)
4543
{
4644
jint fd = fdval(env, fdo);
4745
void *buf = (void *)jlong_to_ptr(address);
4846
int result = recv(fd, buf, len, 0);
4947
if (result < 0 && errno == ECONNREFUSED) {
5048
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
51-
return -2;
49+
return IOS_THROWN;
5250
}
5351
return convertReturnVal(env, result, JNI_TRUE);
5452
}
5553

5654

5755
JNIEXPORT jlong JNICALL
5856
Java_sun_nio_ch_DatagramDispatcher_readv0(JNIEnv *env, jclass clazz,
59-
jobject fdo, jlong address, jint len)
57+
jobject fdo, jlong address, jint len)
6058
{
6159
jint fd = fdval(env, fdo);
6260
ssize_t result = 0;
@@ -74,28 +72,28 @@ Java_sun_nio_ch_DatagramDispatcher_readv0(JNIEnv *env, jclass clazz,
7472
result = recvmsg(fd, &m, 0);
7573
if (result < 0 && errno == ECONNREFUSED) {
7674
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
77-
return -2;
75+
return IOS_THROWN;
7876
}
7977
return convertLongReturnVal(env, (jlong)result, JNI_TRUE);
8078
}
8179

8280
JNIEXPORT jint JNICALL
8381
Java_sun_nio_ch_DatagramDispatcher_write0(JNIEnv *env, jclass clazz,
84-
jobject fdo, jlong address, jint len)
82+
jobject fdo, jlong address, jint len)
8583
{
8684
jint fd = fdval(env, fdo);
8785
void *buf = (void *)jlong_to_ptr(address);
8886
int result = send(fd, buf, len, 0);
8987
if (result < 0 && errno == ECONNREFUSED) {
9088
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
91-
return -2;
89+
return IOS_THROWN;
9290
}
9391
return convertReturnVal(env, result, JNI_FALSE);
9492
}
9593

9694
JNIEXPORT jlong JNICALL
9795
Java_sun_nio_ch_DatagramDispatcher_writev0(JNIEnv *env, jclass clazz,
98-
jobject fdo, jlong address, jint len)
96+
jobject fdo, jlong address, jint len)
9997
{
10098
jint fd = fdval(env, fdo);
10199
struct iovec *iov = (struct iovec *)jlong_to_ptr(address);
@@ -113,7 +111,7 @@ Java_sun_nio_ch_DatagramDispatcher_writev0(JNIEnv *env, jclass clazz,
113111
result = sendmsg(fd, &m, 0);
114112
if (result < 0 && errno == ECONNREFUSED) {
115113
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
116-
return -2;
114+
return IOS_THROWN;
117115
}
118116
return convertLongReturnVal(env, (jlong)result, JNI_FALSE);
119117
}

src/java.base/windows/native/libnio/ch/DatagramDispatcher.c

+29-27
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2002, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -23,29 +23,27 @@
2323
* questions.
2424
*/
2525

26-
/*
27-
*/
28-
2926
#include <windows.h>
3027
#include <winsock2.h>
3128
#include <ctype.h>
29+
3230
#include "jni.h"
3331
#include "jni_util.h"
3432
#include "jvm.h"
3533
#include "jlong.h"
36-
#include "sun_nio_ch_DatagramDispatcher.h"
37-
3834
#include "nio.h"
3935
#include "nio_util.h"
4036

37+
#include "sun_nio_ch_DatagramDispatcher.h"
38+
4139

4240
/**************************************************************
4341
* DatagramDispatcher.c
4442
*/
4543

4644
JNIEXPORT jint JNICALL
4745
Java_sun_nio_ch_DatagramDispatcher_read0(JNIEnv *env, jclass clazz, jobject fdo,
48-
jlong address, jint len)
46+
jlong address, jint len)
4947
{
5048
/* set up */
5149
int i = 0;
@@ -69,16 +67,18 @@ Java_sun_nio_ch_DatagramDispatcher_read0(JNIEnv *env, jclass clazz, jobject fdo,
6967

7068
if (i == SOCKET_ERROR) {
7169
int theErr = (jint)WSAGetLastError();
72-
if (theErr == WSAEWOULDBLOCK) {
73-
return IOS_UNAVAILABLE;
74-
}
75-
if (theErr == WSAECONNRESET) {
76-
purgeOutstandingICMP(env, clazz, fd);
77-
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
70+
if (theErr != WSAEMSGSIZE) {
71+
if (theErr == WSAEWOULDBLOCK) {
72+
return IOS_UNAVAILABLE;
73+
}
74+
if (theErr == WSAECONNRESET) {
75+
purgeOutstandingICMP(env, clazz, fd);
76+
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
77+
return IOS_THROWN;
78+
}
79+
JNU_ThrowIOExceptionWithLastError(env, "WSARecv failed");
7880
return IOS_THROWN;
7981
}
80-
JNU_ThrowIOExceptionWithLastError(env, "Write failed");
81-
return IOS_THROWN;
8282
}
8383

8484
return convertReturnVal(env, (jint)read, JNI_TRUE);
@@ -104,7 +104,7 @@ Java_sun_nio_ch_DatagramDispatcher_readv0(JNIEnv *env, jclass clazz,
104104
for(i=0; i<len; i++) {
105105
bufs[i].buf = (char *)iovp[i].iov_base;
106106
bufs[i].len = (u_long)iovp[i].iov_len;
107-
}
107+
}
108108

109109
/* read into the buffers */
110110
i = WSARecv((SOCKET)fd, /* Socket */
@@ -120,16 +120,18 @@ Java_sun_nio_ch_DatagramDispatcher_readv0(JNIEnv *env, jclass clazz,
120120

121121
if (i != 0) {
122122
int theErr = (jint)WSAGetLastError();
123-
if (theErr == WSAEWOULDBLOCK) {
124-
return IOS_UNAVAILABLE;
125-
}
126-
if (theErr == WSAECONNRESET) {
127-
purgeOutstandingICMP(env, clazz, fd);
128-
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
123+
if (theErr != WSAEMSGSIZE) {
124+
if (theErr == WSAEWOULDBLOCK) {
125+
return IOS_UNAVAILABLE;
126+
}
127+
if (theErr == WSAECONNRESET) {
128+
purgeOutstandingICMP(env, clazz, fd);
129+
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
130+
return IOS_THROWN;
131+
}
132+
JNU_ThrowIOExceptionWithLastError(env, "WSARecv failed");
129133
return IOS_THROWN;
130134
}
131-
JNU_ThrowIOExceptionWithLastError(env, "Write failed");
132-
return IOS_THROWN;
133135
}
134136

135137
return convertLongReturnVal(env, (jlong)read, JNI_TRUE);
@@ -169,7 +171,7 @@ Java_sun_nio_ch_DatagramDispatcher_write0(JNIEnv *env, jclass clazz,
169171
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
170172
return IOS_THROWN;
171173
}
172-
JNU_ThrowIOExceptionWithLastError(env, "Write failed");
174+
JNU_ThrowIOExceptionWithLastError(env, "WSASend failed");
173175
return IOS_THROWN;
174176
}
175177

@@ -178,7 +180,7 @@ Java_sun_nio_ch_DatagramDispatcher_write0(JNIEnv *env, jclass clazz,
178180

179181
JNIEXPORT jlong JNICALL
180182
Java_sun_nio_ch_DatagramDispatcher_writev0(JNIEnv *env, jclass clazz,
181-
jobject fdo, jlong address, jint len)
183+
jobject fdo, jlong address, jint len)
182184
{
183185
/* set up */
184186
int i = 0;
@@ -219,7 +221,7 @@ Java_sun_nio_ch_DatagramDispatcher_writev0(JNIEnv *env, jclass clazz,
219221
JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
220222
return IOS_THROWN;
221223
}
222-
JNU_ThrowIOExceptionWithLastError(env, "Write failed");
224+
JNU_ThrowIOExceptionWithLastError(env, "WSASend failed");
223225
return IOS_THROWN;
224226
}
225227

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/* @test
25+
* @bug 8053479
26+
* @run main Truncate
27+
* @summary Test DatagramChannel receive/read where there are fewer bytes remaining
28+
* in the buffer than are required to hold the datagram. The remainder of the
29+
* datagram should be silently discarded.
30+
*/
31+
32+
import java.io.IOException;
33+
import java.net.InetAddress;
34+
import java.net.InetSocketAddress;
35+
import java.net.SocketAddress;
36+
import java.nio.ByteBuffer;
37+
import java.nio.channels.DatagramChannel;
38+
import java.util.Arrays;
39+
import java.util.stream.IntStream;
40+
41+
public class Truncate {
42+
static final int LARGE_SIZE = 1000;
43+
static final int SMALL_SIZE = 100;
44+
45+
public static void main(String[] args) throws Exception {
46+
try (DatagramChannel dc = DatagramChannel.open()) {
47+
dc.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
48+
49+
// not connected
50+
testReceiveDiscards(dc);
51+
52+
// connected
53+
dc.connect(dc.getLocalAddress());
54+
testReceiveDiscards(dc);
55+
testReadDiscards(dc);
56+
testScatteringReadDiscards(dc);
57+
}
58+
}
59+
60+
/**
61+
* Receive a datagram with a buffer that has fewer bytes remaining than are
62+
* required to hold the datagram.
63+
*/
64+
static void testReceiveDiscards(DatagramChannel dc) throws IOException {
65+
ByteBuffer largeBuffer = send(dc, LARGE_SIZE, dc.getLocalAddress());
66+
67+
ByteBuffer smallBuffer = ByteBuffer.allocate(SMALL_SIZE);
68+
SocketAddress sender = dc.receive(smallBuffer);
69+
assertTrue(sender.equals(dc.getLocalAddress()));
70+
71+
// check buffer/contents
72+
smallBuffer.flip();
73+
assertTrue(smallBuffer.remaining() == SMALL_SIZE);
74+
assertTrue(Arrays.equals(smallBuffer.array(), 0, SMALL_SIZE,
75+
largeBuffer.array(), 0, SMALL_SIZE));
76+
}
77+
78+
/**
79+
* Read a datagram with a buffer that has fewer bytes remaining than are
80+
* required to hold the datagram.
81+
*/
82+
static void testReadDiscards(DatagramChannel dc) throws IOException {
83+
ByteBuffer largeBuffer = send(dc, LARGE_SIZE, dc.getRemoteAddress());
84+
85+
ByteBuffer smallBuffer = ByteBuffer.allocate(SMALL_SIZE);
86+
int n = dc.read(smallBuffer);
87+
assertTrue(n == SMALL_SIZE);
88+
89+
// check buffer/contents
90+
smallBuffer.flip();
91+
assertTrue(smallBuffer.remaining() == SMALL_SIZE);
92+
assertTrue(Arrays.equals(smallBuffer.array(), 0, SMALL_SIZE,
93+
largeBuffer.array(), 0, SMALL_SIZE));
94+
}
95+
96+
/**
97+
* Read a datagram with an array of buffers that have fewer bytes remaining
98+
* than are required to hold the datagram.
99+
*/
100+
static void testScatteringReadDiscards(DatagramChannel dc) throws IOException {
101+
ByteBuffer largeBuffer = send(dc, LARGE_SIZE, dc.getRemoteAddress());
102+
103+
ByteBuffer smallBuffer1 = ByteBuffer.allocate(SMALL_SIZE);
104+
ByteBuffer smallBuffer2 = ByteBuffer.allocate(SMALL_SIZE);
105+
ByteBuffer[] bufs = new ByteBuffer[] { smallBuffer1, smallBuffer2 };
106+
long n = dc.read(bufs);
107+
assertTrue(n == (SMALL_SIZE * bufs.length));
108+
109+
// check buffer/contents
110+
smallBuffer1.flip();
111+
assertTrue(smallBuffer1.remaining() == SMALL_SIZE);
112+
assertTrue(Arrays.equals(smallBuffer1.array(), 0, SMALL_SIZE,
113+
largeBuffer.array(), 0, SMALL_SIZE));
114+
smallBuffer2.flip();
115+
assertTrue(smallBuffer2.remaining() == SMALL_SIZE);
116+
assertTrue(Arrays.equals(smallBuffer2.array(), 0, SMALL_SIZE,
117+
largeBuffer.array(), SMALL_SIZE, SMALL_SIZE << 1));
118+
}
119+
120+
/**
121+
* Send a datagram of the given size to the given target address.
122+
* @return the buffer with the datagram sent to the target address
123+
*/
124+
static ByteBuffer send(DatagramChannel dc, int size, SocketAddress target)
125+
throws IOException
126+
{
127+
ByteBuffer buffer = ByteBuffer.allocate(size);
128+
IntStream.range(0, size).forEach(i -> buffer.put((byte)i));
129+
buffer.flip();
130+
131+
int n = dc.send(buffer, target);
132+
assertTrue(n == size);
133+
buffer.flip();
134+
return buffer;
135+
}
136+
137+
static void assertTrue(boolean e) {
138+
if (!e) throw new RuntimeException();
139+
}
140+
}

0 commit comments

Comments
 (0)