Fix for interpreter crash on new instance of class
Rationale:
Fuzz testing found divergences between the compiler and interpreter
which turned out to be caused by calling instance on java.lang.Class
(this worked for compiler but crashed interpeter). Since RI does not
allow this construct, solution is to force interpreter in this
unlikely case and throw run time exception like RI. This fixes
two cases found with fuzz testing. CL also includes
fail-before/pass-after test.
Test: 600-verifier-fails
BUG=29758098
Change-Id: Ie80f7758def44e6655d28fec4c10c34ffa0fd60b
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 7ecd595..68c954f 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -148,6 +148,11 @@
*slow_path = true;
return nullptr; // Failure
}
+ if (UNLIKELY(klass->IsClassClass())) {
+ ThrowIllegalAccessError(nullptr, "Class %s is inaccessible", PrettyDescriptor(klass).c_str());
+ *slow_path = true;
+ return nullptr; // Failure
+ }
mirror::Class* referrer = method->GetDeclaringClass();
if (UNLIKELY(!referrer->CanAccess(klass))) {
ThrowIllegalAccessErrorClass(referrer, klass);
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index f2ae85a..7373819 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -1257,6 +1257,10 @@
if (descriptor[0] != 'L') {
Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "can't call new-instance on type '" << descriptor << "'";
return false;
+ } else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) {
+ // An unlikely new instance on Class is not allowed. Fall back to interpreter to ensure an
+ // exception is thrown when this statement is executed (compiled code would not do that).
+ Fail(VERIFY_ERROR_INSTANTIATION);
}
return true;
}
diff --git a/test/600-verifier-fails/expected.txt b/test/600-verifier-fails/expected.txt
index eaa0c93..974b995 100644
--- a/test/600-verifier-fails/expected.txt
+++ b/test/600-verifier-fails/expected.txt
@@ -3,3 +3,4 @@
passed C
passed D
passed E
+passed F
diff --git a/test/600-verifier-fails/info.txt b/test/600-verifier-fails/info.txt
index df2396e..23f3ebc 100644
--- a/test/600-verifier-fails/info.txt
+++ b/test/600-verifier-fails/info.txt
@@ -17,4 +17,7 @@
later on
(E) b/29068831:
access validation on method should occur prior to null reference check
+(F) b/29758098:
+ new-instance of java.lang.Class should throw an IllegalAccessError to
+ avoid interpreter crash on zero size object later
diff --git a/test/600-verifier-fails/smali/class.smali b/test/600-verifier-fails/smali/class.smali
new file mode 100644
index 0000000..b2eb254
--- /dev/null
+++ b/test/600-verifier-fails/smali/class.smali
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LF;
+.super Ljava/lang/Object;
+
+.method public constructor <init>()V
+.registers 2
+ invoke-direct {v1}, Ljava/lang/Object;-><init>()V
+ new-instance v0, Ljava/lang/Class;
+ return-void
+.end method
diff --git a/test/600-verifier-fails/src/Main.java b/test/600-verifier-fails/src/Main.java
index fa25d58..1726bc4 100644
--- a/test/600-verifier-fails/src/Main.java
+++ b/test/600-verifier-fails/src/Main.java
@@ -39,5 +39,6 @@
test("C");
test("D");
test("E");
+ test("F");
}
}