Skip to content

Commit 9a19b3b

Browse files
committed
8361447: [REDO] Checked version of JNI Release<type>ArrayElements needs to filter out known wrapped arrays
Reviewed-by: mdoerr Backport-of: 1afb29ad5747baac9f165a8056d393df8de97c4a
1 parent 40381e1 commit 9a19b3b

File tree

6 files changed

+343
-26
lines changed

6 files changed

+343
-26
lines changed

src/hotspot/share/memory/guardedMemory.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@
2727
#include "memory/guardedMemory.hpp"
2828
#include "runtime/os.hpp"
2929

30-
void* GuardedMemory::wrap_copy(const void* ptr, const size_t len, const void* tag) {
30+
void* GuardedMemory::wrap_copy(const void* ptr, const size_t len,
31+
const void* tag, const void* tag2) {
3132
size_t total_sz = GuardedMemory::get_total_size(len);
3233
void* outerp = os::malloc(total_sz, mtInternal);
3334
if (outerp != NULL) {
34-
GuardedMemory guarded(outerp, len, tag);
35+
GuardedMemory guarded(outerp, len, tag, tag2);
3536
void* innerp = guarded.get_user_ptr();
3637
if (ptr != nullptr) {
3738
memcpy(innerp, ptr, len);
@@ -60,8 +61,8 @@ void GuardedMemory::print_on(outputStream* st) const {
6061
return;
6162
}
6263
st->print_cr("GuardedMemory(" PTR_FORMAT ") base_addr=" PTR_FORMAT
63-
" tag=" PTR_FORMAT " user_size=" SIZE_FORMAT " user_data=" PTR_FORMAT,
64-
p2i(this), p2i(_base_addr), p2i(get_tag()), get_user_size(), p2i(get_user_ptr()));
64+
" tag=" PTR_FORMAT "tag2=" PTR_FORMAT " user_size=" SIZE_FORMAT " user_data=" PTR_FORMAT,
65+
p2i(this), p2i(_base_addr), p2i(get_tag()), p2i(get_tag2()), get_user_size(), p2i(get_user_ptr()));
6566

6667
Guard* guard = get_head_guard();
6768
st->print_cr(" Header guard @" PTR_FORMAT " is %s", p2i(guard), (guard->verify() ? "OK" : "BROKEN"));

src/hotspot/share/memory/guardedMemory.hpp

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2014, 2025, 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
@@ -26,6 +26,7 @@
2626
#define SHARE_MEMORY_GUARDEDMEMORY_HPP
2727

2828
#include "memory/allocation.hpp"
29+
#include "runtime/os.hpp"
2930
#include "utilities/globalDefinitions.hpp"
3031

3132
/**
@@ -43,13 +44,14 @@
4344
* |base_addr | 0xABABABABABABABAB | Head guard |
4445
* |+16 | <size_t:user_size> | User data size |
4546
* |+sizeof(uintptr_t) | <tag> | Tag word |
47+
* |+sizeof(uintptr_t) | <tag2> | Tag word |
4648
* |+sizeof(void*) | 0xF1 <user_data> ( | User data |
4749
* |+user_size | 0xABABABABABABABAB | Tail guard |
4850
* -------------------------------------------------------------
4951
*
5052
* Where:
5153
* - guard padding uses "badResourceValue" (0xAB)
52-
* - tag word is general purpose
54+
* - tag word and tag2 word are general purpose
5355
* - user data
5456
* -- initially padded with "uninitBlockPad" (0xF1),
5557
* -- to "freeBlockPad" (0xBA), when freed
@@ -111,6 +113,10 @@ class GuardedMemory : StackObj { // Wrapper on stack
111113
}
112114

113115
bool verify() const {
116+
// We may not be able to dereference directly.
117+
if (!os::is_readable_range((const void*) _guard, (const void*) (_guard + GUARD_SIZE))) {
118+
return false;
119+
}
114120
u_char* c = (u_char*) _guard;
115121
u_char* end = c + GUARD_SIZE;
116122
while (c < end) {
@@ -137,13 +143,16 @@ class GuardedMemory : StackObj { // Wrapper on stack
137143
size_t _user_size;
138144
};
139145
void* _tag;
146+
void* _tag2;
140147
public:
141148
void set_user_size(const size_t usz) { _user_size = usz; }
142149
size_t get_user_size() const { return _user_size; }
143150

144151
void set_tag(const void* tag) { _tag = (void*) tag; }
145152
void* get_tag() const { return _tag; }
146153

154+
void set_tag2(const void* tag2) { _tag2 = (void*) tag2; }
155+
void* get_tag2() const { return _tag2; }
147156
}; // GuardedMemory::GuardHeader
148157

149158
// Guarded Memory...
@@ -162,9 +171,11 @@ class GuardedMemory : StackObj { // Wrapper on stack
162171
* @param base_ptr allocation wishing to be wrapped, must be at least "GuardedMemory::get_total_size()" bytes.
163172
* @param user_size the size of the user data to be wrapped.
164173
* @param tag optional general purpose tag.
174+
* @param tag2 optional second general purpose tag.
165175
*/
166-
GuardedMemory(void* base_ptr, const size_t user_size, const void* tag = NULL) {
167-
wrap_with_guards(base_ptr, user_size, tag);
176+
GuardedMemory(void* base_ptr, const size_t user_size,
177+
const void* tag = nullptr, const void* tag2 = nullptr) {
178+
wrap_with_guards(base_ptr, user_size, tag, tag2);
168179
}
169180

170181
/**
@@ -189,16 +200,19 @@ class GuardedMemory : StackObj { // Wrapper on stack
189200
* @param base_ptr allocation wishing to be wrapped, must be at least "GuardedMemory::get_total_size()" bytes.
190201
* @param user_size the size of the user data to be wrapped.
191202
* @param tag optional general purpose tag.
203+
* @param tag2 optional second general purpose tag.
192204
*
193205
* @return user data pointer (inner pointer to supplied "base_ptr").
194206
*/
195-
void* wrap_with_guards(void* base_ptr, size_t user_size, const void* tag = NULL) {
207+
void* wrap_with_guards(void* base_ptr, size_t user_size,
208+
const void* tag = nullptr, const void* tag2 = nullptr) {
196209
assert(base_ptr != NULL, "Attempt to wrap NULL with memory guard");
197210
_base_addr = (u_char*)base_ptr;
198211
get_head_guard()->build();
199212
get_head_guard()->set_user_size(user_size);
200213
get_tail_guard()->build();
201214
set_tag(tag);
215+
set_tag2(tag2);
202216
set_user_bytes(uninitBlockPad);
203217
assert(verify_guards(), "Expected valid memory guards");
204218
return get_user_ptr();
@@ -230,6 +244,20 @@ class GuardedMemory : StackObj { // Wrapper on stack
230244
*/
231245
void* get_tag() const { return get_head_guard()->get_tag(); }
232246

247+
/**
248+
* Set the second general purpose tag.
249+
*
250+
* @param tag general purpose tag.
251+
*/
252+
void set_tag2(const void* tag) { get_head_guard()->set_tag2(tag); }
253+
254+
/**
255+
* Return the second general purpose tag.
256+
*
257+
* @return the second general purpose tag, defaults to null.
258+
*/
259+
void* get_tag2() const { return get_head_guard()->get_tag2(); }
260+
233261
/**
234262
* Return the size of the user data.
235263
*
@@ -302,10 +330,12 @@ class GuardedMemory : StackObj { // Wrapper on stack
302330
* @param ptr the memory to be copied
303331
* @param len the length of the copy
304332
* @param tag optional general purpose tag (see GuardedMemory::get_tag())
333+
* @param tag2 optional general purpose tag (see GuardedMemory::get_tag2())
305334
*
306335
* @return guarded wrapped memory pointer to the user area, or NULL if OOM.
307336
*/
308-
static void* wrap_copy(const void* p, const size_t len, const void* tag = NULL);
337+
static void* wrap_copy(const void* p, const size_t len,
338+
const void* tag = nullptr, const void* tag2 = nullptr);
309339

310340
/**
311341
* Free wrapped copy.

src/hotspot/share/prims/jniCheck.cpp

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -351,24 +351,33 @@ check_is_obj_array(JavaThread* thr, jarray jArray) {
351351
}
352352
}
353353

354+
// Arbitrary (but well-known) tag for GetStringChars
355+
const void* STRING_TAG = (void*)0x47114711;
356+
357+
// Arbitrary (but well-known) tag for GetStringUTFChars
358+
const void* STRING_UTF_TAG = (void*) 0x48124812;
359+
360+
// Arbitrary (but well-known) tag for GetPrimitiveArrayCritical
361+
const void* CRITICAL_TAG = (void*)0x49134913;
362+
354363
/*
355364
* Copy and wrap array elements for bounds checking.
356365
* Remember the original elements (GuardedMemory::get_tag())
357366
*/
358367
static void* check_jni_wrap_copy_array(JavaThread* thr, jarray array,
359-
void* orig_elements) {
368+
void* orig_elements, jboolean is_critical = JNI_FALSE) {
360369
void* result;
361370
IN_VM(
362371
oop a = JNIHandles::resolve_non_null(array);
363372
size_t len = arrayOop(a)->length() <<
364373
TypeArrayKlass::cast(a->klass())->log2_element_size();
365-
result = GuardedMemory::wrap_copy(orig_elements, len, orig_elements);
374+
result = GuardedMemory::wrap_copy(orig_elements, len, orig_elements, is_critical ? CRITICAL_TAG : nullptr);
366375
)
367376
return result;
368377
}
369378

370379
static void* check_wrapped_array(JavaThread* thr, const char* fn_name,
371-
void* obj, void* carray, size_t* rsz) {
380+
void* obj, void* carray, size_t* rsz, jboolean is_critical) {
372381
if (carray == NULL) {
373382
tty->print_cr("%s: elements vector NULL" PTR_FORMAT, fn_name, p2i(obj));
374383
NativeReportJNIFatalError(thr, "Elements vector NULL");
@@ -387,6 +396,29 @@ static void* check_wrapped_array(JavaThread* thr, const char* fn_name,
387396
DEBUG_ONLY(guarded.print_on(tty);) // This may crash.
388397
NativeReportJNIFatalError(thr, err_msg("%s: unrecognized elements", fn_name));
389398
}
399+
if (orig_result == STRING_TAG || orig_result == STRING_UTF_TAG) {
400+
bool was_utf = orig_result == STRING_UTF_TAG;
401+
tty->print_cr("%s: called on something allocated by %s",
402+
fn_name, was_utf ? "GetStringUTFChars" : "GetStringChars");
403+
DEBUG_ONLY(guarded.print_on(tty);) // This may crash.
404+
NativeReportJNIFatalError(thr, err_msg("%s called on something allocated by %s",
405+
fn_name, was_utf ? "GetStringUTFChars" : "GetStringChars"));
406+
}
407+
408+
if (is_critical && (guarded.get_tag2() != CRITICAL_TAG)) {
409+
tty->print_cr("%s: called on something not allocated by GetPrimitiveArrayCritical", fn_name);
410+
DEBUG_ONLY(guarded.print_on(tty);) // This may crash.
411+
NativeReportJNIFatalError(thr, err_msg("%s called on something not allocated by GetPrimitiveArrayCritical",
412+
fn_name));
413+
}
414+
415+
if (!is_critical && (guarded.get_tag2() == CRITICAL_TAG)) {
416+
tty->print_cr("%s: called on something allocated by GetPrimitiveArrayCritical", fn_name);
417+
DEBUG_ONLY(guarded.print_on(tty);) // This may crash.
418+
NativeReportJNIFatalError(thr, err_msg("%s called on something allocated by GetPrimitiveArrayCritical",
419+
fn_name));
420+
}
421+
390422
if (rsz != NULL) {
391423
*rsz = guarded.get_user_size();
392424
}
@@ -396,7 +428,7 @@ static void* check_wrapped_array(JavaThread* thr, const char* fn_name,
396428
static void* check_wrapped_array_release(JavaThread* thr, const char* fn_name,
397429
void* obj, void* carray, jint mode, jboolean is_critical) {
398430
size_t sz;
399-
void* orig_result = check_wrapped_array(thr, fn_name, obj, carray, &sz);
431+
void* orig_result = check_wrapped_array(thr, fn_name, obj, carray, &sz, is_critical);
400432
switch (mode) {
401433
case 0:
402434
memcpy(orig_result, carray, sz);
@@ -1431,9 +1463,6 @@ JNI_ENTRY_CHECKED(jsize,
14311463
return result;
14321464
JNI_END
14331465

1434-
// Arbitrary (but well-known) tag
1435-
const void* STRING_TAG = (void*)0x47114711;
1436-
14371466
JNI_ENTRY_CHECKED(const jchar *,
14381467
checked_jni_GetStringChars(JNIEnv *env,
14391468
jstring str,
@@ -1515,9 +1544,6 @@ JNI_ENTRY_CHECKED(jsize,
15151544
return result;
15161545
JNI_END
15171546

1518-
// Arbitrary (but well-known) tag - different than GetStringChars
1519-
const void* STRING_UTF_TAG = (void*) 0x48124812;
1520-
15211547
JNI_ENTRY_CHECKED(const char *,
15221548
checked_jni_GetStringUTFChars(JNIEnv *env,
15231549
jstring str,
@@ -1839,7 +1865,7 @@ JNI_ENTRY_CHECKED(void *,
18391865
)
18401866
void *result = UNCHECKED()->GetPrimitiveArrayCritical(env, array, isCopy);
18411867
if (result != NULL) {
1842-
result = check_jni_wrap_copy_array(thr, array, result);
1868+
result = check_jni_wrap_copy_array(thr, array, result, JNI_TRUE);
18431869
}
18441870
functionExit(thr);
18451871
return result;

test/hotspot/gtest/memory/test_guardedMemory.cpp

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ TEST(GuardedMemory, size) {
5959
}
6060

6161
// Test the basic characteristics
62-
TEST(GuardedMemory, basic) {
62+
TEST_VM(GuardedMemory, basic) {
6363
u_char* basep =
6464
(u_char*) os::malloc(GuardedMemory::get_total_size(1), mtInternal);
6565
GuardedMemory guarded(basep, 1, GEN_PURPOSE_TAG);
@@ -80,7 +80,7 @@ TEST(GuardedMemory, basic) {
8080
}
8181

8282
// Test a number of odd sizes
83-
TEST(GuardedMemory, odd_sizes) {
83+
TEST_VM(GuardedMemory, odd_sizes) {
8484
u_char* basep =
8585
(u_char*) os::malloc(GuardedMemory::get_total_size(1), mtInternal);
8686
GuardedMemory guarded(basep, 1, GEN_PURPOSE_TAG);
@@ -101,7 +101,7 @@ TEST(GuardedMemory, odd_sizes) {
101101
}
102102

103103
// Test buffer overrun into head...
104-
TEST(GuardedMemory, buffer_overrun_head) {
104+
TEST_VM(GuardedMemory, buffer_overrun_head) {
105105
u_char* basep =
106106
(u_char*) os::malloc(GuardedMemory::get_total_size(1), mtInternal);
107107
GuardedMemory guarded(basep, 1, GEN_PURPOSE_TAG);
@@ -113,7 +113,7 @@ TEST(GuardedMemory, buffer_overrun_head) {
113113
}
114114

115115
// Test buffer overrun into tail with a number of odd sizes
116-
TEST(GuardedMemory, buffer_overrun_tail) {
116+
TEST_VM(GuardedMemory, buffer_overrun_tail) {
117117
u_char* basep =
118118
(u_char*) os::malloc(GuardedMemory::get_total_size(1), mtInternal);
119119
GuardedMemory guarded(basep, 1, GEN_PURPOSE_TAG);
@@ -130,7 +130,7 @@ TEST(GuardedMemory, buffer_overrun_tail) {
130130
}
131131

132132
// Test wrap_copy/wrap_free
133-
TEST(GuardedMemory, wrap) {
133+
TEST_VM(GuardedMemory, wrap) {
134134
EXPECT_TRUE(GuardedMemory::free_copy(NULL)) << "Expected free NULL to be OK";
135135

136136
const char* str = "Check my bounds out";
@@ -148,3 +148,10 @@ TEST(GuardedMemory, wrap) {
148148
EXPECT_TRUE(GuardedMemory::free_copy(no_data_copy))
149149
<< "Expected valid guards even for no data copy";
150150
}
151+
152+
// Test passing back a bogus GuardedMemory region
153+
TEST_VM(GuardedMemory, unmapped) {
154+
char* unmapped_base = (char*) (GuardedMemoryTest::get_guard_header_size() + 0x1000 + 1); // Avoids assert in constructor
155+
GuardedMemory guarded(unmapped_base);
156+
EXPECT_FALSE(guarded.verify_guards()) << "Guard was not broken as expected";
157+
}

0 commit comments

Comments
 (0)