diff options
-rw-r--r-- | runtime/art_method.h | 4 | ||||
-rw-r--r-- | runtime/dex_file.cc | 10 | ||||
-rw-r--r-- | runtime/dex_file.h | 3 | ||||
-rw-r--r-- | runtime/dex_file_verifier.cc | 210 | ||||
-rw-r--r-- | runtime/dex_file_verifier.h | 15 | ||||
-rw-r--r-- | runtime/dex_file_verifier_test.cc | 221 |
6 files changed, 386 insertions, 77 deletions
diff --git a/runtime/art_method.h b/runtime/art_method.h index 912df85815..d292630de9 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -131,12 +131,12 @@ class ArtMethod FINAL { 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 7d704ad0cc..f59420d332 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -1274,6 +1274,16 @@ std::string Signature::ToString() const { 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 250795b21d..cb7f174787 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -1197,6 +1197,9 @@ class Signature : public ValueObject { 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 a3ab9fa5bd..318123edcd 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -91,6 +91,66 @@ const char* DexFileVerifier::CheckLoadStringByIdx(dex::StringIndex idx, const ch 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 @@ const DexFile::MethodId* DexFileVerifier::CheckLoadMethodId(uint32_t idx, const 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 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, 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 @@ bool DexFileVerifier::CheckClassDataItemMethod(uint32_t idx, 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 @@ bool DexFileVerifier::CheckFieldAccessFlags(uint32_t idx, 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 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, 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 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, } } } + // 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 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, 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 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, 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 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, } // 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 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, 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 0327367059..ae206132dd 100644 --- a/runtime/dex_file_verifier.h +++ b/runtime/dex_file_verifier.h @@ -153,13 +153,15 @@ class DexFileVerifier { 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 @@ class DexFileVerifier { 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 f14b1d56cb..c56b20057d 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -632,12 +632,8 @@ TEST_F(DexFileVerifierTest, B28552165) { "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 @@ TEST_F(DexFileVerifierTest, Checksum) { 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 |