Skip to content

Commit a56598f

Browse files
author
Brian Burkhalter
committed
8299684: (bf) JNI direct buffer functions with large capacity behave unexpectedly
Reviewed-by: dholmes, alanb
1 parent 542bfe6 commit a56598f

File tree

5 files changed

+218
-9
lines changed

5 files changed

+218
-9
lines changed

make/test/JtregNativeJdk.gmk

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright (c) 2015, 2022, Oracle and/or its affiliates. All rights reserved.
2+
# Copyright (c) 2015, 2023, 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
@@ -78,9 +78,11 @@ ifeq ($(call isTargetOs, windows), true)
7878
BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libAsyncInvokers := -I$(TEST_LIB_NATIVE_SRC)
7979

8080
BUILD_JDK_JTREG_LIBRARIES_LIBS_libTracePinnedThreads := jvm.lib
81+
BUILD_JDK_JTREG_LIBRARIES_LIBS_libNewDirectByteBuffer := $(WIN_LIB_JAVA)
8182
else
8283
BUILD_JDK_JTREG_LIBRARIES_LIBS_libstringPlatformChars := -ljava
8384
BUILD_JDK_JTREG_LIBRARIES_LIBS_libDirectIO := -ljava
85+
BUILD_JDK_JTREG_LIBRARIES_LIBS_libNewDirectByteBuffer := -ljava
8486
BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libNativeThread := -pthread
8587

8688
# java.lang.foreign tests

src/hotspot/share/prims/jni.cpp

+2-5
Original file line numberDiff line numberDiff line change
@@ -2959,7 +2959,7 @@ static bool initializeDirectBufferSupport(JNIEnv* env, JavaThread* thread) {
29592959
}
29602960

29612961
// Get needed field and method IDs
2962-
directByteBufferConstructor = env->GetMethodID(directByteBufferClass, "<init>", "(JI)V");
2962+
directByteBufferConstructor = env->GetMethodID(directByteBufferClass, "<init>", "(JJ)V");
29632963
if (env->ExceptionCheck()) {
29642964
env->ExceptionClear();
29652965
directBufferSupportInitializeFailed = 1;
@@ -3011,10 +3011,7 @@ extern "C" jobject JNICALL jni_NewDirectByteBuffer(JNIEnv *env, void* address, j
30113011

30123012
// Being paranoid about accidental sign extension on address
30133013
jlong addr = (jlong) ((uintptr_t) address);
3014-
// NOTE that package-private DirectByteBuffer constructor currently
3015-
// takes int capacity
3016-
jint cap = (jint) capacity;
3017-
jobject ret = env->NewObject(directByteBufferClass, directByteBufferConstructor, addr, cap);
3014+
jobject ret = env->NewObject(directByteBufferClass, directByteBufferConstructor, addr, capacity);
30183015
HOTSPOT_JNI_NEWDIRECTBYTEBUFFER_RETURN(ret);
30193016
return ret;
30203017
}

src/java.base/share/classes/java/nio/Direct-X-Buffer.java.template

+20-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2023, 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
@@ -178,14 +178,31 @@ class Direct$Type$Buffer$RW$$BO$
178178
}
179179

180180
// Invoked only by JNI: NewDirectByteBuffer(void*, long)
181+
// The long-valued capacity is restricted to int range.
181182
//
182-
private Direct$Type$Buffer(long addr, int cap) {
183-
super(-1, 0, cap, cap, null);
183+
private Direct$Type$Buffer(long addr, long cap) {
184+
super(-1, 0, checkCapacity(cap), (int)cap, null);
184185
address = addr;
185186
cleaner = null;
186187
att = null;
187188
}
188189

190+
// Throw an IllegalArgumentException if the capacity is not in
191+
// the range [0, Integer.MAX_VALUE]
192+
//
193+
private static int checkCapacity(long capacity) {
194+
if (capacity < 0) {
195+
throw new IllegalArgumentException
196+
("JNI NewDirectByteBuffer passed capacity < 0: ("
197+
+ capacity + ")");
198+
} else if (capacity > Integer.MAX_VALUE) {
199+
throw new IllegalArgumentException
200+
("JNI NewDirectByteBuffer passed capacity > Integer.MAX_VALUE: ("
201+
+ capacity + ")");
202+
}
203+
return (int)capacity;
204+
}
205+
189206
#end[rw]
190207

191208
// For memory-mapped buffers -- invoked by FileChannelImpl via reflection
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
* Copyright (c) 2023, 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+
import java.nio.ByteBuffer;
24+
import java.util.concurrent.atomic.AtomicReference;
25+
26+
import jdk.internal.misc.Unsafe;
27+
28+
import org.junit.jupiter.params.ParameterizedTest;
29+
import org.junit.jupiter.params.provider.ValueSource;
30+
31+
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
32+
import static org.junit.jupiter.api.Assertions.assertEquals;
33+
import static org.junit.jupiter.api.Assertions.assertFalse;
34+
import static org.junit.jupiter.api.Assertions.assertThrows;
35+
import static org.junit.jupiter.api.Assertions.assertTrue;
36+
37+
/*
38+
* @test
39+
* @bug 8299684
40+
* @summary Unit test for the JNI function NewDirectByteBuffer
41+
* @requires (sun.arch.data.model == "64" & os.maxMemory >= 8g)
42+
* @modules java.base/jdk.internal.misc
43+
* @run junit/othervm/native NewDirectByteBuffer
44+
*/
45+
public class NewDirectByteBuffer {
46+
private static final Unsafe UNSAFE;
47+
static {
48+
System.loadLibrary("NewDirectByteBuffer");
49+
UNSAFE = Unsafe.getUnsafe();
50+
}
51+
52+
private static final void checkBuffer(ByteBuffer buf, long capacity) {
53+
// Verify that the JNI function returns the correct capacity
54+
assertEquals(capacity, getDirectByteBufferCapacity(buf),
55+
"GetDirectBufferCapacity returned unexpected value");
56+
57+
// Verify that the initial state values are correct
58+
assertTrue(buf.isDirect(), "Buffer is not direct");
59+
assertFalse(buf.hasArray(), "Buffer has an array");
60+
if (capacity > 0) {
61+
assertTrue(buf.hasRemaining(), "Buffer has no remaining values");
62+
}
63+
assertFalse(buf.isReadOnly(), "Buffer s read-only");
64+
assertEquals(capacity, buf.capacity(),
65+
"Buffer::capacity returned unexpected value");
66+
assertEquals(0L, buf.position(),
67+
"Buffer::position returned unexpected value");
68+
assertEquals(capacity, buf.limit(),
69+
"Buffer::limit returned unexpected value");
70+
71+
// Verify that the various state mutators work correctly
72+
int halfPos = buf.capacity()/2;
73+
buf.position(halfPos);
74+
assertEquals(halfPos, buf.position(),
75+
"Position not set to halfPos");
76+
assertEquals(buf.capacity() - halfPos, buf.remaining(),
77+
"Remaining not capacity - halfPos");
78+
79+
buf.mark();
80+
81+
int twoThirdsPos = 2*(buf.capacity()/3);
82+
buf.position(twoThirdsPos);
83+
assertEquals(twoThirdsPos, buf.position(),
84+
"Position not set to twoThirdsPos");
85+
assertEquals(buf.capacity() - twoThirdsPos, buf.remaining(),
86+
"Remaining != capacity - twoThirdsPos");
87+
88+
buf.reset();
89+
assertEquals(halfPos, buf.position(),
90+
"Buffer not reset to halfPos");
91+
92+
buf.limit(twoThirdsPos);
93+
assertEquals(twoThirdsPos, buf.limit(),
94+
"Limit not set to twoThirdsPos");
95+
assertEquals(twoThirdsPos - halfPos, buf.remaining(),
96+
"Remaining != twoThirdsPos - halfPos");
97+
98+
buf.position(twoThirdsPos);
99+
assertFalse(buf.hasRemaining(), "Buffer has remaining values");
100+
}
101+
102+
@ParameterizedTest
103+
@ValueSource(longs = {0L, 1L, (long)Integer.MAX_VALUE/2,
104+
(long)Integer.MAX_VALUE - 1, (long)Integer.MAX_VALUE})
105+
void legalCapacities(long capacity) {
106+
long addr;
107+
try {
108+
addr = UNSAFE.allocateMemory(capacity);
109+
} catch (OutOfMemoryError ignore) {
110+
System.err.println("legalCapacities( " + capacity
111+
+ ") test skipped due to insufficient memory");
112+
return;
113+
}
114+
try {
115+
ByteBuffer buf = newDirectByteBuffer(addr, capacity);
116+
assertEquals(addr, getDirectBufferAddress(buf),
117+
"GetDirectBufferAddress does not return supplied address");
118+
checkBuffer(buf, capacity);
119+
} finally {
120+
UNSAFE.freeMemory(addr);
121+
}
122+
}
123+
124+
@ParameterizedTest
125+
@ValueSource(longs = {Long.MIN_VALUE, (long)Integer.MIN_VALUE - 1L, -1L,
126+
(long)Integer.MAX_VALUE + 1L, 3_000_000_000L, 5_000_000_000L,
127+
Long.MAX_VALUE})
128+
void illegalCapacities(long capacity) {
129+
assertThrows(IllegalArgumentException.class, () -> {
130+
long addr = UNSAFE.allocateMemory(1);
131+
try {
132+
ByteBuffer buf = newDirectByteBuffer(addr, capacity);
133+
} finally {
134+
UNSAFE.freeMemory(addr);
135+
}
136+
});
137+
}
138+
139+
// See libNewDirectByteBuffer.c for implementations.
140+
private static native ByteBuffer newDirectByteBuffer(long addr, long capacity);
141+
private static native long getDirectByteBufferCapacity(ByteBuffer buf);
142+
private static native long getDirectBufferAddress(ByteBuffer buf);
143+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright (c) 2023, 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+
#include <stdlib.h>
24+
#include "jni.h"
25+
26+
// private static native ByteBuffer newDirectByteBuffer(long addr, long size)
27+
JNIEXPORT jobject JNICALL
28+
Java_NewDirectByteBuffer_newDirectByteBuffer
29+
(JNIEnv *env, jclass cls, jlong addr, jlong size)
30+
{
31+
// Create the direct byte buffer, freeing the native memory if an exception
32+
// is thrown while constructing the buffer
33+
return (*env)->NewDirectByteBuffer(env, (void*)addr, size);
34+
}
35+
36+
// private static native long getDirectByteBufferCapacity(ByteBuffer buf)
37+
JNIEXPORT jlong JNICALL
38+
Java_NewDirectByteBuffer_getDirectByteBufferCapacity
39+
(JNIEnv *env, jclass cls, jobject buf)
40+
{
41+
return (*env)->GetDirectBufferCapacity(env, buf);
42+
}
43+
44+
// private static native long getDirectBufferAddress(ByteBuffer buf)
45+
JNIEXPORT jlong JNICALL
46+
Java_NewDirectByteBuffer_getDirectBufferAddress
47+
(JNIEnv *env, jclass cls, jobject buf)
48+
{
49+
return (jlong)(*env)->GetDirectBufferAddress(env, buf);
50+
}

0 commit comments

Comments
 (0)