ART: Add integer & long parsing cutout to unstarted runtime
Add a cutout for Integer.parseInt and Long.parseLong. Only handle
(some) successful cases, and abort the transaction in the rest.
Add tests.
Allows to compile-time initialize:
* com.android.org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers
* com.android.org.bouncycastle.asn1.x509.X509ObjectIdentifiers
* sun.security.x509.InhibitAnyPolicyExtension
Bug: 27265238
(cherry picked from commit 9c62dab16e4be0c19a2e6c6c0cc2b24c2814c6f2)
Change-Id: I14c0836fd9ec7c9bf49d8186ff14a3c6830ff711
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 239b825..0e18945 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -16,7 +16,11 @@
#include "unstarted_runtime.h"
+#include <errno.h>
+#include <stdlib.h>
+
#include <cmath>
+#include <limits>
#include <unordered_map>
#include "ScopedLocalRef.h"
@@ -1069,6 +1073,93 @@
}
}
+// A cutout for Integer.parseInt(String). Note: this code is conservative and will bail instead
+// of correctly handling the corner cases.
+void UnstartedRuntime::UnstartedIntegerParseInt(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset);
+ if (obj == nullptr) {
+ AbortTransactionOrFail(self, "Cannot parse null string, retry at runtime.");
+ return;
+ }
+
+ std::string string_value = obj->AsString()->ToModifiedUtf8();
+ if (string_value.empty()) {
+ AbortTransactionOrFail(self, "Cannot parse empty string, retry at runtime.");
+ return;
+ }
+
+ const char* c_str = string_value.c_str();
+ char *end;
+ // Can we set errno to 0? Is this always a variable, and not a macro?
+ // Worst case, we'll incorrectly fail a transaction. Seems OK.
+ int64_t l = strtol(c_str, &end, 10);
+
+ if ((errno == ERANGE && l == LONG_MAX) || l > std::numeric_limits<int32_t>::max() ||
+ (errno == ERANGE && l == LONG_MIN) || l < std::numeric_limits<int32_t>::min()) {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+ if (l == 0) {
+ // Check whether the string wasn't exactly zero.
+ if (string_value != "0") {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+ } else if (*end != '\0') {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+
+ result->SetI(static_cast<int32_t>(l));
+}
+
+// A cutout for Long.parseLong.
+//
+// Note: for now use code equivalent to Integer.parseInt, as the full range may not be supported
+// well.
+void UnstartedRuntime::UnstartedLongParseLong(
+ Thread* self, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ mirror::Object* obj = shadow_frame->GetVRegReference(arg_offset);
+ if (obj == nullptr) {
+ AbortTransactionOrFail(self, "Cannot parse null string, retry at runtime.");
+ return;
+ }
+
+ std::string string_value = obj->AsString()->ToModifiedUtf8();
+ if (string_value.empty()) {
+ AbortTransactionOrFail(self, "Cannot parse empty string, retry at runtime.");
+ return;
+ }
+
+ const char* c_str = string_value.c_str();
+ char *end;
+ // Can we set errno to 0? Is this always a variable, and not a macro?
+ // Worst case, we'll incorrectly fail a transaction. Seems OK.
+ int64_t l = strtol(c_str, &end, 10);
+
+ // Note: comparing against int32_t min/max is intentional here.
+ if ((errno == ERANGE && l == LONG_MAX) || l > std::numeric_limits<int32_t>::max() ||
+ (errno == ERANGE && l == LONG_MIN) || l < std::numeric_limits<int32_t>::min()) {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+ if (l == 0) {
+ // Check whether the string wasn't exactly zero.
+ if (string_value != "0") {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+ } else if (*end != '\0') {
+ AbortTransactionOrFail(self, "Cannot parse string %s, retry at runtime.", c_str);
+ return;
+ }
+
+ result->SetJ(l);
+}
+
void UnstartedRuntime::UnstartedJNIVMRuntimeNewUnpaddedArray(
Thread* self, ArtMethod* method ATTRIBUTE_UNUSED, mirror::Object* receiver ATTRIBUTE_UNUSED,