Skip to content

Commit da0a51c

Browse files
author
David Holmes
committed
8357601: Checked version of JNI Release<type>ArrayElements needs to filter out known wrapped arrays
Reviewed-by: coleenp, jsjolen
1 parent 566279a commit da0a51c

File tree

6 files changed

+337
-22
lines changed

6 files changed

+337
-22
lines changed

src/hotspot/os/windows/safefetch_windows.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "sanitizers/address.hpp"
3030
#include "utilities/globalDefinitions.hpp"
3131

32+
#include <Windows.h>
3233
// On windows, we use structured exception handling to implement SafeFetch
3334

3435
template <class T>

src/hotspot/share/memory/guardedMemory.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@
2525
#include "nmt/memTag.hpp"
2626
#include "runtime/os.hpp"
2727

28-
void* GuardedMemory::wrap_copy(const void* ptr, const size_t len, const void* tag) {
28+
void* GuardedMemory::wrap_copy(const void* ptr, const size_t len,
29+
const void* tag, const void* tag2) {
2930
size_t total_sz = GuardedMemory::get_total_size(len);
3031
void* outerp = os::malloc(total_sz, mtInternal);
3132
if (outerp != nullptr) {
32-
GuardedMemory guarded(outerp, len, tag);
33+
GuardedMemory guarded(outerp, len, tag, tag2);
3334
void* innerp = guarded.get_user_ptr();
3435
if (ptr != nullptr) {
3536
memcpy(innerp, ptr, len);
@@ -58,8 +59,8 @@ void GuardedMemory::print_on(outputStream* st) const {
5859
return;
5960
}
6061
st->print_cr("GuardedMemory(" PTR_FORMAT ") base_addr=" PTR_FORMAT
61-
" tag=" PTR_FORMAT " user_size=%zu user_data=" PTR_FORMAT,
62-
p2i(this), p2i(_base_addr), p2i(get_tag()), get_user_size(), p2i(get_user_ptr()));
62+
" tag=" PTR_FORMAT " tag2=" PTR_FORMAT " user_size=%zu user_data=" PTR_FORMAT,
63+
p2i(this), p2i(_base_addr), p2i(get_tag()), p2i(get_tag2()), get_user_size(), p2i(get_user_ptr()));
6364

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

src/hotspot/share/memory/guardedMemory.hpp

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2014, 2023, 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/safefetch.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
@@ -114,7 +116,11 @@ class GuardedMemory : StackObj { // Wrapper on stack
114116
u_char* c = (u_char*) _guard;
115117
u_char* end = c + GUARD_SIZE;
116118
while (c < end) {
117-
if (*c != badResourceValue) {
119+
// We may not be able to dereference directly so use
120+
// SafeFetch. It doesn't matter if the value read happens
121+
// to be 0xFF as that is not what we expect anyway.
122+
u_char val = (u_char) SafeFetch32((int*)c, 0xFF);
123+
if (val != badResourceValue) {
118124
return false;
119125
}
120126
c++;
@@ -137,13 +143,18 @@ 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; }
156+
157+
147158
}; // GuardedMemory::GuardHeader
148159

149160
// Guarded Memory...
@@ -162,9 +173,11 @@ class GuardedMemory : StackObj { // Wrapper on stack
162173
* @param base_ptr allocation wishing to be wrapped, must be at least "GuardedMemory::get_total_size()" bytes.
163174
* @param user_size the size of the user data to be wrapped.
164175
* @param tag optional general purpose tag.
176+
* @param tag2 optional second general purpose tag.
165177
*/
166-
GuardedMemory(void* base_ptr, const size_t user_size, const void* tag = nullptr) {
167-
wrap_with_guards(base_ptr, user_size, tag);
178+
GuardedMemory(void* base_ptr, const size_t user_size,
179+
const void* tag = nullptr, const void* tag2 = nullptr) {
180+
wrap_with_guards(base_ptr, user_size, tag, tag2);
168181
}
169182

170183
/**
@@ -189,16 +202,19 @@ class GuardedMemory : StackObj { // Wrapper on stack
189202
* @param base_ptr allocation wishing to be wrapped, must be at least "GuardedMemory::get_total_size()" bytes.
190203
* @param user_size the size of the user data to be wrapped.
191204
* @param tag optional general purpose tag.
205+
* @param tag2 optional second general purpose tag.
192206
*
193207
* @return user data pointer (inner pointer to supplied "base_ptr").
194208
*/
195-
void* wrap_with_guards(void* base_ptr, size_t user_size, const void* tag = nullptr) {
209+
void* wrap_with_guards(void* base_ptr, size_t user_size,
210+
const void* tag = nullptr, const void* tag2 = nullptr) {
196211
assert(base_ptr != nullptr, "Attempt to wrap null with memory guard");
197212
_base_addr = (u_char*)base_ptr;
198213
get_head_guard()->build();
199214
get_head_guard()->set_user_size(user_size);
200215
get_tail_guard()->build();
201216
set_tag(tag);
217+
set_tag2(tag2);
202218
set_user_bytes(uninitBlockPad);
203219
assert(verify_guards(), "Expected valid memory guards");
204220
return get_user_ptr();
@@ -230,6 +246,20 @@ class GuardedMemory : StackObj { // Wrapper on stack
230246
*/
231247
void* get_tag() const { return get_head_guard()->get_tag(); }
232248

249+
/**
250+
* Set the second general purpose tag.
251+
*
252+
* @param tag general purpose tag.
253+
*/
254+
void set_tag2(const void* tag) { get_head_guard()->set_tag2(tag); }
255+
256+
/**
257+
* Return the second general purpose tag.
258+
*
259+
* @return the second general purpose tag, defaults to null.
260+
*/
261+
void* get_tag2() const { return get_head_guard()->get_tag2(); }
262+
233263
/**
234264
* Return the size of the user data.
235265
*
@@ -302,10 +332,12 @@ class GuardedMemory : StackObj { // Wrapper on stack
302332
* @param ptr the memory to be copied
303333
* @param len the length of the copy
304334
* @param tag optional general purpose tag (see GuardedMemory::get_tag())
335+
* @param tag2 optional general purpose tag (see GuardedMemory::get_tag2())
305336
*
306337
* @return guarded wrapped memory pointer to the user area, or null if OOM.
307338
*/
308-
static void* wrap_copy(const void* p, const size_t len, const void* tag = nullptr);
339+
static void* wrap_copy(const void* p, const size_t len,
340+
const void* tag = nullptr, const void* tag2 = nullptr);
309341

310342
/**
311343
* Free wrapped copy.

src/hotspot/share/prims/jniCheck.cpp

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

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

369378
static void* check_wrapped_array(JavaThread* thr, const char* fn_name,
370-
void* obj, void* carray, size_t* rsz) {
379+
void* obj, void* carray, size_t* rsz, jboolean is_critical) {
371380
if (carray == nullptr) {
372381
tty->print_cr("%s: elements vector null" PTR_FORMAT, fn_name, p2i(obj));
373382
NativeReportJNIFatalError(thr, "Elements vector null");
@@ -386,6 +395,29 @@ static void* check_wrapped_array(JavaThread* thr, const char* fn_name,
386395
DEBUG_ONLY(guarded.print_on(tty);) // This may crash.
387396
NativeReportJNIFatalError(thr, err_msg("%s: unrecognized elements", fn_name));
388397
}
398+
if (orig_result == STRING_TAG || orig_result == STRING_UTF_TAG) {
399+
bool was_utf = orig_result == STRING_UTF_TAG;
400+
tty->print_cr("%s: called on something allocated by %s",
401+
fn_name, was_utf ? "GetStringUTFChars" : "GetStringChars");
402+
DEBUG_ONLY(guarded.print_on(tty);) // This may crash.
403+
NativeReportJNIFatalError(thr, err_msg("%s called on something allocated by %s",
404+
fn_name, was_utf ? "GetStringUTFChars" : "GetStringChars"));
405+
}
406+
407+
if (is_critical && (guarded.get_tag2() != CRITICAL_TAG)) {
408+
tty->print_cr("%s: called on something not allocated by GetPrimitiveArrayCritical", fn_name);
409+
DEBUG_ONLY(guarded.print_on(tty);) // This may crash.
410+
NativeReportJNIFatalError(thr, err_msg("%s called on something not allocated by GetPrimitiveArrayCritical",
411+
fn_name));
412+
}
413+
414+
if (!is_critical && (guarded.get_tag2() == CRITICAL_TAG)) {
415+
tty->print_cr("%s: called on something allocated by GetPrimitiveArrayCritical", fn_name);
416+
DEBUG_ONLY(guarded.print_on(tty);) // This may crash.
417+
NativeReportJNIFatalError(thr, err_msg("%s called on something allocated by GetPrimitiveArrayCritical",
418+
fn_name));
419+
}
420+
389421
if (rsz != nullptr) {
390422
*rsz = guarded.get_user_size();
391423
}
@@ -395,7 +427,7 @@ static void* check_wrapped_array(JavaThread* thr, const char* fn_name,
395427
static void* check_wrapped_array_release(JavaThread* thr, const char* fn_name,
396428
void* obj, void* carray, jint mode, jboolean is_critical) {
397429
size_t sz;
398-
void* orig_result = check_wrapped_array(thr, fn_name, obj, carray, &sz);
430+
void* orig_result = check_wrapped_array(thr, fn_name, obj, carray, &sz, is_critical);
399431
switch (mode) {
400432
case 0:
401433
memcpy(orig_result, carray, sz);
@@ -1430,9 +1462,6 @@ JNI_ENTRY_CHECKED(jsize,
14301462
return result;
14311463
JNI_END
14321464

1433-
// Arbitrary (but well-known) tag
1434-
const void* STRING_TAG = (void*)0x47114711;
1435-
14361465
JNI_ENTRY_CHECKED(const jchar *,
14371466
checked_jni_GetStringChars(JNIEnv *env,
14381467
jstring str,
@@ -1535,9 +1564,6 @@ JNI_ENTRY_CHECKED(jlong,
15351564
return result;
15361565
JNI_END
15371566

1538-
// Arbitrary (but well-known) tag - different than GetStringChars
1539-
const void* STRING_UTF_TAG = (void*) 0x48124812;
1540-
15411567
JNI_ENTRY_CHECKED(const char *,
15421568
checked_jni_GetStringUTFChars(JNIEnv *env,
15431569
jstring str,
@@ -1859,7 +1885,7 @@ JNI_ENTRY_CHECKED(void *,
18591885
)
18601886
void *result = UNCHECKED()->GetPrimitiveArrayCritical(env, array, isCopy);
18611887
if (result != nullptr) {
1862-
result = check_jni_wrap_copy_array(thr, array, result);
1888+
result = check_jni_wrap_copy_array(thr, array, result, JNI_TRUE);
18631889
}
18641890
functionExit(thr);
18651891
return result;

0 commit comments

Comments
 (0)