Wrap certain exception types when loading an erroneous class.
Bug: 28787733
Change-Id: I22e1e4339a95706a0fb9c1474b360e9bde232539
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 5d4bca1..86fef28 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -641,6 +641,12 @@
REQUIRES(!Locks::classlinker_classes_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
+ // Throw the class initialization failure recorded when first trying to initialize the given
+ // class.
+ void ThrowEarlierClassFailure(mirror::Class* c, bool wrap_in_no_class_def = false)
+ SHARED_REQUIRES(Locks::mutator_lock_)
+ REQUIRES(!dex_lock_);
+
struct DexCacheData {
// Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may
// not work properly.
@@ -1057,12 +1063,6 @@
// Return the quick generic JNI stub for testing.
const void* GetRuntimeQuickGenericJniStub() const;
- // Throw the class initialization failure recorded when first trying to initialize the given
- // class.
- void ThrowEarlierClassFailure(mirror::Class* c, bool wrap_in_no_class_def = false)
- SHARED_REQUIRES(Locks::mutator_lock_)
- REQUIRES(!dex_lock_);
-
bool CanWeInitializeClass(mirror::Class* klass, bool can_init_statics, bool can_init_parents)
SHARED_REQUIRES(Locks::mutator_lock_);
diff --git a/runtime/native/java_lang_VMClassLoader.cc b/runtime/native/java_lang_VMClassLoader.cc
index 1515630..6f735aa 100644
--- a/runtime/native/java_lang_VMClassLoader.cc
+++ b/runtime/native/java_lang_VMClassLoader.cc
@@ -41,6 +41,23 @@
if (c != nullptr && c->IsResolved()) {
return soa.AddLocalReference<jclass>(c);
}
+ // If class is erroneous, throw the earlier failure, wrapped in certain cases. See b/28787733.
+ if (c != nullptr && c->IsErroneous()) {
+ cl->ThrowEarlierClassFailure(c);
+ Thread* self = soa.Self();
+ mirror::Class* eiie_class =
+ self->DecodeJObject(WellKnownClasses::java_lang_ExceptionInInitializerError)->AsClass();
+ mirror::Class* iae_class =
+ self->DecodeJObject(WellKnownClasses::java_lang_IllegalAccessError)->AsClass();
+ mirror::Class* ncdfe_class =
+ self->DecodeJObject(WellKnownClasses::java_lang_NoClassDefFoundError)->AsClass();
+ mirror::Class* exception = self->GetException()->GetClass();
+ if (exception == eiie_class || exception == iae_class || exception == ncdfe_class) {
+ self->ThrowNewWrappedException("Ljava/lang/ClassNotFoundException;",
+ PrettyDescriptor(c).c_str());
+ }
+ return nullptr;
+ }
if (loader != nullptr) {
// Try the common case.
StackHandleScope<1> hs(soa.Self());
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index d288943..355d552 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -41,6 +41,9 @@
jclass WellKnownClasses::java_lang_ClassNotFoundException;
jclass WellKnownClasses::java_lang_Daemons;
jclass WellKnownClasses::java_lang_Error;
+jclass WellKnownClasses::java_lang_ExceptionInInitializerError;
+jclass WellKnownClasses::java_lang_IllegalAccessError;
+jclass WellKnownClasses::java_lang_NoClassDefFoundError;
jclass WellKnownClasses::java_lang_Object;
jclass WellKnownClasses::java_lang_OutOfMemoryError;
jclass WellKnownClasses::java_lang_reflect_AbstractMethod;
@@ -228,6 +231,9 @@
java_lang_Object = CacheClass(env, "java/lang/Object");
java_lang_OutOfMemoryError = CacheClass(env, "java/lang/OutOfMemoryError");
java_lang_Error = CacheClass(env, "java/lang/Error");
+ java_lang_ExceptionInInitializerError = CacheClass(env, "java/lang/ExceptionInInitializerError");
+ java_lang_IllegalAccessError = CacheClass(env, "java/lang/IllegalAccessError");
+ java_lang_NoClassDefFoundError = CacheClass(env, "java/lang/NoClassDefFoundError");
java_lang_reflect_AbstractMethod = CacheClass(env, "java/lang/reflect/AbstractMethod");
java_lang_reflect_Constructor = CacheClass(env, "java/lang/reflect/Constructor");
java_lang_reflect_Field = CacheClass(env, "java/lang/reflect/Field");
diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h
index 482ff0a..cc60b4d 100644
--- a/runtime/well_known_classes.h
+++ b/runtime/well_known_classes.h
@@ -52,6 +52,9 @@
static jclass java_lang_ClassNotFoundException;
static jclass java_lang_Daemons;
static jclass java_lang_Error;
+ static jclass java_lang_ExceptionInInitializerError;
+ static jclass java_lang_IllegalAccessError;
+ static jclass java_lang_NoClassDefFoundError;
static jclass java_lang_Object;
static jclass java_lang_OutOfMemoryError;
static jclass java_lang_reflect_AbstractMethod;
diff --git a/test/142-classloader2/smali/B.smali b/test/142-classloader2/smali/B.smali
new file mode 100644
index 0000000..01bd593
--- /dev/null
+++ b/test/142-classloader2/smali/B.smali
@@ -0,0 +1,10 @@
+.class public LB;
+
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+ .registers 1
+ invoke-direct {p1}, Ljava/lang/Object;-><init>()V
+ return-void
+.end method
+
diff --git a/test/142-classloader2/src/Main.java b/test/142-classloader2/src/Main.java
index 86c61eb..89dadce 100644
--- a/test/142-classloader2/src/Main.java
+++ b/test/142-classloader2/src/Main.java
@@ -71,6 +71,21 @@
throw new IllegalStateException("Expected Ex-A, found " + exValue);
}
+ // Try to load a dex file with bad dex code. Use new instance to force verification.
+ try {
+ Class<?> badClass = Main.class.getClassLoader().loadClass("B");
+ badClass.newInstance();
+ System.out.println("Should not be able to load class from bad dex file.");
+ } catch (VerifyError e) {
+ }
+
+ // Make sure the same error is rethrown when reloading the bad class.
+ try {
+ Class<?> badClass = Main.class.getClassLoader().loadClass("B");
+ System.out.println("Should not be able to load class from bad dex file.");
+ } catch (VerifyError e) {
+ }
+
System.out.println("Everything OK.");
}
}