ART: Improve class initializer and constructor verification.
DEX file verifier checks additional properties of class initializers
and constructors:
(i) Names match expected <clinit> / <init>.
(ii) The method descriptor for <clinit> is ()V.
(iii) The return type of <init> is V.
(iV) No other names start with '<'.
Bug: 31313719
Change-Id: I60bffa6561e1bae353f97c42377ea556bfa790af
Test: m test-art-host-gtest-dex_file_verifier_test
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 2c31f6c..b80ed9c 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -131,12 +131,12 @@
return (GetAccessFlags() & kAccStatic) != 0;
}
- // Returns true if the method is a constructor.
+ // Returns true if the method is a constructor according to access flags.
bool IsConstructor() {
return (GetAccessFlags() & kAccConstructor) != 0;
}
- // Returns true if the method is a class initializer.
+ // Returns true if the method is a class initializer according to access flags.
bool IsClassInitializer() {
return IsConstructor() && IsStatic();
}
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 7d704ad..f59420d 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -1274,6 +1274,16 @@
return result;
}
+uint32_t Signature::GetNumberOfParameters() const {
+ const DexFile::TypeList* params = dex_file_->GetProtoParameters(*proto_id_);
+ return (params != nullptr) ? params->Size() : 0;
+}
+
+bool Signature::IsVoid() const {
+ const char* return_type = dex_file_->GetReturnTypeDescriptor(*proto_id_);
+ return strcmp(return_type, "V") == 0;
+}
+
bool Signature::operator==(const StringPiece& rhs) const {
if (dex_file_ == nullptr) {
return false;
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 250795b..cb7f174 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -1197,6 +1197,9 @@
return Signature();
}
+ bool IsVoid() const;
+ uint32_t GetNumberOfParameters() const;
+
bool operator==(const Signature& rhs) const;
bool operator!=(const Signature& rhs) const {
return !(*this == rhs);
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index a3ab9fa..318123e 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -91,6 +91,66 @@
return dex_file_->StringDataByIdx(idx);
}
+// Try to find the name of the method with the given index. We do not want to rely on DexFile
+// infrastructure at this point, so do it all by hand. begin and header correspond to begin_ and
+// header_ of the DexFileVerifier. str will contain the pointer to the method name on success
+// (flagged by the return value), otherwise error_msg will contain an error string.
+static bool FindMethodName(uint32_t method_index,
+ const uint8_t* begin,
+ const DexFile::Header* header,
+ const char** str,
+ std::string* error_msg) {
+ if (method_index >= header->method_ids_size_) {
+ *error_msg = "Method index not available for method flags verification";
+ return false;
+ }
+ uint32_t string_idx =
+ (reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) +
+ method_index)->name_idx_.index_;
+ if (string_idx >= header->string_ids_size_) {
+ *error_msg = "String index not available for method flags verification";
+ return false;
+ }
+ uint32_t string_off =
+ (reinterpret_cast<const DexFile::StringId*>(begin + header->string_ids_off_) + string_idx)->
+ string_data_off_;
+ if (string_off >= header->file_size_) {
+ *error_msg = "String offset out of bounds for method flags verification";
+ return false;
+ }
+ const uint8_t* str_data_ptr = begin + string_off;
+ uint32_t dummy;
+ if (!DecodeUnsignedLeb128Checked(&str_data_ptr, begin + header->file_size_, &dummy)) {
+ *error_msg = "String size out of bounds for method flags verification";
+ return false;
+ }
+ *str = reinterpret_cast<const char*>(str_data_ptr);
+ return true;
+}
+
+// Gets constructor flags based on the |method_name|. Returns true if
+// method_name is either <clinit> or <init> and sets
+// |constructor_flags_by_name| appropriately. Otherwise set
+// |constructor_flags_by_name| to zero and returns whether
+// |method_name| is valid.
+bool GetConstructorFlagsForMethodName(const char* method_name,
+ uint32_t* constructor_flags_by_name) {
+ if (method_name[0] != '<') {
+ *constructor_flags_by_name = 0;
+ return true;
+ }
+ if (strcmp(method_name + 1, "clinit>") == 0) {
+ *constructor_flags_by_name = kAccStatic | kAccConstructor;
+ return true;
+ }
+ if (strcmp(method_name + 1, "init>") == 0) {
+ *constructor_flags_by_name = kAccConstructor;
+ return true;
+ }
+ *constructor_flags_by_name = 0;
+ return false;
+}
+
const char* DexFileVerifier::CheckLoadStringByTypeIdx(dex::TypeIndex type_idx,
const char* error_string) {
if (UNLIKELY(!CheckIndex(type_idx.index_, dex_file_->NumTypeIds(), error_string))) {
@@ -113,6 +173,13 @@
return &dex_file_->GetMethodId(idx);
}
+const DexFile::ProtoId* DexFileVerifier::CheckLoadProtoId(uint32_t idx, const char* err_string) {
+ if (UNLIKELY(!CheckIndex(idx, dex_file_->NumProtoIds(), err_string))) {
+ return nullptr;
+ }
+ return &dex_file_->GetProtoId(idx);
+}
+
// Helper macro to load string and return false on error.
#define LOAD_STRING(var, idx, error) \
const char* (var) = CheckLoadStringByIdx(idx, error); \
@@ -606,12 +673,24 @@
return false;
}
- // Check method access flags.
- bool has_code = (code_offset != 0);
std::string error_msg;
+ const char* method_name;
+ if (!FindMethodName(idx, begin_, header_, &method_name, &error_msg)) {
+ ErrorStringPrintf("%s", error_msg.c_str());
+ return false;
+ }
+
+ uint32_t constructor_flags_by_name = 0;
+ if (!GetConstructorFlagsForMethodName(method_name, &constructor_flags_by_name)) {
+ ErrorStringPrintf("Bad method name: %s", method_name);
+ return false;
+ }
+
+ bool has_code = (code_offset != 0);
if (!CheckMethodAccessFlags(idx,
access_flags,
class_access_flags,
+ constructor_flags_by_name,
has_code,
expect_direct,
&error_msg)) {
@@ -619,6 +698,13 @@
return false;
}
+ if (constructor_flags_by_name != 0) {
+ if (!CheckConstructorProperties(idx, constructor_flags_by_name)) {
+ DCHECK(FailureReasonIsSet());
+ return false;
+ }
+ }
+
return true;
}
@@ -2653,46 +2739,10 @@
return true;
}
-// Try to find the name of the method with the given index. We do not want to rely on DexFile
-// infrastructure at this point, so do it all by hand. begin and header correspond to begin_ and
-// header_ of the DexFileVerifier. str will contain the pointer to the method name on success
-// (flagged by the return value), otherwise error_msg will contain an error string.
-static bool FindMethodName(uint32_t method_index,
- const uint8_t* begin,
- const DexFile::Header* header,
- const char** str,
- std::string* error_msg) {
- if (method_index >= header->method_ids_size_) {
- *error_msg = "Method index not available for method flags verification";
- return false;
- }
- uint32_t string_idx =
- (reinterpret_cast<const DexFile::MethodId*>(begin + header->method_ids_off_) +
- method_index)->name_idx_.index_;
- if (string_idx >= header->string_ids_size_) {
- *error_msg = "String index not available for method flags verification";
- return false;
- }
- uint32_t string_off =
- (reinterpret_cast<const DexFile::StringId*>(begin + header->string_ids_off_) + string_idx)->
- string_data_off_;
- if (string_off >= header->file_size_) {
- *error_msg = "String offset out of bounds for method flags verification";
- return false;
- }
- const uint8_t* str_data_ptr = begin + string_off;
- uint32_t dummy;
- if (!DecodeUnsignedLeb128Checked(&str_data_ptr, begin + header->file_size_, &dummy)) {
- *error_msg = "String size out of bounds for method flags verification";
- return false;
- }
- *str = reinterpret_cast<const char*>(str_data_ptr);
- return true;
-}
-
bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index,
uint32_t method_access_flags,
uint32_t class_access_flags,
+ uint32_t constructor_flags_by_name,
bool has_code,
bool expect_direct,
std::string* error_msg) {
@@ -2728,36 +2778,23 @@
return false;
}
- // Try to find the name, to check for constructor properties.
- const char* str;
- if (!FindMethodName(method_index, begin_, header_, &str, error_msg)) {
- return false;
- }
- bool is_init_by_name = false;
- constexpr const char* kInitName = "<init>";
- size_t str_offset = (reinterpret_cast<const uint8_t*>(str) - begin_);
- if (header_->file_size_ - str_offset >= sizeof(kInitName)) {
- is_init_by_name = strcmp(kInitName, str) == 0;
- }
- bool is_clinit_by_name = false;
- constexpr const char* kClinitName = "<clinit>";
- if (header_->file_size_ - str_offset >= sizeof(kClinitName)) {
- is_clinit_by_name = strcmp(kClinitName, str) == 0;
- }
- bool is_constructor = is_init_by_name || is_clinit_by_name;
+ constexpr uint32_t kConstructorFlags = kAccStatic | kAccConstructor;
+ const bool is_constructor_by_name = (constructor_flags_by_name & kConstructorFlags) != 0;
+ const bool is_clinit_by_name = constructor_flags_by_name == kConstructorFlags;
// Only methods named "<clinit>" or "<init>" may be marked constructor. Note: we cannot enforce
// the reverse for backwards compatibility reasons.
- if (((method_access_flags & kAccConstructor) != 0) && !is_constructor) {
+ if (((method_access_flags & kAccConstructor) != 0) && !is_constructor_by_name) {
*error_msg =
StringPrintf("Method %" PRIu32 "(%s) is marked constructor, but doesn't match name",
- method_index,
- GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
return false;
}
- // Check that the static constructor (= static initializer) is named "<clinit>" and that the
- // instance constructor is called "<init>".
- if (is_constructor) {
+
+ if (is_constructor_by_name) {
+ // Check that the static constructor (= static initializer) is named "<clinit>" and that the
+ // instance constructor is called "<init>".
bool is_static = (method_access_flags & kAccStatic) != 0;
if (is_static ^ is_clinit_by_name) {
*error_msg = StringPrintf("Constructor %" PRIu32 "(%s) is not flagged correctly wrt/ static.",
@@ -2772,9 +2809,11 @@
}
}
}
+
// Check that static and private methods, as well as constructors, are in the direct methods list,
// and other methods in the virtual methods list.
- bool is_direct = (method_access_flags & (kAccStatic | kAccPrivate)) != 0 || is_constructor;
+ bool is_direct = ((method_access_flags & (kAccStatic | kAccPrivate)) != 0) ||
+ is_constructor_by_name;
if (is_direct != expect_direct) {
*error_msg = StringPrintf("Direct/virtual method %" PRIu32 "(%s) not in expected list %d",
method_index,
@@ -2783,7 +2822,6 @@
return false;
}
-
// From here on out it is easier to mask out the bits we're supposed to ignore.
method_access_flags &= kMethodAccessFlags;
@@ -2819,7 +2857,7 @@
return false;
}
// Constructors must always have code.
- if (is_constructor) {
+ if (is_constructor_by_name) {
*error_msg = StringPrintf("Constructor %u(%s) must not be abstract or native",
method_index,
GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
@@ -2881,7 +2919,7 @@
}
// Instance constructors must not be synchronized and a few other flags.
- if (is_init_by_name) {
+ if (constructor_flags_by_name == kAccConstructor) {
static constexpr uint32_t kInitAllowed =
kAccPrivate | kAccProtected | kAccPublic | kAccStrict | kAccVarargs | kAccSynthetic;
if ((method_access_flags & ~kInitAllowed) != 0) {
@@ -2896,4 +2934,44 @@
return true;
}
+bool DexFileVerifier::CheckConstructorProperties(
+ uint32_t method_index,
+ uint32_t constructor_flags) {
+ DCHECK(constructor_flags == kAccConstructor ||
+ constructor_flags == (kAccConstructor | kAccStatic));
+
+ // Check signature matches expectations.
+ const DexFile::MethodId* const method_id = CheckLoadMethodId(method_index,
+ "Bad <init>/<clinit> method id");
+ if (method_id == nullptr) {
+ return false;
+ }
+
+ // Check the ProtoId for the corresponding method.
+ //
+ // TODO(oth): the error message here is to satisfy the MethodId test
+ // in the DexFileVerifierTest. The test is checking that the error
+ // contains this string if the index is out of range.
+ const DexFile::ProtoId* const proto_id = CheckLoadProtoId(method_id->proto_idx_,
+ "inter_method_id_item proto_idx");
+ if (proto_id == nullptr) {
+ return false;
+ }
+
+ Signature signature = dex_file_->GetMethodSignature(*method_id);
+ if (constructor_flags == (kAccStatic | kAccConstructor)) {
+ if (!signature.IsVoid() || signature.GetNumberOfParameters() != 0) {
+ ErrorStringPrintf("<clinit> must have descriptor ()V");
+ return false;
+ }
+ } else if (!signature.IsVoid()) {
+ ErrorStringPrintf("Constructor %u(%s) must be void",
+ method_index,
+ GetMethodDescriptionOrError(begin_, header_, method_index).c_str());
+ return false;
+ }
+
+ return true;
+}
+
} // namespace art
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index 0327367..ae20613 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -153,13 +153,15 @@
const char* CheckLoadStringByIdx(dex::StringIndex idx, const char* error_fmt);
const char* CheckLoadStringByTypeIdx(dex::TypeIndex type_idx, const char* error_fmt);
- // Load a field/method Id by index. Checks whether the index is in bounds, printing the error if
- // not. If there is an error, null is returned.
+ // Load a field/method/proto Id by index. Checks whether the index is in bounds, printing the
+ // error if not. If there is an error, null is returned.
const DexFile::FieldId* CheckLoadFieldId(uint32_t idx, const char* error_fmt);
const DexFile::MethodId* CheckLoadMethodId(uint32_t idx, const char* error_fmt);
+ const DexFile::ProtoId* CheckLoadProtoId(uint32_t idx, const char* error_fmt);
void ErrorStringPrintf(const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3))) COLD_ATTR;
+ bool FailureReasonIsSet() const { return failure_reason_.size() != 0; }
// Retrieve class index and class access flag from the given member. index is the member index,
// which is taken as either a field or a method index (as designated by is_field). The result,
@@ -177,15 +179,20 @@
bool CheckFieldAccessFlags(uint32_t idx,
uint32_t field_access_flags,
uint32_t class_access_flags,
- std::string* error_msg);
+ std::string* error_message);
+
// Check validity of the given method and access flags, in the context of a class with the given
// second access flags.
bool CheckMethodAccessFlags(uint32_t method_index,
uint32_t method_access_flags,
uint32_t class_access_flags,
+ uint32_t constructor_flags_by_name,
bool has_code,
bool expect_direct,
- std::string* error_msg);
+ std::string* error_message);
+
+ // Check validity of given method if it's a constructor or class initializer.
+ bool CheckConstructorProperties(uint32_t method_index, uint32_t constructor_flags);
const DexFile* const dex_file_;
const uint8_t* const begin_;
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index f14b1d5..c56b200 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -632,12 +632,8 @@
"b28552165",
[](DexFile* dex_file) {
OrMaskToMethodFlags(dex_file, "foo", kAccPublic | kAccProtected);
- uint32_t method_idx;
- FindMethodData(dex_file, "foo", &method_idx);
- auto* method_id = const_cast<DexFile::MethodId*>(&dex_file->GetMethodId(method_idx));
- method_id->name_idx_ = dex::StringIndex(dex_file->NumStringIds());
},
- "Method may have only one of public/protected/private, LMethodFlags;.(error)");
+ "Method may have only one of public/protected/private, LMethodFlags;.foo");
}
// Set of dex files for interface method tests. As it's not as easy to mutate method names, it's
@@ -1674,4 +1670,219 @@
EXPECT_NE(error_msg.find("Bad checksum"), std::string::npos) << error_msg;
}
+TEST_F(DexFileVerifierTest, BadStaticMethodName) {
+ // Generated DEX file version (037) from:
+ //
+ // .class public LBadName;
+ // .super Ljava/lang/Object;
+ //
+ // .method public static <bad_name> (II)V
+ // .registers 2
+ // .prologue
+ // return-void
+ // .end method
+ //
+ // .method public constructor <init>()V
+ // .registers 1
+ // .prologue
+ // .line 1
+ // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ // return-void
+ // .end method
+ //
+ static const char kDexBase64[] =
+ "ZGV4CjAzNwC2NYlwyxEc/h6hv+hMeUVQPtiX6MQBcfgwAgAAcAAAAHhWNBIAAAAAAAAAAJABAAAI"
+ "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABAAQAA8AAAAPAA"
+ "AAD8AAAABAEAABIBAAAVAQAAIAEAADQBAAA3AQAAAwAAAAQAAAAFAAAABgAAAAYAAAADAAAAAAAA"
+ "AAcAAAADAAAAPAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAACAAAA"
+ "AAAAAIABAAAAAAAACjxiYWRfbmFtZT4ABjxpbml0PgAMQmFkTmFtZS5qYXZhAAFJAAlMQmFkTmFt"
+ "ZTsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgADVklJAAIAAAAAAAAAAAAAAAACAAAHAAEABw4AAAIA"
+ "AgAAAAAASAEAAAEAAAAOAAAAAQABAAEAAABOAQAABAAAAHAQAgAAAA4AAAACAAAJ1AIBgYAE6AIA"
+ "AA0AAAAAAAAAAQAAAAAAAAABAAAACAAAAHAAAAACAAAABAAAAJAAAAADAAAAAgAAAKAAAAAFAAAA"
+ "AwAAALgAAAAGAAAAAQAAANAAAAACIAAACAAAAPAAAAABEAAAAQAAADwBAAADEAAAAQAAAEQBAAAD"
+ "IAAAAgAAAEgBAAABIAAAAgAAAFQBAAAAIAAAAQAAAIABAAAAEAAAAQAAAJABAAA=";
+
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length));
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ std::string error_msg;
+ EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "bad static method name",
+ /*verify_checksum*/ true,
+ &error_msg));
+}
+
+TEST_F(DexFileVerifierTest, BadVirtualMethodName) {
+ // Generated DEX file version (037) from:
+ //
+ // .class public LBadVirtualName;
+ // .super Ljava/lang/Object;
+ //
+ // .method public <bad_name> (II)V
+ // .registers 2
+ // return-void
+ // .end method
+ //
+ // .method public constructor <init>()V
+ // .registers 1
+ // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ // return-void
+ // .end method
+ //
+ static const char kDexBase64[] =
+ "ZGV4CjAzNwDcPC8B2E7kYTZmeHX2u2IqrpWV9EXBHpE8AgAAcAAAAHhWNBIAAAAAAAAAAJwBAAAI"
+ "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABMAQAA8AAAAPAA"
+ "AAD8AAAABAEAABkBAAAcAQAALgEAAEIBAABFAQAAAwAAAAQAAAAFAAAABgAAAAYAAAADAAAAAAAA"
+ "AAcAAAADAAAATAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAACAAAA"
+ "AAAAAI4BAAAAAAAACjxiYWRfbmFtZT4ABjxpbml0PgATQmFkVmlydHVhbE5hbWUuamF2YQABSQAQ"
+ "TEJhZFZpcnR1YWxOYW1lOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAANWSUkAAAACAAAAAAAAAAAA"
+ "AAABAAcOAAACAAAHAAABAAEAAQAAAFgBAAAEAAAAcBACAAAADgADAAMAAAAAAF0BAAABAAAADgAA"
+ "AAEBAYGABOQCAAH8Ag0AAAAAAAAAAQAAAAAAAAABAAAACAAAAHAAAAACAAAABAAAAJAAAAADAAAA"
+ "AgAAAKAAAAAFAAAAAwAAALgAAAAGAAAAAQAAANAAAAACIAAACAAAAPAAAAABEAAAAQAAAEwBAAAD"
+ "EAAAAQAAAFQBAAADIAAAAgAAAFgBAAABIAAAAgAAAGQBAAAAIAAAAQAAAI4BAAAAEAAAAQAAAJwB"
+ "AAA=";
+
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length));
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ std::string error_msg;
+ EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "bad virtual method name",
+ /*verify_checksum*/ true,
+ &error_msg));
+}
+
+TEST_F(DexFileVerifierTest, BadClinitSignature) {
+ // Generated DEX file version (037) from:
+ //
+ // .class public LOneClinitBadSig;
+ // .super Ljava/lang/Object;
+ //
+ // .method public static constructor <clinit>(II)V
+ // .registers 2
+ // return-void
+ // .end method
+ //
+ // .method public constructor <init>()V
+ // .registers 1
+ // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ // return-void
+ // .end method
+ //
+ static const char kDexBase64[] =
+ "ZGV4CjAzNwBNOwTbfJmWq5eMOlxUY4EICGiEGJMVg8RAAgAAcAAAAHhWNBIAAAAAAAAAAKABAAAI"
+ "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABQAQAA8AAAAPAA"
+ "AAD6AAAAAgEAAAUBAAAYAQAALAEAAEIBAABFAQAAAgAAAAMAAAAEAAAABgAAAAYAAAADAAAAAAAA"
+ "AAcAAAADAAAATAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAAFAAAA"
+ "AAAAAJABAAAAAAAACDxjbGluaXQ+AAY8aW5pdD4AAUkAEUxPbmVDbGluaXRCYWRTaWc7ABJMamF2"
+ "YS9sYW5nL09iamVjdDsAFE9uZUNsaW5pdEJhZFNpZy5qYXZhAAFWAANWSUkAAAACAAAAAAAAAAAA"
+ "AAAAAgAABwABAAcOAAACAAIAAAAAAFgBAAABAAAADgAAAAEAAQABAAAAXgEAAAQAAABwEAIAAAAO"
+ "AAAAAgAAiYAE5AIBgYAE+AINAAAAAAAAAAEAAAAAAAAAAQAAAAgAAABwAAAAAgAAAAQAAACQAAAA"
+ "AwAAAAIAAACgAAAABQAAAAMAAAC4AAAABgAAAAEAAADQAAAAAiAAAAgAAADwAAAAARAAAAEAAABM"
+ "AQAAAxAAAAEAAABUAQAAAyAAAAIAAABYAQAAASAAAAIAAABkAQAAACAAAAEAAACQAQAAABAAAAEA"
+ "AACgAQAA";
+
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length));
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ std::string error_msg;
+ EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "bad clinit signature",
+ /*verify_checksum*/ true,
+ &error_msg));
+}
+
+TEST_F(DexFileVerifierTest, BadClinitSignatureAgain) {
+ // Generated DEX file version (037) from:
+ //
+ // .class public LOneClinitBadSigAgain;
+ // .super Ljava/lang/Object;
+ //
+ // .method public static constructor <clinit>()I
+ // .registers 1
+ // const/4 v0, 1
+ // return v0
+ // .end method
+ //
+ // .method public constructor <init>()V
+ // .registers 1
+ // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ // return-void
+ // .end method
+ //
+ static const char kDexBase64[] =
+ "ZGV4CjAzNwBfPcPu5NVwKUqZIu/YR8xqVlVD5UzTk0gEAgAAcAAAAHhWNBIAAAAAAAAAAIgBAAAH"
+ "AAAAcAAAAAQAAACMAAAAAgAAAJwAAAAAAAAAAAAAAAMAAAC0AAAAAQAAAMwAAAAYAQAA7AAAAOwA"
+ "AAD2AAAA/gAAAAEBAAAZAQAALQEAAEgBAAACAAAAAwAAAAQAAAAGAAAAAgAAAAAAAAAAAAAABgAA"
+ "AAMAAAAAAAAAAQAAAAAAAAABAAEAAQAAAAIAAQABAAAAAQAAAAEAAAACAAAAAAAAAAUAAAAAAAAA"
+ "eAEAAAAAAAAIPGNsaW5pdD4ABjxpbml0PgABSQAWTE9uZUNsaW5pdEJhZFNpZ0FnYWluOwASTGph"
+ "dmEvbGFuZy9PYmplY3Q7ABlPbmVDbGluaXRCYWRTaWdBZ2Fpbi5qYXZhAAFWAAABAAAAAAAAAAAA"
+ "AAACAAAAEhAPAAEAAQABAAAAAAAAAAQAAABwEAIAAAAOAAAAAgAAiYAEzAIBgYAE4AIKAAAAAAAA"
+ "AAEAAAAAAAAAAQAAAAcAAABwAAAAAgAAAAQAAACMAAAAAwAAAAIAAACcAAAABQAAAAMAAAC0AAAA"
+ "BgAAAAEAAADMAAAAAiAAAAcAAADsAAAAASAAAAIAAABMAQAAACAAAAEAAAB4AQAAABAAAAEAAACI"
+ "AQAA";
+
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length));
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ std::string error_msg;
+ EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "bad clinit signature",
+ /*verify_checksum*/ true,
+ &error_msg));
+}
+
+TEST_F(DexFileVerifierTest, BadInitSignature) {
+ // Generated DEX file version (037) from:
+ //
+ // .class public LBadInitSig;
+ // .super Ljava/lang/Object;
+ //
+ // .method public constructor <init>()I
+ // .registers 1
+ // invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+ // const v0, 1
+ // return v0
+ // .end method
+ //
+ static const char kDexBase64[] =
+ "ZGV4CjAzNwCdMdeh1KoHWamF2Prq32LF39YZ78fV7q+wAQAAcAAAAHhWNBIAAAAAAAAAADQBAAAF"
+ "AAAAcAAAAAQAAACEAAAAAgAAAJQAAAAAAAAAAAAAAAIAAACsAAAAAQAAALwAAADUAAAA3AAAANwA"
+ "AADkAAAA5wAAAPUAAAAJAQAAAQAAAAIAAAADAAAABAAAAAEAAAAAAAAAAAAAAAQAAAADAAAAAAAA"
+ "AAEAAAAAAAAAAgABAAAAAAABAAAAAQAAAAIAAAAAAAAA/////wAAAAAqAQAAAAAAAAY8aW5pdD4A"
+ "AUkADExCYWRJbml0U2lnOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAEAAQABAAAAAAAAAAcAAABw"
+ "EAEAAAAUAAEAAAAPAAAAAQAAgYAEjAIKAAAAAAAAAAEAAAAAAAAAAQAAAAUAAABwAAAAAgAAAAQA"
+ "AACEAAAAAwAAAAIAAACUAAAABQAAAAIAAACsAAAABgAAAAEAAAC8AAAAAiAAAAUAAADcAAAAASAA"
+ "AAEAAAAMAQAAACAAAAEAAAAqAQAAABAAAAEAAAA0AQAA";
+
+ size_t length;
+ std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length));
+ CHECK(dex_bytes != nullptr);
+ // Note: `dex_file` will be destroyed before `dex_bytes`.
+ std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
+ std::string error_msg;
+ EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(),
+ dex_file->Begin(),
+ dex_file->Size(),
+ "bad init signature",
+ /*verify_checksum*/ true,
+ &error_msg));
+}
+
} // namespace art