changeset 57735:fdf6c221ebdc

8053479: (dc) DatagramChannel.read() throws exception instead of discarding data when buffer too small Reviewed-by: redestad, dfuchs
author alanb
date Sat, 18 Jan 2020 19:11:28 +0000
parents ed8e7bf32188
children 4b49cfba69fe
files src/java.base/unix/native/libnio/ch/DatagramDispatcher.c src/java.base/windows/native/libnio/ch/DatagramDispatcher.c test/jdk/java/nio/channels/DatagramChannel/Truncate.java
diffstat 3 files changed, 185 insertions(+), 45 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/unix/native/libnio/ch/DatagramDispatcher.c	Fri Jan 17 17:55:06 2020 +0000
+++ b/src/java.base/unix/native/libnio/ch/DatagramDispatcher.c	Sat Jan 18 19:11:28 2020 +0000
@@ -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 "nio.h"
+#include "nio_util.h"
 #include "sun_nio_ch_DatagramDispatcher.h"
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <sys/socket.h>
-#include <string.h>
-
-#include "nio_util.h"
-#include <limits.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 @@
 
 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 @@
     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 @@
     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);
 }
--- a/src/java.base/windows/native/libnio/ch/DatagramDispatcher.c	Fri Jan 17 17:55:06 2020 +0000
+++ b/src/java.base/windows/native/libnio/ch/DatagramDispatcher.c	Sat Jan 18 19:11:28 2020 +0000
@@ -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 @@
 
     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 @@
     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 @@
 
     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 @@
             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 @@
 
 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 @@
             JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException", 0);
             return IOS_THROWN;
         }
-        JNU_ThrowIOExceptionWithLastError(env, "Write failed");
+        JNU_ThrowIOExceptionWithLastError(env, "WSASend failed");
         return IOS_THROWN;
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/nio/channels/DatagramChannel/Truncate.java	Sat Jan 18 19:11:28 2020 +0000
@@ -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();
+    }
+}