Skip to content

Commit f6bdccb

Browse files
author
Doug Simon
committedJun 30, 2023
8310829: guarantee(!HAS_PENDING_EXCEPTION) failed in ExceptionTranslation::doit
Reviewed-by: never, kvn
1 parent 456bf11 commit f6bdccb

File tree

6 files changed

+155
-53
lines changed

6 files changed

+155
-53
lines changed
 

‎src/hotspot/share/classfile/vmClassMacros.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
#define VM_CLASS_ID(kname) vmClassID::_VM_CLASS_ENUM(kname)
3232

3333
// VM_CLASSES_DO iterates the classes that are directly referenced
34-
// by the VM, suhch as java.lang.Object and java.lang.String. These
34+
// by the VM, such as java.lang.Object and java.lang.String. These
3535
// classes are resolved at VM bootstrap, before any Java code is executed,
3636
// so no class loader is able to provide a different definition.
3737
//

‎src/hotspot/share/classfile/vmSymbols.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,7 @@
757757
template(decodeAndThrowThrowable_name, "decodeAndThrowThrowable") \
758758
template(encodeAnnotations_name, "encodeAnnotations") \
759759
template(encodeAnnotations_signature, "([BLjava/lang/Class;Ljdk/internal/reflect/ConstantPool;Z[Ljava/lang/Class;)[B")\
760-
template(decodeAndThrowThrowable_signature, "(JZ)V") \
760+
template(decodeAndThrowThrowable_signature, "(IJZ)V") \
761761
template(classRedefinedCount_name, "classRedefinedCount") \
762762
template(classLoader_name, "classLoader") \
763763
template(componentType_name, "componentType") \

‎src/hotspot/share/jvmci/jvmciCompilerToVM.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ C2V_VMENTRY_NULL(jobject, lookupType, (JNIEnv* env, jobject, jstring jname, ARGU
582582
TempNewSymbol class_name = SymbolTable::new_symbol(str);
583583

584584
if (class_name->utf8_length() <= 1) {
585-
JVMCI_THROW_MSG_0(InternalError, err_msg("Primitive type %s should be handled in Java code", class_name->as_C_string()));
585+
JVMCI_THROW_MSG_0(InternalError, err_msg("Primitive type %s should be handled in Java code", str));
586586
}
587587

588588
JVMCIKlassHandle resolved_klass(THREAD);

‎src/hotspot/share/jvmci/jvmciEnv.cpp

+54-31
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,15 @@ bool JVMCIEnv::pending_exception_as_string(const char** to_string, const char**
374374
// Shared code for translating an exception from HotSpot to libjvmci or vice versa.
375375
class ExceptionTranslation: public StackObj {
376376
protected:
377+
enum DecodeFormat {
378+
_encoded_ok = 0, // exception was successfully encoded into buffer
379+
_buffer_alloc_fail = 1, // native memory for buffer could not be allocated
380+
_encode_oome_fail = 2, // OutOfMemoryError thrown during encoding
381+
_encode_fail = 3 // some other problem occured during encoding. If buffer != 0,
382+
// buffer contains a `struct { u4 len; char[len] desc}`
383+
// describing the problem
384+
};
385+
377386
JVMCIEnv* _from_env; // Source of translation. Can be null.
378387
JVMCIEnv* _to_env; // Destination of translation. Never null.
379388

@@ -382,49 +391,34 @@ class ExceptionTranslation: public StackObj {
382391
// Encodes the exception in `_from_env` into `buffer`.
383392
// Where N is the number of bytes needed for the encoding, returns N if N <= `buffer_size`
384393
// and the encoding was written to `buffer` otherwise returns -N.
385-
virtual int encode(JavaThread* THREAD, Klass* vmSupport, jlong buffer, int buffer_size) = 0;
394+
virtual int encode(JavaThread* THREAD, jlong buffer, int buffer_size) = 0;
386395

387396
// Decodes the exception in `buffer` in `_to_env` and throws it.
388-
virtual void decode(JavaThread* THREAD, Klass* vmSupport, jlong buffer) = 0;
397+
virtual void decode(JavaThread* THREAD, DecodeFormat format, jlong buffer) = 0;
389398

390399
public:
391400
void doit(JavaThread* THREAD) {
392-
// Resolve VMSupport class explicitly as HotSpotJVMCI::compute_offsets
393-
// may not have been called.
394-
Klass* vmSupport = SystemDictionary::resolve_or_fail(vmSymbols::jdk_internal_vm_VMSupport(), true, THREAD);
395-
guarantee(!HAS_PENDING_EXCEPTION, "");
396-
397401
int buffer_size = 2048;
398402
while (true) {
399403
ResourceMark rm;
400404
jlong buffer = (jlong) NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(THREAD, jbyte, buffer_size);
401405
if (buffer == 0L) {
402-
decode(THREAD, vmSupport, 0L);
406+
JVMCI_event_1("error translating exception: translation buffer allocation failed");
407+
decode(THREAD, _buffer_alloc_fail, 0L);
403408
return;
404409
}
405-
int res = encode(THREAD, vmSupport, buffer, buffer_size);
406-
if (_from_env != nullptr && !_from_env->is_hotspot() && _from_env->has_pending_exception()) {
407-
// Cannot get name of exception thrown by `encode` as that involves
408-
// calling into libjvmci which in turn can raise another exception.
409-
_from_env->clear_pending_exception();
410-
decode(THREAD, vmSupport, -2L);
411-
return;
412-
} else if (HAS_PENDING_EXCEPTION) {
413-
Symbol *ex_name = PENDING_EXCEPTION->klass()->name();
414-
CLEAR_PENDING_EXCEPTION;
415-
if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) {
416-
decode(THREAD, vmSupport, -1L);
417-
} else {
418-
decode(THREAD, vmSupport, -2L);
419-
}
410+
int res = encode(THREAD, buffer, buffer_size);
411+
if (_to_env->has_pending_exception()) {
412+
// Propagate pending exception
420413
return;
421-
} else if (res < 0) {
414+
}
415+
if (res < 0) {
422416
int required_buffer_size = -res;
423417
if (required_buffer_size > buffer_size) {
424418
buffer_size = required_buffer_size;
425419
}
426420
} else {
427-
decode(THREAD, vmSupport, buffer);
421+
decode(THREAD, _encoded_ok, buffer);
428422
if (!_to_env->has_pending_exception()) {
429423
_to_env->throw_InternalError("decodeAndThrowThrowable should have thrown an exception");
430424
}
@@ -439,7 +433,26 @@ class HotSpotToSharedLibraryExceptionTranslation : public ExceptionTranslation {
439433
private:
440434
const Handle& _throwable;
441435

442-
int encode(JavaThread* THREAD, Klass* vmSupport, jlong buffer, int buffer_size) {
436+
int encode(JavaThread* THREAD, jlong buffer, int buffer_size) {
437+
Klass* vmSupport = SystemDictionary::resolve_or_fail(vmSymbols::jdk_internal_vm_VMSupport(), true, THREAD);
438+
if (HAS_PENDING_EXCEPTION) {
439+
Handle throwable = Handle(THREAD, PENDING_EXCEPTION);
440+
Symbol *ex_name = throwable->klass()->name();
441+
CLEAR_PENDING_EXCEPTION;
442+
if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) {
443+
JVMCI_event_1("error translating exception: OutOfMemoryError");
444+
decode(THREAD, _encode_oome_fail, 0L);
445+
} else {
446+
char* char_buffer = (char*) buffer + 4;
447+
stringStream st(char_buffer, (size_t) buffer_size - 4);
448+
java_lang_Throwable::print_stack_trace(throwable, &st);
449+
u4 len = (u4) st.size();
450+
*((u4*) buffer) = len;
451+
JVMCI_event_1("error translating exception: %s", char_buffer);
452+
decode(THREAD, _encode_fail, buffer);
453+
}
454+
return 0;
455+
}
443456
JavaCallArguments jargs;
444457
jargs.push_oop(_throwable);
445458
jargs.push_long(buffer);
@@ -452,11 +465,11 @@ class HotSpotToSharedLibraryExceptionTranslation : public ExceptionTranslation {
452465
return result.get_jint();
453466
}
454467

455-
void decode(JavaThread* THREAD, Klass* vmSupport, jlong buffer) {
468+
void decode(JavaThread* THREAD, DecodeFormat format, jlong buffer) {
456469
JNIAccessMark jni(_to_env, THREAD);
457470
jni()->CallStaticVoidMethod(JNIJVMCI::VMSupport::clazz(),
458471
JNIJVMCI::VMSupport::decodeAndThrowThrowable_method(),
459-
buffer, false);
472+
format, buffer, false);
460473
}
461474
public:
462475
HotSpotToSharedLibraryExceptionTranslation(JVMCIEnv* hotspot_env, JVMCIEnv* jni_env, const Handle& throwable) :
@@ -468,15 +481,25 @@ class SharedLibraryToHotSpotExceptionTranslation : public ExceptionTranslation {
468481
private:
469482
jthrowable _throwable;
470483

471-
int encode(JavaThread* THREAD, Klass* vmSupport, jlong buffer, int buffer_size) {
484+
int encode(JavaThread* THREAD, jlong buffer, int buffer_size) {
472485
JNIAccessMark jni(_from_env, THREAD);
473-
return jni()->CallStaticIntMethod(JNIJVMCI::VMSupport::clazz(),
486+
int res = jni()->CallStaticIntMethod(JNIJVMCI::VMSupport::clazz(),
474487
JNIJVMCI::VMSupport::encodeThrowable_method(),
475488
_throwable, buffer, buffer_size);
489+
if (jni()->ExceptionCheck()) {
490+
// Cannot get name of exception thrown as that can raise another exception.
491+
jni()->ExceptionClear();
492+
JVMCI_event_1("error translating exception: unknown error");
493+
decode(THREAD, _encode_fail, 0L);
494+
return 0;
495+
}
496+
return res;
476497
}
477498

478-
void decode(JavaThread* THREAD, Klass* vmSupport, jlong buffer) {
499+
void decode(JavaThread* THREAD, DecodeFormat format, jlong buffer) {
500+
Klass* vmSupport = SystemDictionary::resolve_or_fail(vmSymbols::jdk_internal_vm_VMSupport(), true, CHECK);
479501
JavaCallArguments jargs;
502+
jargs.push_int(format);
480503
jargs.push_long(buffer);
481504
jargs.push_int(true);
482505
JavaValue result(T_VOID);

‎src/java.base/share/classes/jdk/internal/vm/VMSupport.java

+29-18
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import java.io.IOException;
4141
import java.lang.annotation.Annotation;
4242
import java.lang.annotation.IncompleteAnnotationException;
43+
import java.nio.charset.StandardCharsets;
4344
import java.util.Collection;
4445
import java.util.LinkedHashMap;
4546
import java.util.Map;
@@ -112,36 +113,46 @@ public static byte[] serializeAgentPropertiesToByteArray() throws IOException {
112113
public static native String getVMTemporaryDirectory();
113114

114115
/**
115-
* Decodes the exception encoded in {@code errorOrBuffer} and throws it.
116-
*
117-
* @param errorOrBuffer an error code or a native byte errorOrBuffer containing an exception encoded by
118-
* {@link #encodeThrowable}. Error code values and their meanings are:
116+
* Decodes the exception described by {@code format} and {@code buffer} and throws it.
119117
*
118+
* @param format specifies how to interpret {@code buffer}:
120119
* <pre>
121-
* 0: native memory for the errorOrBuffer could not be allocated
122-
* -1: an OutOfMemoryError was thrown while encoding the exception
123-
* -2: some other throwable was thrown while encoding the exception
120+
* 0: {@code buffer} was created by {@link #encodeThrowable}
121+
* 1: native memory for {@code buffer} could not be allocated
122+
* 2: an OutOfMemoryError was thrown while encoding the exception
123+
* 3: some other problem occured while encoding the exception. If {@code buffer != 0},
124+
* it contains a {@code struct { u4 len; char[len] desc}} where {@code desc} describes the problem
124125
* </pre>
125-
* @param errorOrBuffer a native byte errorOrBuffer containing an exception encoded by
126-
* {@link #encodeThrowable}
126+
* @param buffer encoded info about the exception to throw (depends on {@code format})
127127
* @param inJVMHeap [@code true} if executing in the JVM heap, {@code false} otherwise
128128
*/
129-
public static void decodeAndThrowThrowable(long errorOrBuffer, boolean inJVMHeap) throws Throwable {
130-
if (errorOrBuffer >= -2L && errorOrBuffer <= 0) {
129+
public static void decodeAndThrowThrowable(int format, long buffer, boolean inJVMHeap) throws Throwable {
130+
if (format != 0) {
131131
String context = String.format("while encoding an exception to translate it %s the JVM heap",
132132
inJVMHeap ? "to" : "from");
133-
if (errorOrBuffer == 0) {
134-
throw new InternalError("native errorOrBuffer could not be allocated " + context);
133+
if (format == 1) {
134+
throw new InternalError("native buffer could not be allocated " + context);
135135
}
136-
if (errorOrBuffer == -1L) {
136+
if (format == 2) {
137137
throw new OutOfMemoryError("OutOfMemoryError occurred " + context);
138138
}
139+
if (format == 3 && buffer != 0L) {
140+
byte[] bytes = bufferToBytes(buffer);
141+
throw new InternalError("unexpected problem occurred " + context + ": " + new String(bytes, StandardCharsets.UTF_8));
142+
}
139143
throw new InternalError("unexpected problem occurred " + context);
140144
}
141-
int encodingLength = U.getInt(errorOrBuffer);
142-
byte[] encoding = new byte[encodingLength];
143-
U.copyMemory(null, errorOrBuffer + 4, encoding, Unsafe.ARRAY_BYTE_BASE_OFFSET, encodingLength);
144-
throw TranslatedException.decodeThrowable(encoding);
145+
throw TranslatedException.decodeThrowable(bufferToBytes(buffer));
146+
}
147+
148+
private static byte[] bufferToBytes(long buffer) {
149+
if (buffer == 0) {
150+
return null;
151+
}
152+
int len = U.getInt(buffer);
153+
byte[] bytes = new byte[len];
154+
U.copyMemory(null, buffer + 4, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
155+
return bytes;
145156
}
146157

147158
/**

‎test/jdk/jdk/internal/vm/TestTranslatedException.java

+69-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import java.io.PrintStream;
3535
import java.lang.reflect.InvocationTargetException;
3636
import java.lang.reflect.Method;
37+
import java.nio.charset.StandardCharsets;
3738

3839
import org.testng.Assert;
3940
import org.testng.annotations.Test;
@@ -57,11 +58,78 @@ public void encodeDecodeTest() throws Exception {
5758
throwable = new ExceptionInInitializerError(new InvocationTargetException(new RuntimeException(String.valueOf(i), throwable), "invoke"));
5859
}
5960
encodeDecode(throwable);
61+
62+
try {
63+
VMSupport.decodeAndThrowThrowable(0, 0L, true);
64+
throw new AssertionError("expected decodeAndThrowThrowable to throw an exception");
65+
} catch (NullPointerException decoded) {
66+
// Expected
67+
} catch (Throwable decoded) {
68+
throw new AssertionError("unexpected exception: " + decoded);
69+
}
70+
71+
try {
72+
VMSupport.decodeAndThrowThrowable(1, 0L, true);
73+
throw new AssertionError("expected decodeAndThrowThrowable to throw an exception");
74+
} catch (InternalError decoded) {
75+
if (!decoded.getMessage().startsWith("native buffer could not be allocated")) {
76+
throw new AssertionError("unexpected exception: " + decoded);
77+
}
78+
} catch (Throwable decoded) {
79+
throw new AssertionError("unexpected exception: " + decoded);
80+
}
81+
82+
try {
83+
VMSupport.decodeAndThrowThrowable(2, 0L, true);
84+
throw new AssertionError("expected decodeAndThrowThrowable to throw an exception");
85+
} catch (OutOfMemoryError decoded) {
86+
// Expected
87+
} catch (Throwable decoded) {
88+
throw new AssertionError("unexpected exception: " + decoded);
89+
}
90+
91+
try {
92+
VMSupport.decodeAndThrowThrowable(3, 0L, true);
93+
throw new AssertionError("expected decodeAndThrowThrowable to throw an exception");
94+
} catch (InternalError decoded) {
95+
// Expected
96+
} catch (Throwable decoded) {
97+
throw new AssertionError("unexpected exception: " + decoded);
98+
}
99+
100+
try {
101+
VMSupport.decodeAndThrowThrowable(4, 0L, true);
102+
throw new AssertionError("expected decodeAndThrowThrowable to throw an exception");
103+
} catch (InternalError decoded) {
104+
// Expected
105+
} catch (Throwable decoded) {
106+
throw new AssertionError("unexpected exception: " + decoded);
107+
}
108+
109+
Unsafe unsafe = Unsafe.getUnsafe();
110+
byte[] problem = "very unlikely problem".getBytes(StandardCharsets.UTF_8);
111+
long buffer = unsafe.allocateMemory(problem.length + 4);
112+
try {
113+
unsafe.putInt(buffer, problem.length);
114+
unsafe.copyMemory(problem, Unsafe.ARRAY_BYTE_BASE_OFFSET, null, buffer + 4, problem.length);
115+
VMSupport.decodeAndThrowThrowable(3, buffer, true);
116+
throw new AssertionError("expected decodeAndThrowThrowable to throw an exception");
117+
} catch (InternalError decoded) {
118+
String msg = decoded.getMessage();
119+
if (!msg.endsWith("very unlikely problem")) {
120+
throw new AssertionError("unexpected exception: " + decoded);
121+
}
122+
} catch (Throwable decoded) {
123+
throw new AssertionError("unexpected exception: " + decoded);
124+
} finally {
125+
unsafe.freeMemory(buffer);
126+
}
60127
}
61128

62129
private void encodeDecode(Throwable throwable) throws Exception {
63130
Unsafe unsafe = Unsafe.getUnsafe();
64131
int bufferSize = 512;
132+
int format = 0;
65133
long buffer = 0L;
66134
while (true) {
67135
buffer = unsafe.allocateMemory(bufferSize);
@@ -71,7 +139,7 @@ private void encodeDecode(Throwable throwable) throws Exception {
71139
bufferSize = -res;
72140
} else {
73141
try {
74-
VMSupport.decodeAndThrowThrowable(buffer, true);
142+
VMSupport.decodeAndThrowThrowable(format, buffer, true);
75143
throw new AssertionError("expected decodeAndThrowThrowable to throw an exception");
76144
} catch (Throwable decoded) {
77145
assertThrowableEquals(throwable, decoded);

0 commit comments

Comments
 (0)
Please sign in to comment.