Skip to content
This repository was archived by the owner on Sep 19, 2023. It is now read-only.
/ jdk21 Public archive

Commit 6de4e8f

Browse files
committed
8310829: guarantee(!HAS_PENDING_EXCEPTION) failed in ExceptionTranslation::doit
Reviewed-by: chagedorn Backport-of: f6bdccb45caca0f69918a773a9ad9b2ad91b702f
1 parent 6f3f4aa commit 6de4e8f

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
@@ -760,7 +760,7 @@
760760
template(decodeAndThrowThrowable_name, "decodeAndThrowThrowable") \
761761
template(encodeAnnotations_name, "encodeAnnotations") \
762762
template(encodeAnnotations_signature, "([BLjava/lang/Class;Ljdk/internal/reflect/ConstantPool;Z[Ljava/lang/Class;)[B")\
763-
template(decodeAndThrowThrowable_signature, "(JZ)V") \
763+
template(decodeAndThrowThrowable_signature, "(IJZ)V") \
764764
template(classRedefinedCount_name, "classRedefinedCount") \
765765
template(classLoader_name, "classLoader") \
766766
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
@@ -446,6 +446,15 @@ bool JVMCIEnv::pending_exception_as_string(const char** to_string, const char**
446446
// Shared code for translating an exception from HotSpot to libjvmci or vice versa.
447447
class ExceptionTranslation: public StackObj {
448448
protected:
449+
enum DecodeFormat {
450+
_encoded_ok = 0, // exception was successfully encoded into buffer
451+
_buffer_alloc_fail = 1, // native memory for buffer could not be allocated
452+
_encode_oome_fail = 2, // OutOfMemoryError thrown during encoding
453+
_encode_fail = 3 // some other problem occured during encoding. If buffer != 0,
454+
// buffer contains a `struct { u4 len; char[len] desc}`
455+
// describing the problem
456+
};
457+
449458
JVMCIEnv* _from_env; // Source of translation. Can be null.
450459
JVMCIEnv* _to_env; // Destination of translation. Never null.
451460

@@ -454,49 +463,34 @@ class ExceptionTranslation: public StackObj {
454463
// Encodes the exception in `_from_env` into `buffer`.
455464
// Where N is the number of bytes needed for the encoding, returns N if N <= `buffer_size`
456465
// and the encoding was written to `buffer` otherwise returns -N.
457-
virtual int encode(JavaThread* THREAD, Klass* vmSupport, jlong buffer, int buffer_size) = 0;
466+
virtual int encode(JavaThread* THREAD, jlong buffer, int buffer_size) = 0;
458467

459468
// Decodes the exception in `buffer` in `_to_env` and throws it.
460-
virtual void decode(JavaThread* THREAD, Klass* vmSupport, jlong buffer) = 0;
469+
virtual void decode(JavaThread* THREAD, DecodeFormat format, jlong buffer) = 0;
461470

462471
public:
463472
void doit(JavaThread* THREAD) {
464-
// Resolve VMSupport class explicitly as HotSpotJVMCI::compute_offsets
465-
// may not have been called.
466-
Klass* vmSupport = SystemDictionary::resolve_or_fail(vmSymbols::jdk_internal_vm_VMSupport(), true, THREAD);
467-
guarantee(!HAS_PENDING_EXCEPTION, "");
468-
469473
int buffer_size = 2048;
470474
while (true) {
471475
ResourceMark rm;
472476
jlong buffer = (jlong) NEW_RESOURCE_ARRAY_IN_THREAD_RETURN_NULL(THREAD, jbyte, buffer_size);
473477
if (buffer == 0L) {
474-
decode(THREAD, vmSupport, 0L);
478+
JVMCI_event_1("error translating exception: translation buffer allocation failed");
479+
decode(THREAD, _buffer_alloc_fail, 0L);
475480
return;
476481
}
477-
int res = encode(THREAD, vmSupport, buffer, buffer_size);
478-
if (_from_env != nullptr && !_from_env->is_hotspot() && _from_env->has_pending_exception()) {
479-
// Cannot get name of exception thrown by `encode` as that involves
480-
// calling into libjvmci which in turn can raise another exception.
481-
_from_env->clear_pending_exception();
482-
decode(THREAD, vmSupport, -2L);
483-
return;
484-
} else if (HAS_PENDING_EXCEPTION) {
485-
Symbol *ex_name = PENDING_EXCEPTION->klass()->name();
486-
CLEAR_PENDING_EXCEPTION;
487-
if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) {
488-
decode(THREAD, vmSupport, -1L);
489-
} else {
490-
decode(THREAD, vmSupport, -2L);
491-
}
482+
int res = encode(THREAD, buffer, buffer_size);
483+
if (_to_env->has_pending_exception()) {
484+
// Propagate pending exception
492485
return;
493-
} else if (res < 0) {
486+
}
487+
if (res < 0) {
494488
int required_buffer_size = -res;
495489
if (required_buffer_size > buffer_size) {
496490
buffer_size = required_buffer_size;
497491
}
498492
} else {
499-
decode(THREAD, vmSupport, buffer);
493+
decode(THREAD, _encoded_ok, buffer);
500494
if (!_to_env->has_pending_exception()) {
501495
_to_env->throw_InternalError("decodeAndThrowThrowable should have thrown an exception");
502496
}
@@ -511,7 +505,26 @@ class HotSpotToSharedLibraryExceptionTranslation : public ExceptionTranslation {
511505
private:
512506
const Handle& _throwable;
513507

514-
int encode(JavaThread* THREAD, Klass* vmSupport, jlong buffer, int buffer_size) {
508+
int encode(JavaThread* THREAD, jlong buffer, int buffer_size) {
509+
Klass* vmSupport = SystemDictionary::resolve_or_fail(vmSymbols::jdk_internal_vm_VMSupport(), true, THREAD);
510+
if (HAS_PENDING_EXCEPTION) {
511+
Handle throwable = Handle(THREAD, PENDING_EXCEPTION);
512+
Symbol *ex_name = throwable->klass()->name();
513+
CLEAR_PENDING_EXCEPTION;
514+
if (ex_name == vmSymbols::java_lang_OutOfMemoryError()) {
515+
JVMCI_event_1("error translating exception: OutOfMemoryError");
516+
decode(THREAD, _encode_oome_fail, 0L);
517+
} else {
518+
char* char_buffer = (char*) buffer + 4;
519+
stringStream st(char_buffer, (size_t) buffer_size - 4);
520+
java_lang_Throwable::print_stack_trace(throwable, &st);
521+
u4 len = (u4) st.size();
522+
*((u4*) buffer) = len;
523+
JVMCI_event_1("error translating exception: %s", char_buffer);
524+
decode(THREAD, _encode_fail, buffer);
525+
}
526+
return 0;
527+
}
515528
JavaCallArguments jargs;
516529
jargs.push_oop(_throwable);
517530
jargs.push_long(buffer);
@@ -524,11 +537,11 @@ class HotSpotToSharedLibraryExceptionTranslation : public ExceptionTranslation {
524537
return result.get_jint();
525538
}
526539

527-
void decode(JavaThread* THREAD, Klass* vmSupport, jlong buffer) {
540+
void decode(JavaThread* THREAD, DecodeFormat format, jlong buffer) {
528541
JNIAccessMark jni(_to_env, THREAD);
529542
jni()->CallStaticVoidMethod(JNIJVMCI::VMSupport::clazz(),
530543
JNIJVMCI::VMSupport::decodeAndThrowThrowable_method(),
531-
buffer, false);
544+
format, buffer, false);
532545
}
533546
public:
534547
HotSpotToSharedLibraryExceptionTranslation(JVMCIEnv* hotspot_env, JVMCIEnv* jni_env, const Handle& throwable) :
@@ -540,15 +553,25 @@ class SharedLibraryToHotSpotExceptionTranslation : public ExceptionTranslation {
540553
private:
541554
jthrowable _throwable;
542555

543-
int encode(JavaThread* THREAD, Klass* vmSupport, jlong buffer, int buffer_size) {
556+
int encode(JavaThread* THREAD, jlong buffer, int buffer_size) {
544557
JNIAccessMark jni(_from_env, THREAD);
545-
return jni()->CallStaticIntMethod(JNIJVMCI::VMSupport::clazz(),
558+
int res = jni()->CallStaticIntMethod(JNIJVMCI::VMSupport::clazz(),
546559
JNIJVMCI::VMSupport::encodeThrowable_method(),
547560
_throwable, buffer, buffer_size);
561+
if (jni()->ExceptionCheck()) {
562+
// Cannot get name of exception thrown as that can raise another exception.
563+
jni()->ExceptionClear();
564+
JVMCI_event_1("error translating exception: unknown error");
565+
decode(THREAD, _encode_fail, 0L);
566+
return 0;
567+
}
568+
return res;
548569
}
549570

550-
void decode(JavaThread* THREAD, Klass* vmSupport, jlong buffer) {
571+
void decode(JavaThread* THREAD, DecodeFormat format, jlong buffer) {
572+
Klass* vmSupport = SystemDictionary::resolve_or_fail(vmSymbols::jdk_internal_vm_VMSupport(), true, CHECK);
551573
JavaCallArguments jargs;
574+
jargs.push_int(format);
552575
jargs.push_long(buffer);
553576
jargs.push_int(true);
554577
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;
@@ -125,36 +126,46 @@ public static byte[] serializeSavedPropertiesToByteArray() throws IOException {
125126
public static native String getVMTemporaryDirectory();
126127

127128
/**
128-
* Decodes the exception encoded in {@code errorOrBuffer} and throws it.
129-
*
130-
* @param errorOrBuffer an error code or a native byte errorOrBuffer containing an exception encoded by
131-
* {@link #encodeThrowable}. Error code values and their meanings are:
129+
* Decodes the exception described by {@code format} and {@code buffer} and throws it.
132130
*
131+
* @param format specifies how to interpret {@code buffer}:
133132
* <pre>
134-
* 0: native memory for the errorOrBuffer could not be allocated
135-
* -1: an OutOfMemoryError was thrown while encoding the exception
136-
* -2: some other throwable was thrown while encoding the exception
133+
* 0: {@code buffer} was created by {@link #encodeThrowable}
134+
* 1: native memory for {@code buffer} could not be allocated
135+
* 2: an OutOfMemoryError was thrown while encoding the exception
136+
* 3: some other problem occured while encoding the exception. If {@code buffer != 0},
137+
* it contains a {@code struct { u4 len; char[len] desc}} where {@code desc} describes the problem
137138
* </pre>
138-
* @param errorOrBuffer a native byte errorOrBuffer containing an exception encoded by
139-
* {@link #encodeThrowable}
139+
* @param buffer encoded info about the exception to throw (depends on {@code format})
140140
* @param inJVMHeap [@code true} if executing in the JVM heap, {@code false} otherwise
141141
*/
142-
public static void decodeAndThrowThrowable(long errorOrBuffer, boolean inJVMHeap) throws Throwable {
143-
if (errorOrBuffer >= -2L && errorOrBuffer <= 0) {
142+
public static void decodeAndThrowThrowable(int format, long buffer, boolean inJVMHeap) throws Throwable {
143+
if (format != 0) {
144144
String context = String.format("while encoding an exception to translate it %s the JVM heap",
145145
inJVMHeap ? "to" : "from");
146-
if (errorOrBuffer == 0) {
147-
throw new InternalError("native errorOrBuffer could not be allocated " + context);
146+
if (format == 1) {
147+
throw new InternalError("native buffer could not be allocated " + context);
148148
}
149-
if (errorOrBuffer == -1L) {
149+
if (format == 2) {
150150
throw new OutOfMemoryError("OutOfMemoryError occurred " + context);
151151
}
152+
if (format == 3 && buffer != 0L) {
153+
byte[] bytes = bufferToBytes(buffer);
154+
throw new InternalError("unexpected problem occurred " + context + ": " + new String(bytes, StandardCharsets.UTF_8));
155+
}
152156
throw new InternalError("unexpected problem occurred " + context);
153157
}
154-
int encodingLength = U.getInt(errorOrBuffer);
155-
byte[] encoding = new byte[encodingLength];
156-
U.copyMemory(null, errorOrBuffer + 4, encoding, Unsafe.ARRAY_BYTE_BASE_OFFSET, encodingLength);
157-
throw TranslatedException.decodeThrowable(encoding);
158+
throw TranslatedException.decodeThrowable(bufferToBytes(buffer));
159+
}
160+
161+
private static byte[] bufferToBytes(long buffer) {
162+
if (buffer == 0) {
163+
return null;
164+
}
165+
int len = U.getInt(buffer);
166+
byte[] bytes = new byte[len];
167+
U.copyMemory(null, buffer + 4, bytes, Unsafe.ARRAY_BYTE_BASE_OFFSET, len);
168+
return bytes;
158169
}
159170

160171
/**

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)