| /* |
| * Copyright (C) 2011 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. |
| */ |
| |
| #include "dex_file_verifier.h" |
| |
| #include <zlib.h> |
| |
| #include <functional> |
| #include <memory> |
| |
| #include <android-base/logging.h> |
| |
| #include "base/bit_utils.h" |
| #include "base/leb128.h" |
| #include "base/macros.h" |
| #include "base64_test_util.h" |
| #include "class_accessor-inl.h" |
| #include "descriptors_names.h" |
| #include "dex_file-inl.h" |
| #include "dex_file_loader.h" |
| #include "dex_file_types.h" |
| #include "gtest/gtest.h" |
| #include "standard_dex_file.h" |
| |
| namespace art { |
| |
| static constexpr char kLocationString[] = "dex_file_location"; |
| |
| // Make the Dex file version 37. |
| static void MakeDexVersion37(DexFile* dex_file) { |
| size_t offset = OFFSETOF_MEMBER(DexFile::Header, magic_) + 6; |
| CHECK_EQ(*(dex_file->Begin() + offset), '5'); |
| *(const_cast<uint8_t*>(dex_file->Begin()) + offset) = '7'; |
| } |
| |
| static void FixUpChecksum(uint8_t* dex_file) { |
| DexFile::Header* header = reinterpret_cast<DexFile::Header*>(dex_file); |
| uint32_t expected_size = header->file_size_; |
| uint32_t adler_checksum = adler32(0L, Z_NULL, 0); |
| const uint32_t non_sum = sizeof(DexFile::Header::magic_) + sizeof(DexFile::Header::checksum_); |
| const uint8_t* non_sum_ptr = dex_file + non_sum; |
| adler_checksum = adler32(adler_checksum, non_sum_ptr, expected_size - non_sum); |
| header->checksum_ = adler_checksum; |
| } |
| |
| class DexFileVerifierTest : public testing::Test { |
| protected: |
| DexFile* GetDexFile(const uint8_t* dex_bytes, size_t length) { |
| auto container = std::make_shared<MemoryDexFileContainer>(dex_bytes, length); |
| return new StandardDexFile(dex_bytes, length, "tmp", 0, nullptr, std::move(container)); |
| } |
| |
| void VerifyModification(const char* dex_file_base64_content, |
| const char* location, |
| const std::function<void(DexFile*)>& f, |
| const char* expected_error) { |
| size_t length; |
| std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(dex_file_base64_content, &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)); |
| f(dex_file.get()); |
| FixUpChecksum(const_cast<uint8_t*>(dex_file->Begin())); |
| |
| static constexpr bool kVerifyChecksum = true; |
| std::string error_msg; |
| bool success = dex::Verify(dex_file.get(), location, kVerifyChecksum, &error_msg); |
| if (expected_error == nullptr) { |
| EXPECT_TRUE(success) << error_msg; |
| } else { |
| EXPECT_FALSE(success) << "Expected " << expected_error; |
| if (!success) { |
| EXPECT_NE(error_msg.find(expected_error), std::string::npos) << error_msg; |
| } |
| } |
| } |
| }; |
| |
| static std::unique_ptr<const DexFile> OpenDexFileBase64(const char* base64, |
| const char* location, |
| std::string* error_msg) { |
| // decode base64 |
| CHECK(base64 != nullptr); |
| size_t length; |
| std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(base64, &length)); |
| CHECK(dex_bytes.get() != nullptr); |
| |
| // read dex |
| std::vector<std::unique_ptr<const DexFile>> tmp; |
| DexFileLoader dex_file_loader(dex_bytes.get(), length, location); |
| DexFileLoaderErrorCode error_code; |
| bool success = dex_file_loader.Open(/* verify= */ true, |
| /* verify_checksum= */ true, |
| &error_code, |
| error_msg, |
| &tmp); |
| CHECK(success) << *error_msg; |
| EXPECT_EQ(1U, tmp.size()); |
| std::unique_ptr<const DexFile> dex_file = std::move(tmp[0]); |
| return dex_file; |
| } |
| |
| // To generate a base64 encoded Dex file (such as kGoodTestDex, below) |
| // from Smali files, use: |
| // |
| // smali assemble -o classes.dex class1.smali [class2.smali ...] |
| // base64 classes.dex >classes.dex.base64 |
| |
| // For reference. |
| static const char kGoodTestDex[] = |
| "ZGV4CjAzNQDrVbyVkxX1HljTznNf95AglkUAhQuFtmKkAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAN" |
| "AAAAcAAAAAYAAACkAAAAAgAAALwAAAABAAAA1AAAAAQAAADcAAAAAQAAAPwAAACIAQAAHAEAAFoB" |
| "AABiAQAAagEAAIEBAACVAQAAqQEAAL0BAADDAQAAzgEAANEBAADVAQAA2gEAAN8BAAABAAAAAgAA" |
| "AAMAAAAEAAAABQAAAAgAAAAIAAAABQAAAAAAAAAJAAAABQAAAFQBAAAEAAEACwAAAAAAAAAAAAAA" |
| "AAAAAAoAAAABAAEADAAAAAIAAAAAAAAAAAAAAAEAAAACAAAAAAAAAAcAAAAAAAAA8wEAAAAAAAAB" |
| "AAEAAQAAAOgBAAAEAAAAcBADAAAADgACAAAAAgAAAO0BAAAIAAAAYgAAABoBBgBuIAIAEAAOAAEA" |
| "AAADAAY8aW5pdD4ABkxUZXN0OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09i" |
| "amVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07AARUZXN0AAlUZXN0" |
| "LmphdmEAAVYAAlZMAANmb28AA291dAAHcHJpbnRsbgABAAcOAAMABw54AAAAAgAAgYAEnAIBCbQC" |
| "AAAADQAAAAAAAAABAAAAAAAAAAEAAAANAAAAcAAAAAIAAAAGAAAApAAAAAMAAAACAAAAvAAAAAQA" |
| "AAABAAAA1AAAAAUAAAAEAAAA3AAAAAYAAAABAAAA/AAAAAEgAAACAAAAHAEAAAEQAAABAAAAVAEA" |
| "AAIgAAANAAAAWgEAAAMgAAACAAAA6AEAAAAgAAABAAAA8wEAAAAQAAABAAAABAIAAA=="; |
| |
| TEST_F(DexFileVerifierTest, GoodDex) { |
| std::string error_msg; |
| std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kGoodTestDex, |
| kLocationString, |
| &error_msg)); |
| ASSERT_TRUE(raw.get() != nullptr) << error_msg; |
| } |
| |
| TEST_F(DexFileVerifierTest, MethodId) { |
| // Class idx error. |
| VerifyModification( |
| kGoodTestDex, |
| "method_id_class_idx", |
| [](DexFile* dex_file) { |
| dex::MethodId* method_id = const_cast<dex::MethodId*>(&dex_file->GetMethodId(0)); |
| method_id->class_idx_ = dex::TypeIndex(0xFF); |
| }, |
| "Bad index for method_id.class"); |
| |
| // Proto idx error. |
| VerifyModification( |
| kGoodTestDex, |
| "method_id_proto_idx", |
| [](DexFile* dex_file) { |
| dex::MethodId* method_id = const_cast<dex::MethodId*>(&dex_file->GetMethodId(0)); |
| method_id->proto_idx_ = dex::ProtoIndex(0xFF); |
| }, |
| "Bad index for method_id.proto"); |
| |
| // Name idx error. |
| VerifyModification( |
| kGoodTestDex, |
| "method_id_name_idx", |
| [](DexFile* dex_file) { |
| dex::MethodId* method_id = const_cast<dex::MethodId*>(&dex_file->GetMethodId(0)); |
| method_id->name_idx_ = dex::StringIndex(0xFF); |
| }, |
| "Bad index for method_id.name"); |
| } |
| |
| TEST_F(DexFileVerifierTest, InitCachingWithUnicode) { |
| static const char kInitWithUnicode[] = |
| "ZGV4CjAzNQDhN60rgMnSK13MoRscTuD+NZe7f6rIkHAAAgAAcAAAAHhWNBIAAAAAAAAAAGwBAAAJ" |
| "AAAAcAAAAAMAAACUAAAAAQAAAKAAAAAAAAAAAAAAAAIAAACsAAAAAQAAALwAAAAkAQAA3AAAANwA" |
| "AADgAAAA5gAAAO4AAAD1AAAAAQEAABUBAAAgAQAAIwEAAAQAAAAFAAAABwAAAAcAAAACAAAAAAAA" |
| "AAAAAAACAAAAAQAAAAIAAAAAAAAAAAAAAAEAAAAAAAAABgAAAAAAAABgAQAAAAAAAAHAgAACwIDA" |
| "gAAGPGluaXQ+AAVIZWxsbwAKTFRlc3RTeW5jOwASTGphdmEvbGFuZy9PYmplY3Q7AAlNYWluLmph" |
| "dmEAAVYABVdvcmxkAAAAAAAAAAYABw4AAAAACgABAAEAAAAwAQAADAAAAHAQAQAJABoBAwAaAggA" |
| "GgMAABoEAQAOAAAAAQAAgIAEuAIAAAwAAAAAAAAAAQAAAAAAAAABAAAACQAAAHAAAAACAAAAAwAA" |
| "AJQAAAADAAAAAQAAAKAAAAAFAAAAAgAAAKwAAAAGAAAAAQAAALwAAAACIAAACQAAANwAAAADEAAA" |
| "AQAAACwBAAADIAAAAQAAADABAAABIAAAAQAAADgBAAAAIAAAAQAAAGABAAAAEAAAAQAAAGwBAAA="; |
| // Just ensure it verifies w/o modification. |
| VerifyModification(kInitWithUnicode, "init_with_unicode", [](DexFile*) {}, nullptr); |
| } |
| |
| // Method flags test class generated from the following smali code. The declared-synchronized |
| // flags are there to enforce a 3-byte uLEB128 encoding so we don't have to relayout |
| // the code, but we need to remove them before doing tests. |
| // |
| // .class public LMethodFlags; |
| // .super Ljava/lang/Object; |
| // |
| // .method public static constructor <clinit>()V |
| // .registers 1 |
| // return-void |
| // .end method |
| // |
| // .method public constructor <init>()V |
| // .registers 1 |
| // return-void |
| // .end method |
| // |
| // .method private declared-synchronized foo()V |
| // .registers 1 |
| // return-void |
| // .end method |
| // |
| // .method public declared-synchronized bar()V |
| // .registers 1 |
| // return-void |
| // .end method |
| |
| static const char kMethodFlagsTestDex[] = |
| "ZGV4CjAzNQCyOQrJaDBwiIWv5MIuYKXhxlLLsQcx5SwgAgAAcAAAAHhWNBIAAAAAAAAAAJgBAAAH" |
| "AAAAcAAAAAMAAACMAAAAAQAAAJgAAAAAAAAAAAAAAAQAAACkAAAAAQAAAMQAAAA8AQAA5AAAAOQA" |
| "AADuAAAA9gAAAAUBAAAZAQAAHAEAACEBAAACAAAAAwAAAAQAAAAEAAAAAgAAAAAAAAAAAAAAAAAA" |
| "AAAAAAABAAAAAAAAAAUAAAAAAAAABgAAAAAAAAABAAAAAQAAAAAAAAD/////AAAAAHoBAAAAAAAA" |
| "CDxjbGluaXQ+AAY8aW5pdD4ADUxNZXRob2RGbGFnczsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgAD" |
| "YmFyAANmb28AAAAAAAAAAQAAAAAAAAAAAAAAAQAAAA4AAAABAAEAAAAAAAAAAAABAAAADgAAAAEA" |
| "AQAAAAAAAAAAAAEAAAAOAAAAAQABAAAAAAAAAAAAAQAAAA4AAAADAQCJgASsAgGBgATAAgKCgAjU" |
| "AgKBgAjoAgAACwAAAAAAAAABAAAAAAAAAAEAAAAHAAAAcAAAAAIAAAADAAAAjAAAAAMAAAABAAAA" |
| "mAAAAAUAAAAEAAAApAAAAAYAAAABAAAAxAAAAAIgAAAHAAAA5AAAAAMQAAABAAAAKAEAAAEgAAAE" |
| "AAAALAEAAAAgAAABAAAAegEAAAAQAAABAAAAmAEAAA=="; |
| |
| // Find the method data for the first method with the given name (from class 0). Note: the pointer |
| // is to the access flags, so that the caller doesn't have to handle the leb128-encoded method-index |
| // delta. |
| static const uint8_t* FindMethodData(const DexFile* dex_file, |
| const char* name, |
| /*out*/ uint32_t* method_idx = nullptr) { |
| ClassAccessor accessor(*dex_file, dex_file->GetClassDef(0)); |
| |
| for (const ClassAccessor::Method& method : accessor.GetMethods()) { |
| uint32_t method_index = method.GetIndex(); |
| dex::StringIndex name_index = dex_file->GetMethodId(method_index).name_idx_; |
| const dex::StringId& string_id = dex_file->GetStringId(name_index); |
| const char* str = dex_file->GetStringData(string_id); |
| if (strcmp(name, str) == 0) { |
| if (method_idx != nullptr) { |
| *method_idx = method_index; |
| } |
| // Go back 2 lebs to the access flags. |
| const uint8_t* trailing = ReverseSearchUnsignedLeb128(method.GetDataPointer()); |
| trailing = ReverseSearchUnsignedLeb128(trailing); |
| return trailing; |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| // Set the method flags to the given value. |
| static void SetMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) { |
| uint8_t* method_flags_ptr = const_cast<uint8_t*>(FindMethodData(dex_file, method)); |
| CHECK(method_flags_ptr != nullptr) << method; |
| |
| // Unroll this, as we only have three bytes, anyways. |
| uint8_t base1 = static_cast<uint8_t>(mask & 0x7F); |
| *(method_flags_ptr++) = (base1 | 0x80); |
| mask >>= 7; |
| |
| uint8_t base2 = static_cast<uint8_t>(mask & 0x7F); |
| *(method_flags_ptr++) = (base2 | 0x80); |
| mask >>= 7; |
| |
| uint8_t base3 = static_cast<uint8_t>(mask & 0x7F); |
| *method_flags_ptr = base3; |
| } |
| |
| static uint32_t GetMethodFlags(DexFile* dex_file, const char* method) { |
| const uint8_t* method_flags_ptr = const_cast<uint8_t*>(FindMethodData(dex_file, method)); |
| CHECK(method_flags_ptr != nullptr) << method; |
| return DecodeUnsignedLeb128(&method_flags_ptr); |
| } |
| |
| // Apply the given mask to method flags. |
| static void ApplyMaskToMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) { |
| uint32_t value = GetMethodFlags(dex_file, method); |
| value &= mask; |
| SetMethodFlags(dex_file, method, value); |
| } |
| |
| // Apply the given mask to method flags. |
| static void OrMaskToMethodFlags(DexFile* dex_file, const char* method, uint32_t mask) { |
| uint32_t value = GetMethodFlags(dex_file, method); |
| value |= mask; |
| SetMethodFlags(dex_file, method, value); |
| } |
| |
| // Set code_off to 0 for the method. |
| static void RemoveCode(DexFile* dex_file, const char* method) { |
| const uint8_t* ptr = FindMethodData(dex_file, method); |
| // Next is flags, pass. |
| DecodeUnsignedLeb128(&ptr); |
| |
| // Figure out how many bytes the code_off is. |
| const uint8_t* tmp = ptr; |
| DecodeUnsignedLeb128(&tmp); |
| size_t bytes = tmp - ptr; |
| |
| uint8_t* mod = const_cast<uint8_t*>(ptr); |
| for (size_t i = 1; i < bytes; ++i) { |
| *(mod++) = 0x80; |
| } |
| *mod = 0x00; |
| } |
| |
| TEST_F(DexFileVerifierTest, MethodAccessFlagsBase) { |
| // Check that it's OK when the wrong declared-synchronized flag is removed from "foo." |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "method_flags_ok", |
| [](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| }, |
| nullptr); |
| } |
| |
| TEST_F(DexFileVerifierTest, MethodAccessFlagsConstructors) { |
| // Make sure we still accept constructors without their flags. |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "method_flags_missing_constructor_tag_ok", |
| [](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccConstructor); |
| ApplyMaskToMethodFlags(dex_file, "<clinit>", ~kAccConstructor); |
| }, |
| nullptr); |
| |
| constexpr const char* kConstructors[] = { "<clinit>", "<init>"}; |
| for (size_t i = 0; i < 2; ++i) { |
| // Constructor with code marked native. |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "method_flags_constructor_native", |
| [&](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| OrMaskToMethodFlags(dex_file, kConstructors[i], kAccNative); |
| }, |
| "has code, but is marked native or abstract"); |
| // Constructor with code marked abstract. |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "method_flags_constructor_abstract", |
| [&](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| OrMaskToMethodFlags(dex_file, kConstructors[i], kAccAbstract); |
| }, |
| "has code, but is marked native or abstract"); |
| // Constructor as-is without code. |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "method_flags_constructor_nocode", |
| [&](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| RemoveCode(dex_file, kConstructors[i]); |
| }, |
| "has no code, but is not marked native or abstract"); |
| // Constructor without code marked native. |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "method_flags_constructor_native_nocode", |
| [&](DexFile* dex_file) { |
| MakeDexVersion37(dex_file); |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| OrMaskToMethodFlags(dex_file, kConstructors[i], kAccNative); |
| RemoveCode(dex_file, kConstructors[i]); |
| }, |
| "must not be abstract or native"); |
| // Constructor without code marked abstract. |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "method_flags_constructor_abstract_nocode", |
| [&](DexFile* dex_file) { |
| MakeDexVersion37(dex_file); |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| OrMaskToMethodFlags(dex_file, kConstructors[i], kAccAbstract); |
| RemoveCode(dex_file, kConstructors[i]); |
| }, |
| "must not be abstract or native"); |
| } |
| // <init> may only have (modulo ignored): |
| // kAccPrivate | kAccProtected | kAccPublic | kAccStrict | kAccVarargs | kAccSynthetic |
| static constexpr uint32_t kInitAllowed[] = { |
| 0, |
| kAccPrivate, |
| kAccProtected, |
| kAccPublic, |
| kAccStrict, |
| kAccVarargs, |
| kAccSynthetic |
| }; |
| for (size_t i = 0; i < arraysize(kInitAllowed); ++i) { |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "init_allowed_flags", |
| [&](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic); |
| OrMaskToMethodFlags(dex_file, "<init>", kInitAllowed[i]); |
| }, |
| nullptr); |
| } |
| // Only one of public-private-protected. |
| for (size_t i = 1; i < 8; ++i) { |
| if (POPCOUNT(i) < 2) { |
| continue; |
| } |
| // Technically the flags match, but just be defensive here. |
| uint32_t mask = ((i & 1) != 0 ? kAccPrivate : 0) | |
| ((i & 2) != 0 ? kAccProtected : 0) | |
| ((i & 4) != 0 ? kAccPublic : 0); |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "init_one_of_ppp", |
| [&](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic); |
| OrMaskToMethodFlags(dex_file, "<init>", mask); |
| }, |
| "Method may have only one of public/protected/private"); |
| } |
| // <init> doesn't allow |
| // kAccStatic | kAccFinal | kAccSynchronized | kAccBridge |
| // Need to handle static separately as it has its own error message. |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "init_not_allowed_flags", |
| [&](DexFile* dex_file) { |
| MakeDexVersion37(dex_file); |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic); |
| OrMaskToMethodFlags(dex_file, "<init>", kAccStatic); |
| }, |
| "Constructor 1(LMethodFlags;.<init>) is not flagged correctly wrt/ static"); |
| static constexpr uint32_t kInitNotAllowed[] = { |
| kAccFinal, |
| kAccSynchronized, |
| kAccBridge |
| }; |
| for (size_t i = 0; i < arraysize(kInitNotAllowed); ++i) { |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "init_not_allowed_flags", |
| [&](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToMethodFlags(dex_file, "<init>", ~kAccPublic); |
| OrMaskToMethodFlags(dex_file, "<init>", kInitNotAllowed[i]); |
| }, |
| "Constructor 1(LMethodFlags;.<init>) flagged inappropriately"); |
| } |
| } |
| |
| TEST_F(DexFileVerifierTest, MethodAccessFlagsMethods) { |
| constexpr const char* kMethods[] = { "foo", "bar"}; |
| for (size_t i = 0; i < arraysize(kMethods); ++i) { |
| // Make sure we reject non-constructors marked as constructors. |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "method_flags_non_constructor", |
| [&](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| OrMaskToMethodFlags(dex_file, kMethods[i], kAccConstructor); |
| }, |
| "is marked constructor, but doesn't match name"); |
| |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "method_flags_native_with_code", |
| [&](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| OrMaskToMethodFlags(dex_file, kMethods[i], kAccNative); |
| }, |
| "has code, but is marked native or abstract"); |
| |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "method_flags_abstract_with_code", |
| [&](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract); |
| }, |
| "has code, but is marked native or abstract"); |
| |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "method_flags_non_abstract_native_no_code", |
| [&](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| RemoveCode(dex_file, kMethods[i]); |
| }, |
| "has no code, but is not marked native or abstract"); |
| |
| // Abstract methods may not have the following flags. |
| constexpr uint32_t kAbstractDisallowed[] = { |
| kAccPrivate, |
| kAccStatic, |
| kAccFinal, |
| kAccNative, |
| kAccStrict, |
| kAccSynchronized, |
| }; |
| for (size_t j = 0; j < arraysize(kAbstractDisallowed); ++j) { |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "method_flags_abstract_and_disallowed_no_code", |
| [&](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| RemoveCode(dex_file, kMethods[i]); |
| |
| // Can't check private and static with foo, as it's in the virtual list and gives a |
| // different error. |
| if (((GetMethodFlags(dex_file, kMethods[i]) & kAccPublic) != 0) && |
| ((kAbstractDisallowed[j] & (kAccPrivate | kAccStatic)) != 0)) { |
| // Use another breaking flag. |
| OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract | kAccFinal); |
| } else { |
| OrMaskToMethodFlags(dex_file, kMethods[i], kAccAbstract | kAbstractDisallowed[j]); |
| } |
| }, |
| "has disallowed access flags"); |
| } |
| |
| // Only one of public-private-protected. |
| for (size_t j = 1; j < 8; ++j) { |
| if (POPCOUNT(j) < 2) { |
| continue; |
| } |
| // Technically the flags match, but just be defensive here. |
| uint32_t mask = ((j & 1) != 0 ? kAccPrivate : 0) | |
| ((j & 2) != 0 ? kAccProtected : 0) | |
| ((j & 4) != 0 ? kAccPublic : 0); |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "method_flags_one_of_ppp", |
| [&](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToMethodFlags(dex_file, kMethods[i], ~kAccPublic); |
| OrMaskToMethodFlags(dex_file, kMethods[i], mask); |
| }, |
| "Method may have only one of public/protected/private"); |
| } |
| } |
| } |
| |
| TEST_F(DexFileVerifierTest, MethodAccessFlagsIgnoredOK) { |
| constexpr const char* kMethods[] = { "<clinit>", "<init>", "foo", "bar"}; |
| for (size_t i = 0; i < arraysize(kMethods); ++i) { |
| // All interesting method flags, other flags are to be ignored. |
| constexpr uint32_t kAllMethodFlags = |
| kAccPublic | |
| kAccPrivate | |
| kAccProtected | |
| kAccStatic | |
| kAccFinal | |
| kAccSynchronized | |
| kAccBridge | |
| kAccVarargs | |
| kAccNative | |
| kAccAbstract | |
| kAccStrict | |
| kAccSynthetic; |
| constexpr uint32_t kIgnoredMask = ~kAllMethodFlags & 0xFFFF; |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "method_flags_ignored", |
| [&](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToMethodFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| OrMaskToMethodFlags(dex_file, kMethods[i], kIgnoredMask); |
| }, |
| nullptr); |
| } |
| } |
| |
| TEST_F(DexFileVerifierTest, B28552165) { |
| // Regression test for bad error string retrieval in different situations. |
| // Using invalid access flags to trigger the error. |
| VerifyModification( |
| kMethodFlagsTestDex, |
| "b28552165", |
| [](DexFile* dex_file) { |
| OrMaskToMethodFlags(dex_file, "foo", kAccPublic | kAccProtected); |
| }, |
| "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 |
| // just easier to break up bad cases. |
| |
| // Standard interface. Use declared-synchronized again for 3B encoding. |
| // |
| // .class public interface LInterfaceMethodFlags; |
| // .super Ljava/lang/Object; |
| // |
| // .method public static constructor <clinit>()V |
| // .registers 1 |
| // return-void |
| // .end method |
| // |
| // .method public abstract declared-synchronized foo()V |
| // .end method |
| static const char kMethodFlagsInterface[] = |
| "ZGV4CjAzNQCOM0odZ5bws1d9GSmumXaK5iE/7XxFpOm8AQAAcAAAAHhWNBIAAAAAAAAAADQBAAAF" |
| "AAAAcAAAAAMAAACEAAAAAQAAAJAAAAAAAAAAAAAAAAIAAACcAAAAAQAAAKwAAADwAAAAzAAAAMwA" |
| "AADWAAAA7gAAAAIBAAAFAQAAAQAAAAIAAAADAAAAAwAAAAIAAAAAAAAAAAAAAAAAAAAAAAAABAAA" |
| "AAAAAAABAgAAAQAAAAAAAAD/////AAAAACIBAAAAAAAACDxjbGluaXQ+ABZMSW50ZXJmYWNlTWV0" |
| "aG9kRmxhZ3M7ABJMamF2YS9sYW5nL09iamVjdDsAAVYAA2ZvbwAAAAAAAAABAAAAAAAAAAAAAAAB" |
| "AAAADgAAAAEBAImABJACAYGICAAAAAALAAAAAAAAAAEAAAAAAAAAAQAAAAUAAABwAAAAAgAAAAMA" |
| "AACEAAAAAwAAAAEAAACQAAAABQAAAAIAAACcAAAABgAAAAEAAACsAAAAAiAAAAUAAADMAAAAAxAA" |
| "AAEAAAAMAQAAASAAAAEAAAAQAQAAACAAAAEAAAAiAQAAABAAAAEAAAA0AQAA"; |
| |
| // To simplify generation of interesting "sub-states" of src_value, allow a "simple" mask to apply |
| // to a src_value, such that mask bit 0 applies to the lowest set bit in src_value, and so on. |
| static uint32_t ApplyMaskShifted(uint32_t src_value, uint32_t mask) { |
| uint32_t result = 0; |
| uint32_t mask_index = 0; |
| while (src_value != 0) { |
| uint32_t index = CTZ(src_value); |
| if (((src_value & (1 << index)) != 0) && |
| ((mask & (1 << mask_index)) != 0)) { |
| result |= (1 << index); |
| } |
| src_value &= ~(1 << index); |
| mask_index++; |
| } |
| return result; |
| } |
| |
| TEST_F(DexFileVerifierTest, MethodAccessFlagsInterfaces) { |
| VerifyModification( |
| kMethodFlagsInterface, |
| "method_flags_interface_ok", |
| [](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| }, |
| nullptr); |
| VerifyModification( |
| kMethodFlagsInterface, |
| "method_flags_interface_ok37", |
| [](DexFile* dex_file) { |
| MakeDexVersion37(dex_file); |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| }, |
| nullptr); |
| |
| VerifyModification( |
| kMethodFlagsInterface, |
| "method_flags_interface_non_public", |
| [](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic); |
| }, |
| nullptr); // Should be allowed in older dex versions for backwards compatibility. |
| VerifyModification( |
| kMethodFlagsInterface, |
| "method_flags_interface_non_public", |
| [](DexFile* dex_file) { |
| MakeDexVersion37(dex_file); |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic); |
| }, |
| "Interface virtual method 1(LInterfaceMethodFlags;.foo) is not public"); |
| |
| VerifyModification( |
| kMethodFlagsInterface, |
| "method_flags_interface_non_abstract", |
| [](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccAbstract); |
| }, |
| "Method 1(LInterfaceMethodFlags;.foo) has no code, but is not marked native or abstract"); |
| |
| VerifyModification( |
| kMethodFlagsInterface, |
| "method_flags_interface_static", |
| [](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| OrMaskToMethodFlags(dex_file, "foo", kAccStatic); |
| }, |
| "Direct/virtual method 1(LInterfaceMethodFlags;.foo) not in expected list 0"); |
| VerifyModification( |
| kMethodFlagsInterface, |
| "method_flags_interface_private", |
| [](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic); |
| OrMaskToMethodFlags(dex_file, "foo", kAccPrivate); |
| }, |
| "Direct/virtual method 1(LInterfaceMethodFlags;.foo) not in expected list 0"); |
| |
| VerifyModification( |
| kMethodFlagsInterface, |
| "method_flags_interface_non_public", |
| [](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic); |
| }, |
| nullptr); // Should be allowed in older dex versions for backwards compatibility. |
| VerifyModification( |
| kMethodFlagsInterface, |
| "method_flags_interface_non_public", |
| [](DexFile* dex_file) { |
| MakeDexVersion37(dex_file); |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic); |
| }, |
| "Interface virtual method 1(LInterfaceMethodFlags;.foo) is not public"); |
| |
| VerifyModification( |
| kMethodFlagsInterface, |
| "method_flags_interface_protected", |
| [](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic); |
| OrMaskToMethodFlags(dex_file, "foo", kAccProtected); |
| }, |
| nullptr); // Should be allowed in older dex versions for backwards compatibility. |
| VerifyModification( |
| kMethodFlagsInterface, |
| "method_flags_interface_protected", |
| [](DexFile* dex_file) { |
| MakeDexVersion37(dex_file); |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic); |
| OrMaskToMethodFlags(dex_file, "foo", kAccProtected); |
| }, |
| "Interface virtual method 1(LInterfaceMethodFlags;.foo) is not public"); |
| |
| constexpr uint32_t kAllMethodFlags = |
| kAccPublic | |
| kAccPrivate | |
| kAccProtected | |
| kAccStatic | |
| kAccFinal | |
| kAccSynchronized | |
| kAccBridge | |
| kAccVarargs | |
| kAccNative | |
| kAccAbstract | |
| kAccStrict | |
| kAccSynthetic; |
| constexpr uint32_t kInterfaceMethodFlags = |
| kAccPublic | kAccAbstract | kAccVarargs | kAccBridge | kAccSynthetic; |
| constexpr uint32_t kInterfaceDisallowed = kAllMethodFlags & |
| ~kInterfaceMethodFlags & |
| // Already tested, needed to be separate. |
| ~kAccStatic & |
| ~kAccPrivate & |
| ~kAccProtected; |
| static_assert(kInterfaceDisallowed != 0, "There should be disallowed flags."); |
| |
| uint32_t bits = POPCOUNT(kInterfaceDisallowed); |
| for (uint32_t i = 1; i < (1u << bits); ++i) { |
| VerifyModification( |
| kMethodFlagsInterface, |
| "method_flags_interface_non_abstract", |
| [&](DexFile* dex_file) { |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i); |
| if ((mask & kAccProtected) != 0) { |
| mask &= ~kAccProtected; |
| ApplyMaskToMethodFlags(dex_file, "foo", ~kAccPublic); |
| } |
| OrMaskToMethodFlags(dex_file, "foo", mask); |
| }, |
| "Abstract method 1(LInterfaceMethodFlags;.foo) has disallowed access flags"); |
| } |
| } |
| |
| /////////////////////////////////////////////////////////////////// |
| |
| // Field flags. |
| |
| // Find the method data for the first method with the given name (from class 0). Note: the pointer |
| // is to the access flags, so that the caller doesn't have to handle the leb128-encoded method-index |
| // delta. |
| static const uint8_t* FindFieldData(const DexFile* dex_file, const char* name) { |
| ClassAccessor accessor(*dex_file, dex_file->GetClassDef(0)); |
| |
| for (const ClassAccessor::Field& field : accessor.GetFields()) { |
| uint32_t field_index = field.GetIndex(); |
| dex::StringIndex name_index = dex_file->GetFieldId(field_index).name_idx_; |
| const dex::StringId& string_id = dex_file->GetStringId(name_index); |
| const char* str = dex_file->GetStringData(string_id); |
| if (strcmp(name, str) == 0) { |
| // Go to the back of the access flags. |
| return ReverseSearchUnsignedLeb128(field.GetDataPointer()); |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| // Set the method flags to the given value. |
| static void SetFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) { |
| uint8_t* field_flags_ptr = const_cast<uint8_t*>(FindFieldData(dex_file, field)); |
| CHECK(field_flags_ptr != nullptr) << field; |
| |
| // Unroll this, as we only have three bytes, anyways. |
| uint8_t base1 = static_cast<uint8_t>(mask & 0x7F); |
| *(field_flags_ptr++) = (base1 | 0x80); |
| mask >>= 7; |
| |
| uint8_t base2 = static_cast<uint8_t>(mask & 0x7F); |
| *(field_flags_ptr++) = (base2 | 0x80); |
| mask >>= 7; |
| |
| uint8_t base3 = static_cast<uint8_t>(mask & 0x7F); |
| *field_flags_ptr = base3; |
| } |
| |
| static uint32_t GetFieldFlags(DexFile* dex_file, const char* field) { |
| const uint8_t* field_flags_ptr = const_cast<uint8_t*>(FindFieldData(dex_file, field)); |
| CHECK(field_flags_ptr != nullptr) << field; |
| return DecodeUnsignedLeb128(&field_flags_ptr); |
| } |
| |
| // Apply the given mask to method flags. |
| static void ApplyMaskToFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) { |
| uint32_t value = GetFieldFlags(dex_file, field); |
| value &= mask; |
| SetFieldFlags(dex_file, field, value); |
| } |
| |
| // Apply the given mask to method flags. |
| static void OrMaskToFieldFlags(DexFile* dex_file, const char* field, uint32_t mask) { |
| uint32_t value = GetFieldFlags(dex_file, field); |
| value |= mask; |
| SetFieldFlags(dex_file, field, value); |
| } |
| |
| // Standard class. Use declared-synchronized again for 3B encoding. |
| // |
| // .class public LFieldFlags; |
| // .super Ljava/lang/Object; |
| // |
| // .field declared-synchronized public foo:I |
| // |
| // .field declared-synchronized public static bar:I |
| |
| static const char kFieldFlagsTestDex[] = |
| "ZGV4CjAzNQBtLw7hydbfv4TdXidZyzAB70W7w3vnYJRwAQAAcAAAAHhWNBIAAAAAAAAAAAABAAAF" |
| "AAAAcAAAAAMAAACEAAAAAAAAAAAAAAACAAAAkAAAAAAAAAAAAAAAAQAAAKAAAACwAAAAwAAAAMAA" |
| "AADDAAAA0QAAAOUAAADqAAAAAAAAAAEAAAACAAAAAQAAAAMAAAABAAAABAAAAAEAAAABAAAAAgAA" |
| "AAAAAAD/////AAAAAPQAAAAAAAAAAUkADExGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7" |
| "AANiYXIAA2ZvbwAAAAAAAAEBAAAAiYAIAYGACAkAAAAAAAAAAQAAAAAAAAABAAAABQAAAHAAAAAC" |
| "AAAAAwAAAIQAAAAEAAAAAgAAAJAAAAAGAAAAAQAAAKAAAAACIAAABQAAAMAAAAADEAAAAQAAAPAA" |
| "AAAAIAAAAQAAAPQAAAAAEAAAAQAAAAABAAA="; |
| |
| TEST_F(DexFileVerifierTest, FieldAccessFlagsBase) { |
| // Check that it's OK when the wrong declared-synchronized flag is removed from "foo." |
| VerifyModification( |
| kFieldFlagsTestDex, |
| "field_flags_ok", |
| [](DexFile* dex_file) { |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| }, |
| nullptr); |
| } |
| |
| TEST_F(DexFileVerifierTest, FieldAccessFlagsWrongList) { |
| // Mark the field so that it should appear in the opposite list (instance vs static). |
| VerifyModification( |
| kFieldFlagsTestDex, |
| "field_flags_wrong_list", |
| [](DexFile* dex_file) { |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| OrMaskToFieldFlags(dex_file, "foo", kAccStatic); |
| }, |
| "Static/instance field not in expected list"); |
| VerifyModification( |
| kFieldFlagsTestDex, |
| "field_flags_wrong_list", |
| [](DexFile* dex_file) { |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToFieldFlags(dex_file, "bar", ~kAccStatic); |
| }, |
| "Static/instance field not in expected list"); |
| } |
| |
| TEST_F(DexFileVerifierTest, FieldAccessFlagsPPP) { |
| static const char* kFields[] = { "foo", "bar" }; |
| for (size_t i = 0; i < arraysize(kFields); ++i) { |
| // Should be OK to remove public. |
| VerifyModification( |
| kFieldFlagsTestDex, |
| "field_flags_non_public", |
| [&](DexFile* dex_file) { |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToFieldFlags(dex_file, kFields[i], ~kAccPublic); |
| }, |
| nullptr); |
| constexpr uint32_t kAccFlags = kAccPublic | kAccPrivate | kAccProtected; |
| uint32_t bits = POPCOUNT(kAccFlags); |
| for (uint32_t j = 1; j < (1u << bits); ++j) { |
| if (POPCOUNT(j) < 2) { |
| continue; |
| } |
| VerifyModification( |
| kFieldFlagsTestDex, |
| "field_flags_ppp", |
| [&](DexFile* dex_file) { |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToFieldFlags(dex_file, kFields[i], ~kAccPublic); |
| uint32_t mask = ApplyMaskShifted(kAccFlags, j); |
| OrMaskToFieldFlags(dex_file, kFields[i], mask); |
| }, |
| "Field may have only one of public/protected/private"); |
| } |
| } |
| } |
| |
| TEST_F(DexFileVerifierTest, FieldAccessFlagsIgnoredOK) { |
| constexpr const char* kFields[] = { "foo", "bar"}; |
| for (size_t i = 0; i < arraysize(kFields); ++i) { |
| // All interesting method flags, other flags are to be ignored. |
| constexpr uint32_t kAllFieldFlags = |
| kAccPublic | |
| kAccPrivate | |
| kAccProtected | |
| kAccStatic | |
| kAccFinal | |
| kAccVolatile | |
| kAccTransient | |
| kAccSynthetic | |
| kAccEnum; |
| constexpr uint32_t kIgnoredMask = ~kAllFieldFlags & 0xFFFF; |
| VerifyModification( |
| kFieldFlagsTestDex, |
| "field_flags_ignored", |
| [&](DexFile* dex_file) { |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| OrMaskToFieldFlags(dex_file, kFields[i], kIgnoredMask); |
| }, |
| nullptr); |
| } |
| } |
| |
| TEST_F(DexFileVerifierTest, FieldAccessFlagsVolatileFinal) { |
| constexpr const char* kFields[] = { "foo", "bar"}; |
| for (size_t i = 0; i < arraysize(kFields); ++i) { |
| VerifyModification( |
| kFieldFlagsTestDex, |
| "field_flags_final_and_volatile", |
| [&](DexFile* dex_file) { |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| ApplyMaskToFieldFlags(dex_file, "bar", ~kAccDeclaredSynchronized); |
| |
| OrMaskToFieldFlags(dex_file, kFields[i], kAccVolatile | kAccFinal); |
| }, |
| "Fields may not be volatile and final"); |
| } |
| } |
| |
| // Standard interface. Needs to be separate from class as interfaces do not allow instance fields. |
| // Use declared-synchronized again for 3B encoding. |
| // |
| // .class public interface LInterfaceFieldFlags; |
| // .super Ljava/lang/Object; |
| // |
| // .field declared-synchronized public static final foo:I |
| |
| static const char kFieldFlagsInterfaceTestDex[] = |
| "ZGV4CjAzNQCVMHfEimR1zZPk6hl6O9GPAYqkl3u0umFkAQAAcAAAAHhWNBIAAAAAAAAAAPQAAAAE" |
| "AAAAcAAAAAMAAACAAAAAAAAAAAAAAAABAAAAjAAAAAAAAAAAAAAAAQAAAJQAAACwAAAAtAAAALQA" |
| "AAC3AAAAzgAAAOIAAAAAAAAAAQAAAAIAAAABAAAAAwAAAAEAAAABAgAAAgAAAAAAAAD/////AAAA" |
| "AOwAAAAAAAAAAUkAFUxJbnRlcmZhY2VGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7AANm" |
| "b28AAAAAAAABAAAAAJmACAkAAAAAAAAAAQAAAAAAAAABAAAABAAAAHAAAAACAAAAAwAAAIAAAAAE" |
| "AAAAAQAAAIwAAAAGAAAAAQAAAJQAAAACIAAABAAAALQAAAADEAAAAQAAAOgAAAAAIAAAAQAAAOwA" |
| "AAAAEAAAAQAAAPQAAAA="; |
| |
| TEST_F(DexFileVerifierTest, FieldAccessFlagsInterface) { |
| VerifyModification( |
| kFieldFlagsInterfaceTestDex, |
| "field_flags_interface", |
| [](DexFile* dex_file) { |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| }, |
| nullptr); |
| VerifyModification( |
| kFieldFlagsInterfaceTestDex, |
| "field_flags_interface", |
| [](DexFile* dex_file) { |
| MakeDexVersion37(dex_file); |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| }, |
| nullptr); |
| |
| VerifyModification( |
| kFieldFlagsInterfaceTestDex, |
| "field_flags_interface_non_public", |
| [](DexFile* dex_file) { |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic); |
| }, |
| nullptr); // Should be allowed in older dex versions for backwards compatibility. |
| VerifyModification( |
| kFieldFlagsInterfaceTestDex, |
| "field_flags_interface_non_public", |
| [](DexFile* dex_file) { |
| MakeDexVersion37(dex_file); |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic); |
| }, |
| "Interface field is not public final static"); |
| |
| VerifyModification( |
| kFieldFlagsInterfaceTestDex, |
| "field_flags_interface_non_final", |
| [](DexFile* dex_file) { |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccFinal); |
| }, |
| nullptr); // Should be allowed in older dex versions for backwards compatibility. |
| VerifyModification( |
| kFieldFlagsInterfaceTestDex, |
| "field_flags_interface_non_final", |
| [](DexFile* dex_file) { |
| MakeDexVersion37(dex_file); |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccFinal); |
| }, |
| "Interface field is not public final static"); |
| |
| VerifyModification( |
| kFieldFlagsInterfaceTestDex, |
| "field_flags_interface_protected", |
| [](DexFile* dex_file) { |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic); |
| OrMaskToFieldFlags(dex_file, "foo", kAccProtected); |
| }, |
| nullptr); // Should be allowed in older dex versions for backwards compatibility. |
| VerifyModification( |
| kFieldFlagsInterfaceTestDex, |
| "field_flags_interface_protected", |
| [](DexFile* dex_file) { |
| MakeDexVersion37(dex_file); |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic); |
| OrMaskToFieldFlags(dex_file, "foo", kAccProtected); |
| }, |
| "Interface field is not public final static"); |
| |
| VerifyModification( |
| kFieldFlagsInterfaceTestDex, |
| "field_flags_interface_private", |
| [](DexFile* dex_file) { |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic); |
| OrMaskToFieldFlags(dex_file, "foo", kAccPrivate); |
| }, |
| nullptr); // Should be allowed in older dex versions for backwards compatibility. |
| VerifyModification( |
| kFieldFlagsInterfaceTestDex, |
| "field_flags_interface_private", |
| [](DexFile* dex_file) { |
| MakeDexVersion37(dex_file); |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic); |
| OrMaskToFieldFlags(dex_file, "foo", kAccPrivate); |
| }, |
| "Interface field is not public final static"); |
| |
| VerifyModification( |
| kFieldFlagsInterfaceTestDex, |
| "field_flags_interface_synthetic", |
| [](DexFile* dex_file) { |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| OrMaskToFieldFlags(dex_file, "foo", kAccSynthetic); |
| }, |
| nullptr); |
| |
| constexpr uint32_t kAllFieldFlags = |
| kAccPublic | |
| kAccPrivate | |
| kAccProtected | |
| kAccStatic | |
| kAccFinal | |
| kAccVolatile | |
| kAccTransient | |
| kAccSynthetic | |
| kAccEnum; |
| constexpr uint32_t kInterfaceFieldFlags = kAccPublic | kAccStatic | kAccFinal | kAccSynthetic; |
| constexpr uint32_t kInterfaceDisallowed = kAllFieldFlags & |
| ~kInterfaceFieldFlags & |
| ~kAccProtected & |
| ~kAccPrivate; |
| static_assert(kInterfaceDisallowed != 0, "There should be disallowed flags."); |
| |
| uint32_t bits = POPCOUNT(kInterfaceDisallowed); |
| for (uint32_t i = 1; i < (1u << bits); ++i) { |
| VerifyModification( |
| kFieldFlagsInterfaceTestDex, |
| "field_flags_interface_disallowed", |
| [&](DexFile* dex_file) { |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i); |
| if ((mask & kAccProtected) != 0) { |
| mask &= ~kAccProtected; |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic); |
| } |
| OrMaskToFieldFlags(dex_file, "foo", mask); |
| }, |
| nullptr); // Should be allowed in older dex versions for backwards compatibility. |
| VerifyModification( |
| kFieldFlagsInterfaceTestDex, |
| "field_flags_interface_disallowed", |
| [&](DexFile* dex_file) { |
| MakeDexVersion37(dex_file); |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| |
| uint32_t mask = ApplyMaskShifted(kInterfaceDisallowed, i); |
| if ((mask & kAccProtected) != 0) { |
| mask &= ~kAccProtected; |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccPublic); |
| } |
| OrMaskToFieldFlags(dex_file, "foo", mask); |
| }, |
| "Interface field has disallowed flag"); |
| } |
| } |
| |
| // Standard bad interface. Needs to be separate from class as interfaces do not allow instance |
| // fields. Use declared-synchronized again for 3B encoding. |
| // |
| // .class public interface LInterfaceFieldFlags; |
| // .super Ljava/lang/Object; |
| // |
| // .field declared-synchronized public final foo:I |
| |
| static const char kFieldFlagsInterfaceBadTestDex[] = |
| "ZGV4CjAzNQByMUnqYKHBkUpvvNp+9CnZ2VyDkKnRN6VkAQAAcAAAAHhWNBIAAAAAAAAAAPQAAAAE" |
| "AAAAcAAAAAMAAACAAAAAAAAAAAAAAAABAAAAjAAAAAAAAAAAAAAAAQAAAJQAAACwAAAAtAAAALQA" |
| "AAC3AAAAzgAAAOIAAAAAAAAAAQAAAAIAAAABAAAAAwAAAAEAAAABAgAAAgAAAAAAAAD/////AAAA" |
| "AOwAAAAAAAAAAUkAFUxJbnRlcmZhY2VGaWVsZEZsYWdzOwASTGphdmEvbGFuZy9PYmplY3Q7AANm" |
| "b28AAAAAAAAAAQAAAJGACAkAAAAAAAAAAQAAAAAAAAABAAAABAAAAHAAAAACAAAAAwAAAIAAAAAE" |
| "AAAAAQAAAIwAAAAGAAAAAQAAAJQAAAACIAAABAAAALQAAAADEAAAAQAAAOgAAAAAIAAAAQAAAOwA" |
| "AAAAEAAAAQAAAPQAAAA="; |
| |
| TEST_F(DexFileVerifierTest, FieldAccessFlagsInterfaceNonStatic) { |
| VerifyModification( |
| kFieldFlagsInterfaceBadTestDex, |
| "field_flags_interface_non_static", |
| [](DexFile* dex_file) { |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| }, |
| nullptr); // Should be allowed in older dex versions for backwards compatibility. |
| VerifyModification( |
| kFieldFlagsInterfaceBadTestDex, |
| "field_flags_interface_non_static", |
| [](DexFile* dex_file) { |
| MakeDexVersion37(dex_file); |
| ApplyMaskToFieldFlags(dex_file, "foo", ~kAccDeclaredSynchronized); |
| }, |
| "Interface field is not public final static"); |
| } |
| |
| // Generated from: |
| // |
| // .class public LTest; |
| // .super Ljava/lang/Object; |
| // .source "Test.java" |
| // |
| // .method public constructor <init>()V |
| // .registers 1 |
| // |
| // .prologue |
| // .line 1 |
| // invoke-direct {p0}, Ljava/lang/Object;-><init>()V |
| // |
| // return-void |
| // .end method |
| // |
| // .method public static main()V |
| // .registers 2 |
| // |
| // const-string v0, "a" |
| // const-string v0, "b" |
| // const-string v0, "c" |
| // const-string v0, "d" |
| // const-string v0, "e" |
| // const-string v0, "f" |
| // const-string v0, "g" |
| // const-string v0, "h" |
| // const-string v0, "i" |
| // const-string v0, "j" |
| // const-string v0, "k" |
| // |
| // .local v1, "local_var":Ljava/lang/String; |
| // const-string v1, "test" |
| // .end method |
| |
| static const char kDebugInfoTestDex[] = |
| "ZGV4CjAzNQCHRkHix2eIMQgvLD/0VGrlllZLo0Rb6VyUAgAAcAAAAHhWNBIAAAAAAAAAAAwCAAAU" |
| "AAAAcAAAAAQAAADAAAAAAQAAANAAAAAAAAAAAAAAAAMAAADcAAAAAQAAAPQAAACAAQAAFAEAABQB" |
| "AAAcAQAAJAEAADgBAABMAQAAVwEAAFoBAABdAQAAYAEAAGMBAABmAQAAaQEAAGwBAABvAQAAcgEA" |
| "AHUBAAB4AQAAewEAAIYBAACMAQAAAQAAAAIAAAADAAAABQAAAAUAAAADAAAAAAAAAAAAAAAAAAAA" |
| "AAAAABIAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAAEAAAAAAAAAPwBAAAAAAAABjxpbml0PgAG" |
| "TFRlc3Q7ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZhL2xhbmcvU3RyaW5nOwAJVGVzdC5qYXZh" |
| "AAFWAAFhAAFiAAFjAAFkAAFlAAFmAAFnAAFoAAFpAAFqAAFrAAlsb2NhbF92YXIABG1haW4ABHRl" |
| "c3QAAAABAAcOAAAAARYDARIDAAAAAQABAAEAAACUAQAABAAAAHAQAgAAAA4AAgAAAAAAAACZAQAA" |
| "GAAAABoABgAaAAcAGgAIABoACQAaAAoAGgALABoADAAaAA0AGgAOABoADwAaABAAGgETAAAAAgAA" |
| "gYAEpAMBCbwDAAALAAAAAAAAAAEAAAAAAAAAAQAAABQAAABwAAAAAgAAAAQAAADAAAAAAwAAAAEA" |
| "AADQAAAABQAAAAMAAADcAAAABgAAAAEAAAD0AAAAAiAAABQAAAAUAQAAAyAAAAIAAACUAQAAASAA" |
| "AAIAAACkAQAAACAAAAEAAAD8AQAAABAAAAEAAAAMAgAA"; |
| |
| TEST_F(DexFileVerifierTest, DebugInfoTypeIdxTest) { |
| { |
| // The input dex file should be good before modification. |
| std::string error_msg; |
| std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kDebugInfoTestDex, |
| kLocationString, |
| &error_msg)); |
| ASSERT_TRUE(raw.get() != nullptr) << error_msg; |
| } |
| |
| // Modify the debug information entry. |
| VerifyModification( |
| kDebugInfoTestDex, |
| "debug_start_type_idx", |
| [](DexFile* dex_file) { |
| *(const_cast<uint8_t*>(dex_file->Begin()) + 416) = 0x14U; |
| }, |
| "DBG_START_LOCAL type_idx"); |
| } |
| |
| TEST_F(DexFileVerifierTest, SectionAlignment) { |
| { |
| // The input dex file should be good before modification. Any file is fine, as long as it |
| // uses all sections. |
| std::string error_msg; |
| std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kGoodTestDex, |
| kLocationString, |
| &error_msg)); |
| ASSERT_TRUE(raw.get() != nullptr) << error_msg; |
| } |
| |
| // Modify all section offsets to be unaligned. |
| constexpr size_t kSections = 7; |
| for (size_t i = 0; i < kSections; ++i) { |
| VerifyModification( |
| kGoodTestDex, |
| "section_align", |
| [&](DexFile* dex_file) { |
| DexFile::Header* header = const_cast<DexFile::Header*>( |
| reinterpret_cast<const DexFile::Header*>(dex_file->Begin())); |
| uint32_t* off_ptr; |
| switch (i) { |
| case 0: |
| off_ptr = &header->map_off_; |
| break; |
| case 1: |
| off_ptr = &header->string_ids_off_; |
| break; |
| case 2: |
| off_ptr = &header->type_ids_off_; |
| break; |
| case 3: |
| off_ptr = &header->proto_ids_off_; |
| break; |
| case 4: |
| off_ptr = &header->field_ids_off_; |
| break; |
| case 5: |
| off_ptr = &header->method_ids_off_; |
| break; |
| case 6: |
| off_ptr = &header->class_defs_off_; |
| break; |
| |
| static_assert(kSections == 7, "kSections is wrong"); |
| default: |
| LOG(FATAL) << "Unexpected section"; |
| UNREACHABLE(); |
| } |
| ASSERT_TRUE(off_ptr != nullptr); |
| ASSERT_NE(*off_ptr, 0U) << i; // Should already contain a value (in use). |
| (*off_ptr)++; // Add one, which should misalign it (all the sections |
| // above are aligned by 4). |
| }, |
| "should be aligned by 4 for"); |
| } |
| } |
| |
| // Generated from |
| // |
| // .class LOverloading; |
| // |
| // .super Ljava/lang/Object; |
| // |
| // .method public static foo()V |
| // .registers 1 |
| // return-void |
| // .end method |
| // |
| // .method public static foo(I)V |
| // .registers 1 |
| // return-void |
| // .end method |
| static const char kProtoOrderingTestDex[] = |
| "ZGV4CjAzNQA1L+ABE6voQ9Lr4Ci//efB53oGnDr5PinsAQAAcAAAAHhWNBIAAAAAAAAAAFgBAAAG" |
| "AAAAcAAAAAQAAACIAAAAAgAAAJgAAAAAAAAAAAAAAAIAAACwAAAAAQAAAMAAAAAMAQAA4AAAAOAA" |
| "AADjAAAA8gAAAAYBAAAJAQAADQEAAAAAAAABAAAAAgAAAAMAAAADAAAAAwAAAAAAAAAEAAAAAwAA" |
| "ABQBAAABAAAABQAAAAEAAQAFAAAAAQAAAAAAAAACAAAAAAAAAP////8AAAAASgEAAAAAAAABSQAN" |
| "TE92ZXJsb2FkaW5nOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAJWSQADZm9vAAAAAQAAAAAAAAAA" |
| "AAAAAAAAAAEAAAAAAAAAAAAAAAEAAAAOAAAAAQABAAAAAAAAAAAAAQAAAA4AAAACAAAJpAIBCbgC" |
| "AAAMAAAAAAAAAAEAAAAAAAAAAQAAAAYAAABwAAAAAgAAAAQAAACIAAAAAwAAAAIAAACYAAAABQAA" |
| "AAIAAACwAAAABgAAAAEAAADAAAAAAiAAAAYAAADgAAAAARAAAAEAAAAUAQAAAxAAAAIAAAAcAQAA" |
| "ASAAAAIAAAAkAQAAACAAAAEAAABKAQAAABAAAAEAAABYAQAA"; |
| |
| TEST_F(DexFileVerifierTest, ProtoOrdering) { |
| { |
| // The input dex file should be good before modification. |
| std::string error_msg; |
| std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kProtoOrderingTestDex, |
| kLocationString, |
| &error_msg)); |
| ASSERT_TRUE(raw.get() != nullptr) << error_msg; |
| } |
| |
| // Modify the order of the ProtoIds for two overloads of "foo" with the |
| // same return type and one having longer parameter list than the other. |
| for (size_t i = 0; i != 2; ++i) { |
| VerifyModification( |
| kProtoOrderingTestDex, |
| "proto_ordering", |
| [i](DexFile* dex_file) { |
| uint32_t method_idx; |
| const uint8_t* data = FindMethodData(dex_file, "foo", &method_idx); |
| CHECK(data != nullptr); |
| // There should be 2 methods called "foo". |
| CHECK_LT(method_idx + 1u, dex_file->NumMethodIds()); |
| CHECK_EQ(dex_file->GetMethodId(method_idx).name_idx_, |
| dex_file->GetMethodId(method_idx + 1).name_idx_); |
| CHECK_EQ(dex_file->GetMethodId(method_idx).proto_idx_.index_ + 1u, |
| dex_file->GetMethodId(method_idx + 1).proto_idx_.index_); |
| // Their return types should be the same. |
| dex::ProtoIndex proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_; |
| const dex::ProtoId& proto1 = dex_file->GetProtoId(proto1_idx); |
| dex::ProtoIndex proto2_idx(proto1_idx.index_ + 1u); |
| const dex::ProtoId& proto2 = dex_file->GetProtoId(proto2_idx); |
| CHECK_EQ(proto1.return_type_idx_, proto2.return_type_idx_); |
| // And the first should not have any parameters while the second should have some. |
| CHECK(!DexFileParameterIterator(*dex_file, proto1).HasNext()); |
| CHECK(DexFileParameterIterator(*dex_file, proto2).HasNext()); |
| if (i == 0) { |
| // Swap the proto parameters and shorties to break the ordering. |
| std::swap(const_cast<uint32_t&>(proto1.parameters_off_), |
| const_cast<uint32_t&>(proto2.parameters_off_)); |
| std::swap(const_cast<dex::StringIndex&>(proto1.shorty_idx_), |
| const_cast<dex::StringIndex&>(proto2.shorty_idx_)); |
| } else { |
| // Copy the proto parameters and shorty to create duplicate proto id. |
| const_cast<uint32_t&>(proto1.parameters_off_) = proto2.parameters_off_; |
| const_cast<dex::StringIndex&>(proto1.shorty_idx_) = proto2.shorty_idx_; |
| } |
| }, |
| "Out-of-order proto_id arguments"); |
| } |
| } |
| |
| // To generate a base64 encoded Dex file version 037 from Smali files, use: |
| // |
| // smali assemble --api 24 -o classes.dex class1.smali [class2.smali ...] |
| // base64 classes.dex >classes.dex.base64 |
| |
| // Dex file version 037 generated from: |
| // |
| // .class public LB28685551; |
| // .super LB28685551; |
| |
| static const char kClassExtendsItselfTestDex[] = |
| "ZGV4CjAzNwDeGbgRg1kb6swszpcTWrrOAALB++F4OPT0AAAAcAAAAHhWNBIAAAAAAAAAAKgAAAAB" |
| "AAAAcAAAAAEAAAB0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAHgAAABcAAAAmAAAAJgA" |
| "AAAAAAAAAAAAAAEAAAAAAAAAAAAAAP////8AAAAAAAAAAAAAAAALTEIyODY4NTU1MTsAAAAABgAA" |
| "AAAAAAABAAAAAAAAAAEAAAABAAAAcAAAAAIAAAABAAAAdAAAAAYAAAABAAAAeAAAAAIgAAABAAAA" |
| "mAAAAAAQAAABAAAAqAAAAA=="; |
| |
| TEST_F(DexFileVerifierTest, ClassExtendsItself) { |
| VerifyModification( |
| kClassExtendsItselfTestDex, |
| "class_extends_itself", |
| [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ }, |
| "Class with same type idx as its superclass: '0'"); |
| } |
| |
| // Dex file version 037 generated from: |
| // |
| // .class public LFoo; |
| // .super LBar; |
| // |
| // and: |
| // |
| // .class public LBar; |
| // .super LFoo; |
| |
| static const char kClassesExtendOneAnotherTestDex[] = |
| "ZGV4CjAzNwBXHSrwpDMwRBkg+L+JeQCuFNRLhQ86duEcAQAAcAAAAHhWNBIAAAAAAAAAANAAAAAC" |
| "AAAAcAAAAAIAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAIAAAABcAAAAwAAAAMAA" |
| "AADHAAAAAAAAAAEAAAABAAAAAQAAAAAAAAAAAAAA/////wAAAAAAAAAAAAAAAAAAAAABAAAAAQAA" |
| "AAAAAAD/////AAAAAAAAAAAAAAAABUxCYXI7AAVMRm9vOwAAAAYAAAAAAAAAAQAAAAAAAAABAAAA" |
| "AgAAAHAAAAACAAAAAgAAAHgAAAAGAAAAAgAAAIAAAAACIAAAAgAAAMAAAAAAEAAAAQAAANAAAAA="; |
| |
| TEST_F(DexFileVerifierTest, ClassesExtendOneAnother) { |
| VerifyModification( |
| kClassesExtendOneAnotherTestDex, |
| "classes_extend_one_another", |
| [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ }, |
| "Invalid class definition ordering: class with type idx: '1' defined before" |
| " superclass with type idx: '0'"); |
| } |
| |
| // Dex file version 037 generated from: |
| // |
| // .class public LAll; |
| // .super LYour; |
| // |
| // and: |
| // |
| // .class public LYour; |
| // .super LBase; |
| // |
| // and: |
| // |
| // .class public LBase; |
| // .super LAll; |
| |
| static const char kCircularClassInheritanceTestDex[] = |
| "ZGV4CjAzNwBMJxgP0SJz6oLXnKfl+J7lSEORLRwF5LNMAQAAcAAAAHhWNBIAAAAAAAAAAAABAAAD" |
| "AAAAcAAAAAMAAAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAIgAAABkAAAA6AAAAOgA" |
| "AADvAAAA9wAAAAAAAAABAAAAAgAAAAEAAAABAAAAAAAAAAAAAAD/////AAAAAAAAAAAAAAAAAgAA" |
| "AAEAAAABAAAAAAAAAP////8AAAAAAAAAAAAAAAAAAAAAAQAAAAIAAAAAAAAA/////wAAAAAAAAAA" |
| "AAAAAAVMQWxsOwAGTEJhc2U7AAZMWW91cjsAAAYAAAAAAAAAAQAAAAAAAAABAAAAAwAAAHAAAAAC" |
| "AAAAAwAAAHwAAAAGAAAAAwAAAIgAAAACIAAAAwAAAOgAAAAAEAAAAQAAAAABAAA="; |
| |
| TEST_F(DexFileVerifierTest, CircularClassInheritance) { |
| VerifyModification( |
| kCircularClassInheritanceTestDex, |
| "circular_class_inheritance", |
| [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ }, |
| "Invalid class definition ordering: class with type idx: '1' defined before" |
| " superclass with type idx: '0'"); |
| } |
| |
| // Dex file version 037 generated from: |
| // |
| // .class public abstract interface LInterfaceImplementsItself; |
| // .super Ljava/lang/Object; |
| // .implements LInterfaceImplementsItself; |
| |
| static const char kInterfaceImplementsItselfTestDex[] = |
| "ZGV4CjAzNwCKKrjatp8XbXl5S/bEVJnqaBhjZkQY4440AQAAcAAAAHhWNBIAAAAAAAAAANwAAAAC" |
| "AAAAcAAAAAIAAAB4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAIAAAACUAAAAoAAAAKAA" |
| "AAC9AAAAAAAAAAEAAAAAAAAAAQYAAAEAAADUAAAA/////wAAAAAAAAAAAAAAABtMSW50ZXJmYWNl" |
| "SW1wbGVtZW50c0l0c2VsZjsAEkxqYXZhL2xhbmcvT2JqZWN0OwAAAAABAAAAAAAAAAcAAAAAAAAA" |
| "AQAAAAAAAAABAAAAAgAAAHAAAAACAAAAAgAAAHgAAAAGAAAAAQAAAIAAAAACIAAAAgAAAKAAAAAB" |
| "EAAAAQAAANQAAAAAEAAAAQAAANwAAAA="; |
| |
| TEST_F(DexFileVerifierTest, InterfaceImplementsItself) { |
| VerifyModification( |
| kInterfaceImplementsItselfTestDex, |
| "interface_implements_itself", |
| [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ }, |
| "Class with same type idx as implemented interface: '0'"); |
| } |
| |
| // Dex file version 037 generated from: |
| // |
| // .class public abstract interface LPing; |
| // .super Ljava/lang/Object; |
| // .implements LPong; |
| // |
| // and: |
| // |
| // .class public abstract interface LPong; |
| // .super Ljava/lang/Object; |
| // .implements LPing; |
| |
| static const char kInterfacesImplementOneAnotherTestDex[] = |
| "ZGV4CjAzNwD0Kk9sxlYdg3Dy1Cff0gQCuJAQfEP6ohZUAQAAcAAAAHhWNBIAAAAAAAAAAPwAAAAD" |
| "AAAAcAAAAAMAAAB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAIgAAACMAAAAyAAAAMgA" |
| "AADQAAAA2AAAAAAAAAABAAAAAgAAAAEAAAABBgAAAgAAAOwAAAD/////AAAAAAAAAAAAAAAAAAAA" |
| "AAEGAAACAAAA9AAAAP////8AAAAAAAAAAAAAAAAGTFBpbmc7AAZMUG9uZzsAEkxqYXZhL2xhbmcv" |
| "T2JqZWN0OwABAAAAAAAAAAEAAAABAAAABwAAAAAAAAABAAAAAAAAAAEAAAADAAAAcAAAAAIAAAAD" |
| "AAAAfAAAAAYAAAACAAAAiAAAAAIgAAADAAAAyAAAAAEQAAACAAAA7AAAAAAQAAABAAAA/AAAAA=="; |
| |
| TEST_F(DexFileVerifierTest, InterfacesImplementOneAnother) { |
| VerifyModification( |
| kInterfacesImplementOneAnotherTestDex, |
| "interfaces_implement_one_another", |
| [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ }, |
| "Invalid class definition ordering: class with type idx: '1' defined before" |
| " implemented interface with type idx: '0'"); |
| } |
| |
| // Dex file version 037 generated from: |
| // |
| // .class public abstract interface LA; |
| // .super Ljava/lang/Object; |
| // .implements LB; |
| // |
| // and: |
| // |
| // .class public abstract interface LB; |
| // .super Ljava/lang/Object; |
| // .implements LC; |
| // |
| // and: |
| // |
| // .class public abstract interface LC; |
| // .super Ljava/lang/Object; |
| // .implements LA; |
| |
| static const char kCircularInterfaceImplementationTestDex[] = |
| "ZGV4CjAzNwCzKmD5Fol6XAU6ichYHcUTIP7Z7MdTcEmEAQAAcAAAAHhWNBIAAAAAAAAAACwBAAAE" |
| "AAAAcAAAAAQAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAJAAAACUAAAA8AAAAPAA" |
| "AAD1AAAA+gAAAP8AAAAAAAAAAQAAAAIAAAADAAAAAgAAAAEGAAADAAAAHAEAAP////8AAAAAAAAA" |
| "AAAAAAABAAAAAQYAAAMAAAAUAQAA/////wAAAAAAAAAAAAAAAAAAAAABBgAAAwAAACQBAAD/////" |
| "AAAAAAAAAAAAAAAAA0xBOwADTEI7AANMQzsAEkxqYXZhL2xhbmcvT2JqZWN0OwAAAQAAAAIAAAAB" |
| "AAAAAAAAAAEAAAABAAAABwAAAAAAAAABAAAAAAAAAAEAAAAEAAAAcAAAAAIAAAAEAAAAgAAAAAYA" |
| "AAADAAAAkAAAAAIgAAAEAAAA8AAAAAEQAAADAAAAFAEAAAAQAAABAAAALAEAAA=="; |
| |
| TEST_F(DexFileVerifierTest, CircularInterfaceImplementation) { |
| VerifyModification( |
| kCircularInterfaceImplementationTestDex, |
| "circular_interface_implementation", |
| [](DexFile* dex_file ATTRIBUTE_UNUSED) { /* empty */ }, |
| "Invalid class definition ordering: class with type idx: '2' defined before" |
| " implemented interface with type idx: '0'"); |
| } |
| |
| TEST_F(DexFileVerifierTest, Checksum) { |
| size_t length; |
| std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kGoodTestDex, &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; |
| |
| // Good checksum: all pass. |
| EXPECT_TRUE(dex::Verify(dex_file.get(), |
| "good checksum, no verify", |
| /*verify_checksum=*/false, |
| &error_msg)); |
| EXPECT_TRUE(dex::Verify(dex_file.get(), |
| "good checksum, verify", |
| /*verify_checksum=*/true, |
| &error_msg)); |
| |
| // Bad checksum: !verify_checksum passes verify_checksum fails. |
| DexFile::Header* header = reinterpret_cast<DexFile::Header*>( |
| const_cast<uint8_t*>(dex_file->Begin())); |
| header->checksum_ = 0; |
| EXPECT_TRUE(dex::Verify(dex_file.get(), |
| "bad checksum, no verify", |
| /*verify_checksum=*/false, |
| &error_msg)); |
| EXPECT_FALSE(dex::Verify(dex_file.get(), |
| "bad checksum, verify", |
| /*verify_checksum=*/true, |
| &error_msg)); |
| 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(dex::Verify(dex_file.get(), |
| "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(dex::Verify(dex_file.get(), |
| "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(dex::Verify(dex_file.get(), |
| "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(dex::Verify(dex_file.get(), |
| "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(dex::Verify(dex_file.get(), |
| "bad init signature", |
| /*verify_checksum=*/true, |
| &error_msg)); |
| } |
| |
| static const char* kInvokeCustomDexFiles[] = { |
| // TODO(oth): Revisit this test when we have smali / dx support. |
| // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test001/Tests.java |
| "ZGV4CjAzOAAEj12s/acmmdGuDL92SWSBh6iLBjxgomWkCAAAcAAAAHhWNBIAAAAAAAAAALwHAAAx" |
| "AAAAcAAAABYAAAA0AQAACQAAAIwBAAADAAAA+AEAAAsAAAAQAgAAAQAAAHACAAAMBgAAmAIAAMID" |
| "AADKAwAAzQMAANIDAADhAwAA5AMAAOoDAAAfBAAAUgQAAIMEAAC4BAAA1AQAAOsEAAD+BAAAEgUA" |
| "ACYFAAA6BQAAUQUAAG4FAACTBQAAtAUAAN0FAAD/BQAAHgYAADgGAABKBgAAVgYAAFkGAABdBgAA" |
| "YgYAAGYGAAB7BgAAgAYAAI8GAACdBgAAtAYAAMMGAADSBgAA3gYAAPIGAAD4BgAABgcAAA4HAAAU" |
| "BwAAGgcAAB8HAAAoBwAANAcAADoHAAABAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0A" |
| "AAAOAAAADwAAABAAAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABoAAAAeAAAAAgAA" |
| "AAAAAACMAwAABQAAAAwAAACUAwAABQAAAA4AAACgAwAABAAAAA8AAAAAAAAAGgAAABQAAAAAAAAA" |
| "GwAAABQAAACsAwAAHAAAABQAAACMAwAAHQAAABQAAAC0AwAAHQAAABQAAAC8AwAAAwADAAMAAAAE" |
| "AAwAJAAAAAoABgAsAAAABAAEAAAAAAAEAAAAHwAAAAQAAQAoAAAABAAIACoAAAAEAAQALwAAAAYA" |
| "BQAtAAAACAAEAAAAAAANAAcAAAAAAA8AAgAlAAAAEAADACkAAAASAAYAIQAAAJYHAACWBwAABAAA" |
| "AAEAAAAIAAAAAAAAABkAAABkAwAAnQcAAAAAAAAEAAAAAgAAAAEAAABjBwAAAQAAAIsHAAACAAAA" |
| "iwcAAJMHAAABAAEAAQAAAEEHAAAEAAAAcBAGAAAADgADAAIAAAAAAEYHAAADAAAAkAABAg8AAAAF" |
| "AAMABAAAAE0HAAAQAAAAcQAJAAAADAAcAQQAbkAIABBDDAAiAQ0AcCAHAAEAEQEEAAEAAgAAAFYH" |
| "AAAMAAAAYgACABIhEjL8IAAAIQAKAW4gBQAQAA4AAwABAAIAAABdBwAACwAAABIgEjH8IAEAEAAK" |
| "ABJRcSAKAAEADgAAAAAAAAAAAAAAAwAAAAAAAAABAAAAmAIAAAIAAACgAgAABAAAAKgCAAACAAAA" |
| "AAAAAAMAAAAPAAkAEQAAAAMAAAAHAAkAEQAAAAEAAAAAAAAAAQAAAA4AAAABAAAAFQAGPGluaXQ+" |
| "AAFJAANJSUkADUlOVk9LRV9TVEFUSUMAAUwABExMTEwAM0xjb20vYW5kcm9pZC9qYWNrL2Fubm90" |
| "YXRpb25zL0NhbGxlZEJ5SW52b2tlQ3VzdG9tOwAxTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlv" |
| "bnMvTGlua2VyTWV0aG9kSGFuZGxlOwAvTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvTWV0" |
| "aG9kSGFuZGxlS2luZDsAM0xjb20vYW5kcm9pZC9qYWNrL2phdmE3L2ludm9rZWN1c3RvbS90ZXN0" |
| "MDAxL1Rlc3RzOwAaTGRhbHZpay9hbm5vdGF0aW9uL1Rocm93czsAFUxqYXZhL2lvL1ByaW50U3Ry" |
| "ZWFtOwARTGphdmEvbGFuZy9DbGFzczsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9T" |
| "dHJpbmc7ABJMamF2YS9sYW5nL1N5c3RlbTsAFUxqYXZhL2xhbmcvVGhyb3dhYmxlOwAbTGphdmEv" |
| "bGFuZy9pbnZva2UvQ2FsbFNpdGU7ACNMamF2YS9sYW5nL2ludm9rZS9Db25zdGFudENhbGxTaXRl" |
| "OwAfTGphdmEvbGFuZy9pbnZva2UvTWV0aG9kSGFuZGxlOwAnTGphdmEvbGFuZy9pbnZva2UvTWV0" |
| "aG9kSGFuZGxlcyRMb29rdXA7ACBMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzOwAdTGph" |
| "dmEvbGFuZy9pbnZva2UvTWV0aG9kVHlwZTsAGExqdW5pdC9mcmFtZXdvcmsvQXNzZXJ0OwAQTG9y" |
| "Zy9qdW5pdC9UZXN0OwAKVGVzdHMuamF2YQABVgACVkkAA1ZJSQACVkwAE1tMamF2YS9sYW5nL1N0" |
| "cmluZzsAA2FkZAANYXJndW1lbnRUeXBlcwAMYXNzZXJ0RXF1YWxzABVlbWl0dGVyOiBqYWNrLTQu" |
| "MC1lbmcADWVuY2xvc2luZ1R5cGUADWZpZWxkQ2FsbFNpdGUACmZpbmRTdGF0aWMAEmludm9rZU1l" |
| "dGhvZEhhbmRsZQAEa2luZAAMbGlua2VyTWV0aG9kAAZsb29rdXAABG1haW4ABG5hbWUAA291dAAH" |
| "cHJpbnRsbgAKcmV0dXJuVHlwZQAEdGVzdAAFdmFsdWUAIgAHDgAvAgAABw4ANQMAAAAHDqUAPwEA" |
| "Bw60ADsABw6lAAABBCAcAhgAGAAmHAEdAgQgHAMYDxgJGBEjGAQnGwArFygrFx8uGAACBQEwHAEY" |
| "CwETAAMWABcfFQABAAQBAQkAgYAEtAUBCswFAQrkBQEJlAYEAbwGAAAAEwAAAAAAAAABAAAAAAAA" |
| "AAEAAAAxAAAAcAAAAAIAAAAWAAAANAEAAAMAAAAJAAAAjAEAAAQAAAADAAAA+AEAAAUAAAALAAAA" |
| "EAIAAAcAAAACAAAAaAIAAAYAAAABAAAAcAIAAAgAAAABAAAAkAIAAAMQAAADAAAAmAIAAAEgAAAF" |
| "AAAAtAIAAAYgAAABAAAAZAMAAAEQAAAGAAAAjAMAAAIgAAAxAAAAwgMAAAMgAAAFAAAAQQcAAAQg" |
| "AAADAAAAYwcAAAUgAAABAAAAlgcAAAAgAAABAAAAnQcAAAAQAAABAAAAvAcAAA==", |
| // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test002/Tests.java |
| "ZGV4CjAzOAAzq3aGAwKhT4QQj4lqNfZJAO8Tm24uTyNICQAAcAAAAHhWNBIAAAAAAAAAAGAIAAA2" |
| "AAAAcAAAABgAAABIAQAACQAAAKgBAAAEAAAAFAIAAA0AAAA0AgAAAQAAAKQCAAB8BgAAzAIAACYE" |
| "AAAwBAAAOAQAAEQEAABHBAAATAQAAE8EAABVBAAAigQAALwEAADtBAAAIgUAAD4FAABVBQAAaAUA" |
| "AH0FAACRBQAApQUAALkFAADQBQAA7QUAABIGAAAzBgAAXAYAAH4GAACdBgAAtwYAAMkGAADPBgAA" |
| "2wYAAN4GAADiBgAA5wYAAOsGAAD/BgAAFAcAABkHAAAoBwAANgcAAE0HAABcBwAAawcAAH4HAACK" |
| "BwAAkAcAAJgHAACeBwAAqgcAALAHAAC1BwAAxgcAAM8HAADbBwAA4QcAAAMAAAAHAAAACAAAAAkA" |
| "AAAKAAAACwAAAAwAAAANAAAADgAAAA8AAAAQAAAAEQAAABIAAAATAAAAFAAAABUAAAAWAAAAFwAA" |
| "ABgAAAAZAAAAGgAAAB0AAAAhAAAAIgAAAAQAAAAAAAAA8AMAAAYAAAAPAAAA+AMAAAUAAAAQAAAA" |
| "AAAAAAYAAAASAAAABAQAAB0AAAAVAAAAAAAAAB4AAAAVAAAAEAQAAB8AAAAVAAAA8AMAACAAAAAV" |
| "AAAAGAQAACAAAAAVAAAAIAQAAAMAAwACAAAABAANACgAAAAIAAcAGwAAAAsABgAwAAAABAAEAAAA" |
| "AAAEAAQAAQAAAAQAAAAjAAAABAAIAC0AAAAEAAQANAAAAAYABQAyAAAACQAEAAEAAAAMAAQAMQAA" |
| "AA4ABwABAAAAEAABACoAAAARAAIALAAAABIAAwAuAAAAEwAGACUAAAA4CAAAOAgAAAQAAAABAAAA" |
| "CQAAAAAAAAAcAAAA0AMAAD8IAAAAAAAAAQAAAAEAAAABAAAADggAAAIAAAAtCAAANQgAAAgAAAAE" |
| "AAEA6AcAACoAAABxAAoAAAAMABwBBAAbAiMAAABiAwIAYgQCABIVI1UWAGIGAgASB00GBQdxMAsA" |
| "QwUMA25ACQAQMgwAIgEOAHAgCAABAGkBAQAOAA0AbhAHAAAAKPsAAAAAJAABAAEBDCUBAAEAAQAA" |
| "APUHAAAEAAAAcBAGAAAADgADAAIAAAAAAPoHAAADAAAAkAABAg8AAAAEAAEAAgAAAAEIAAAMAAAA" |
| "YgADABIhEjL8IAAAIQAKAW4gBQAQAA4AAwABAAIAAAAICAAACwAAABIgEjH8IAEAEAAKABJRcSAM" |
| "AAEADgAAAAAAAAAAAAAAAgAAAAAAAAACAAAAzAIAAAQAAADUAgAAAgAAAAAAAAADAAAABwAKABIA" |
| "AAADAAAABwAHABYAAAABAAAAAAAAAAEAAAAPAAAAAQAAABcACDxjbGluaXQ+AAY8aW5pdD4ACkdF" |
| "VF9TVEFUSUMAAUkAA0lJSQABTAAETExMTAAzTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMv" |
| "Q2FsbGVkQnlJbnZva2VDdXN0b207ADBMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9MaW5r" |
| "ZXJGaWVsZEhhbmRsZTsAL0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL01ldGhvZEhhbmRs" |
| "ZUtpbmQ7ADNMY29tL2FuZHJvaWQvamFjay9qYXZhNy9pbnZva2VjdXN0b20vdGVzdDAwMi9UZXN0" |
| "czsAGkxkYWx2aWsvYW5ub3RhdGlvbi9UaHJvd3M7ABVMamF2YS9pby9QcmludFN0cmVhbTsAEUxq" |
| "YXZhL2xhbmcvQ2xhc3M7ABNMamF2YS9sYW5nL0ludGVnZXI7ABJMamF2YS9sYW5nL09iamVjdDsA" |
| "EkxqYXZhL2xhbmcvU3RyaW5nOwASTGphdmEvbGFuZy9TeXN0ZW07ABVMamF2YS9sYW5nL1Rocm93" |
| "YWJsZTsAG0xqYXZhL2xhbmcvaW52b2tlL0NhbGxTaXRlOwAjTGphdmEvbGFuZy9pbnZva2UvQ29u" |
| "c3RhbnRDYWxsU2l0ZTsAH0xqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZTsAJ0xqYXZhL2xh" |
| "bmcvaW52b2tlL01ldGhvZEhhbmRsZXMkTG9va3VwOwAgTGphdmEvbGFuZy9pbnZva2UvTWV0aG9k" |
| "SGFuZGxlczsAHUxqYXZhL2xhbmcvaW52b2tlL01ldGhvZFR5cGU7ABhManVuaXQvZnJhbWV3b3Jr" |
| "L0Fzc2VydDsAEExvcmcvanVuaXQvVGVzdDsABFRZUEUAClRlc3RzLmphdmEAAVYAAlZJAANWSUkA" |
| "AlZMABJbTGphdmEvbGFuZy9DbGFzczsAE1tMamF2YS9sYW5nL1N0cmluZzsAA2FkZAANYXJndW1l" |
| "bnRUeXBlcwAMYXNzZXJ0RXF1YWxzABVlbWl0dGVyOiBqYWNrLTQuMC1lbmcADWVuY2xvc2luZ1R5" |
| "cGUADWZpZWxkQ2FsbFNpdGUAEWZpZWxkTWV0aG9kSGFuZGxlAApmaW5kU3RhdGljAARraW5kAAZs" |
| "b29rdXAABG1haW4ACm1ldGhvZFR5cGUABG5hbWUAA291dAAPcHJpbnRTdGFja1RyYWNlAAdwcmlu" |
| "dGxuAApyZXR1cm5UeXBlAAR0ZXN0AAV2YWx1ZQAoAAcOAR0PAnh3Jh4AIQAHDgA2AgAABw4APwEA" |
| "Bw60ADsABw6lAAABBCQcAhgAGAApHAEdAgMnGAQrGwAvFygvFyMzGAACBQE1HAEYDAEUAAMWABcj" |
| "FQABAAQBAQkAiIAE4AUBgYAE0AYBCugGAQmABwQBqAcAAAATAAAAAAAAAAEAAAAAAAAAAQAAADYA" |
| "AABwAAAAAgAAABgAAABIAQAAAwAAAAkAAACoAQAABAAAAAQAAAAUAgAABQAAAA0AAAA0AgAABwAA" |
| "AAIAAACcAgAABgAAAAEAAACkAgAACAAAAAEAAADEAgAAAxAAAAIAAADMAgAAASAAAAUAAADgAgAA" |
| "BiAAAAEAAADQAwAAARAAAAYAAADwAwAAAiAAADYAAAAmBAAAAyAAAAUAAADoBwAABCAAAAMAAAAO" |
| "CAAABSAAAAEAAAA4CAAAACAAAAEAAAA/CAAAABAAAAEAAABgCAAA", |
| // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test003/Tests.java |
| "ZGV4CjAzOABjnhkFatj30/7cHTCJsfr7vAjz9/p+Y+TcCAAAcAAAAHhWNBIAAAAAAAAAAPQHAAAx" |
| "AAAAcAAAABYAAAA0AQAACQAAAIwBAAADAAAA+AEAAAsAAAAQAgAAAQAAAHACAABEBgAAmAIAAOoD" |
| "AADyAwAA9QMAAP4DAAANBAAAEAQAABYEAABLBAAAfgQAAK8EAADkBAAAAAUAABcFAAAqBQAAPgUA" |
| "AFIFAABmBQAAfQUAAJoFAAC/BQAA4AUAAAkGAAArBgAASgYAAGQGAAB2BgAAggYAAIUGAACJBgAA" |
| "jgYAAJIGAACnBgAArAYAALsGAADJBgAA4AYAAO8GAAD+BgAACgcAAB4HAAAkBwAAMgcAADoHAABA" |
| "BwAARgcAAEsHAABUBwAAYAcAAGYHAAABAAAABgAAAAcAAAAIAAAACQAAAAoAAAALAAAADAAAAA0A" |
| "AAAOAAAADwAAABAAAAARAAAAEgAAABMAAAAUAAAAFQAAABYAAAAXAAAAGAAAABoAAAAeAAAAAgAA" |
| "AAAAAACkAwAABQAAAAwAAAC0AwAABQAAAA4AAADAAwAABAAAAA8AAAAAAAAAGgAAABQAAAAAAAAA" |
| "GwAAABQAAADMAwAAHAAAABQAAADUAwAAHQAAABQAAADcAwAAHQAAABQAAADkAwAAAwADAAMAAAAE" |
| "AAwAJAAAAAoABgAsAAAABAAEAAAAAAAEAAAAHwAAAAQAAQAoAAAABAAIACoAAAAEAAQALwAAAAYA" |
| "BQAtAAAACAAEAAAAAAANAAcAAAAAAA8AAgAlAAAAEAADACkAAAASAAYAIQAAAM4HAADOBwAABAAA" |
| "AAEAAAAIAAAAAAAAABkAAAB8AwAA1QcAAAAAAAAEAAAAAgAAAAEAAACTBwAAAQAAAMMHAAACAAAA" |
| "wwcAAMsHAAABAAEAAQAAAG0HAAAEAAAAcBAGAAAADgAHAAYAAAAAAHIHAAAHAAAAkAABArAwsECw" |
| "ULBgDwAAAAUAAwAEAAAAfQcAABAAAABxAAkAAAAMABwBBABuQAgAEEMMACIBDQBwIAcAAQARAQgA" |
| "AQACAAAAhgcAABAAAABiBgIAEhASIRIyEkMSVBJl/QYAAAAACgBuIAUABgAOAAcAAQACAAAAjQcA" |
| "ABAAAAASEBIhEjISQxJUEmX9BgEAAAAKABMBFQBxIAoAAQAOAAAAAAAAAAAAAwAAAAAAAAABAAAA" |
| "mAIAAAIAAACgAgAABAAAAKgCAAAGAAAAAAAAAAAAAAAAAAAAAwAAAA8ACQARAAAAAwAAAAcACQAR" |
| "AAAAAQAAAAAAAAACAAAAAAAAAAEAAAAOAAAAAQAAABUABjxpbml0PgABSQAHSUlJSUlJSQANSU5W" |
| "T0tFX1NUQVRJQwABTAAETExMTAAzTGNvbS9hbmRyb2lkL2phY2svYW5ub3RhdGlvbnMvQ2FsbGVk" |
| "QnlJbnZva2VDdXN0b207ADFMY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9MaW5rZXJNZXRo" |
| "b2RIYW5kbGU7AC9MY29tL2FuZHJvaWQvamFjay9hbm5vdGF0aW9ucy9NZXRob2RIYW5kbGVLaW5k" |
| "OwAzTGNvbS9hbmRyb2lkL2phY2svamF2YTcvaW52b2tlY3VzdG9tL3Rlc3QwMDMvVGVzdHM7ABpM" |
| "ZGFsdmlrL2Fubm90YXRpb24vVGhyb3dzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABFMamF2YS9s" |
| "YW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZh" |
| "L2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9D" |
| "YWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5n" |
| "L2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExv" |
| "b2t1cDsAIExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXM7AB1MamF2YS9sYW5nL2ludm9r" |
| "ZS9NZXRob2RUeXBlOwAYTGp1bml0L2ZyYW1ld29yay9Bc3NlcnQ7ABBMb3JnL2p1bml0L1Rlc3Q7" |
| "AApUZXN0cy5qYXZhAAFWAAJWSQADVklJAAJWTAATW0xqYXZhL2xhbmcvU3RyaW5nOwADYWRkAA1h" |
| "cmd1bWVudFR5cGVzAAxhc3NlcnRFcXVhbHMAFWVtaXR0ZXI6IGphY2stNC4wLWVuZwANZW5jbG9z" |
| "aW5nVHlwZQANZmllbGRDYWxsU2l0ZQAKZmluZFN0YXRpYwASaW52b2tlTWV0aG9kSGFuZGxlAARr" |
| "aW5kAAxsaW5rZXJNZXRob2QABmxvb2t1cAAEbWFpbgAEbmFtZQADb3V0AAdwcmludGxuAApyZXR1" |
| "cm5UeXBlAAR0ZXN0AAV2YWx1ZQAiAAcOAC8GAAAAAAAABw4ANQMAAAAHDqUAPwEABw7wADsABw7w" |
| "AAABBCAcBhgAGAAYABgAGAAYACYcAR0CBCAcAxgPGAkYESMYBCcbACsXKCsXHy4YAAIFATAcARgL" |
| "ARMAAxYAFx8VAAEABAEBCQCBgAS0BQEKzAUBCuwFAQmcBgQBzAYAAAATAAAAAAAAAAEAAAAAAAAA" |
| "AQAAADEAAABwAAAAAgAAABYAAAA0AQAAAwAAAAkAAACMAQAABAAAAAMAAAD4AQAABQAAAAsAAAAQ" |
| "AgAABwAAAAIAAABoAgAABgAAAAEAAABwAgAACAAAAAEAAACQAgAAAxAAAAMAAACYAgAAASAAAAUA" |
| "AAC0AgAABiAAAAEAAAB8AwAAARAAAAcAAACkAwAAAiAAADEAAADqAwAAAyAAAAUAAABtBwAABCAA" |
| "AAMAAACTBwAABSAAAAEAAADOBwAAACAAAAEAAADVBwAAABAAAAEAAAD0BwAA", |
| // https://cs.corp.google.com/android/toolchain/jack/jack-tests/tests/com/android/jack/java7/invokecustom/test004/Tests.java |
| "ZGV4CjAzOABvUVfbV74qWbSOEsgKP+EzahlNQLW2/8TMDAAAcAAAAHhWNBIAAAAAAAAAAOQLAABS" |
| "AAAAcAAAAB8AAAC4AQAAEAAAADQCAAADAAAA9AIAABIAAAAMAwAAAQAAAKQDAAAACQAAzAMAANYF" |
| "AADZBQAA4QUAAOkFAADsBQAA7wUAAPIFAAD1BQAA/AUAAP8FAAAEBgAAEwYAABYGAAAZBgAAHwYA" |
| "AC8GAABkBgAAjQYAAMAGAADxBgAAJgcAAEUHAABhBwAAeAcAAIoHAACdBwAAsQcAAMUHAADZBwAA" |
| "8AcAAA0IAAAyCAAAUwgAAHwIAACeCAAAvQgAANcIAADpCAAA7AgAAPgIAAD7CAAAAAkAAAYJAAAM" |
| "CQAAEAkAABUJAAAaCQAAHgkAACMJAAAnCQAAKgkAADMJAABICQAATQkAAFwJAABqCQAAdgkAAIQJ" |
| "AACPCQAAmgkAAKYJAACzCQAAygkAANkJAADoCQAA9AkAAAAKAAAKCgAAHgoAACQKAAAyCgAAPQoA" |
| "AEUKAABLCgAAYgoAAGgKAABtCgAAdgoAAIIKAACOCgAAmwoAAKEKAAADAAAABAAAAAUAAAAGAAAA" |
| "CAAAAAsAAAAPAAAAEAAAABEAAAASAAAAEwAAABQAAAAVAAAAFgAAABgAAAAZAAAAGgAAABsAAAAc" |
| "AAAAHQAAAB4AAAAfAAAAIAAAACEAAAAiAAAAIwAAACQAAAAlAAAAJwAAADEAAAAzAAAACQAAAAQA" |
| "AABMBQAADgAAABMAAABUBQAADQAAABUAAAB0BQAADAAAABYAAAAAAAAAJwAAABwAAAAAAAAAKAAA" |
| "ABwAAACABQAAKQAAABwAAACIBQAAKgAAABwAAACUBQAAKwAAABwAAACgBQAALAAAABwAAABMBQAA" |
| "LQAAABwAAACoBQAALwAAABwAAACwBQAALwAAABwAAAC4BQAALgAAABwAAADABQAAMAAAABwAAADI" |
| "BQAALgAAABwAAADQBQAACQAJAAoAAAAKABMAPwAAABEADQBLAAAACgAEAAIAAAAKAAAANAAAAAoA" |
| "AQBFAAAACgAPAEgAAAAKAAQAUAAAAA0ACABMAAAADwAEAAIAAAAUAA0AAgAAABYAAgBAAAAAFwAD" |
| "AEcAAAAZAAUANgAAABkABgA2AAAAGQAHADYAAAAZAAkANgAAABkACgA2AAAAGQALADYAAAAZAAwA" |
| "NgAAABkADgA3AAAAnQsAAJ0LAAAKAAAAAQAAAA8AAAAAAAAAJgAAACQFAADGCwAAAAAAAAQAAAAC" |
| "AAAAAQAAAN4KAAACAAAAegsAAJILAAACAAAAkgsAAJoLAAABAAEAAQAAAKgKAAAEAAAAcBAGAAAA" |
| "DgADAAIAAAAAAK0KAAADAAAAkAABAg8AAAAYAA8ABgAAALQKAABTAAAAcRARAAwAEhJxIA0A0gAT" |
| "AmEAcSAKAOIAEwIABHEgDQDyABISAgAQAHEgDQACABICFAOamTFBAgARAHEwDAADAhYGAAAYApqZ" |
| "mZmZmQFABQQSAHcGCwACABsCBwAAAAgAFABxIBAAAgAcAgoACAAVAHEgDwACABcCFc1bBwUAFgBx" |
| "QA4AMhBxAAkAAAAMAhwDCgBuQAgAMroMAiIDFABwIAcAIwARAwAABAABAAIAAADRCgAADAAAAGIA" |
| "AgASIRIy/CAAACEACgFuIAUAEAAOAAMAAQACAAAA2AoAAAsAAAASIBIx/CABABAACgASUXEgDQAB" |
| "AA4AAAAAAAAAAAAAAAMAAAAAAAAAAQAAAMwDAAACAAAA1AMAAAQAAADgAwAAAgAAAAQABAANAAAA" |
| "FgAQABgAHQAAAAEAGwAEAAMAAgAQAA4ABQAAAAMAAAAOABAAGAAAAAIAAAABAAEAAwAAAAIAAgAC" |
| "AAAAAwAAAAMAAwADAAAAAQAAAAQAAAACAAAABQAFAAIAAAAPAA8AAgAAABAAEAABAAAAFQAAAAEA" |
| "AAAdAAAAAQAAAB4AASgABjwqPjtKKQAGPGluaXQ+AAFCAAFDAAFEAAFGAAVIZWxsbwABSQADSUlJ" |
| "AA1JTlZPS0VfU1RBVElDAAFKAAFMAARMTExMAA5MTExMWkJDU0lGRExMSgAzTGNvbS9hbmRyb2lk" |
| "L2phY2svYW5ub3RhdGlvbnMvQ2FsbGVkQnlJbnZva2VDdXN0b207ACdMY29tL2FuZHJvaWQvamFj" |
| "ay9hbm5vdGF0aW9ucy9Db25zdGFudDsAMUxjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL0xp" |
| "bmtlck1ldGhvZEhhbmRsZTsAL0xjb20vYW5kcm9pZC9qYWNrL2Fubm90YXRpb25zL01ldGhvZEhh" |
| "bmRsZUtpbmQ7ADNMY29tL2FuZHJvaWQvamFjay9qYXZhNy9pbnZva2VjdXN0b20vdGVzdDAwNC9U" |
| "ZXN0czsAHUxkYWx2aWsvYW5ub3RhdGlvbi9TaWduYXR1cmU7ABpMZGFsdmlrL2Fubm90YXRpb24v" |
| "VGhyb3dzOwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABBMamF2YS9sYW5nL0NsYXNzABFMamF2YS9s" |
| "YW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZh" |
| "L2xhbmcvU3lzdGVtOwAVTGphdmEvbGFuZy9UaHJvd2FibGU7ABtMamF2YS9sYW5nL2ludm9rZS9D" |
| "YWxsU2l0ZTsAI0xqYXZhL2xhbmcvaW52b2tlL0NvbnN0YW50Q2FsbFNpdGU7AB9MamF2YS9sYW5n" |
| "L2ludm9rZS9NZXRob2RIYW5kbGU7ACdMamF2YS9sYW5nL2ludm9rZS9NZXRob2RIYW5kbGVzJExv" |
| "b2t1cDsAIExqYXZhL2xhbmcvaW52b2tlL01ldGhvZEhhbmRsZXM7AB1MamF2YS9sYW5nL2ludm9r" |
| "ZS9NZXRob2RUeXBlOwAYTGp1bml0L2ZyYW1ld29yay9Bc3NlcnQ7ABBMb3JnL2p1bml0L1Rlc3Q7" |
| "AAFTAApUZXN0cy5qYXZhAAFWAANWQ0MABFZEREQABFZGRkYAAlZJAANWSUkAA1ZKSgACVkwAA1ZM" |
| "TAACVloAAVoAB1pCQ1NJRkQAE1tMamF2YS9sYW5nL1N0cmluZzsAA2FkZAANYXJndW1lbnRUeXBl" |
| "cwAMYXNzZXJ0RXF1YWxzAAphc3NlcnRUcnVlAAxib29sZWFuVmFsdWUACWJ5dGVWYWx1ZQAJY2hh" |
| "clZhbHVlAApjbGFzc1ZhbHVlAAtkb3VibGVWYWx1ZQAVZW1pdHRlcjogamFjay00LjAtZW5nAA1l" |
| "bmNsb3NpbmdUeXBlAA1maWVsZENhbGxTaXRlAApmaW5kU3RhdGljAApmbG9hdFZhbHVlAAhpbnRW" |
| "YWx1ZQASaW52b2tlTWV0aG9kSGFuZGxlAARraW5kAAxsaW5rZXJNZXRob2QACWxvbmdWYWx1ZQAG" |
| "bG9va3VwAARtYWluABVtZXRob2RIYW5kbGVFeHRyYUFyZ3MABG5hbWUAA291dAAHcHJpbnRsbgAK" |
| "cmV0dXJuVHlwZQAKc2hvcnRWYWx1ZQALc3RyaW5nVmFsdWUABHRlc3QABXZhbHVlACMABw4ANwIA" |
| "AAcOAD4NAAAAAAAAAAAAAAAAAAcOPEtaWmmWw4d4h6UAUgEABw60AE4ABw6lAAAGBTUcAhgEGARD" |
| "HAEdCAQ1HA0YFhgQGBgYHRgAGAEYGxgEGAMYAhgQGA4YBT4YCkQbAEoXRUkcCh0HATgcAT8dBwE5" |
| "HAEAAR0HATocAQNhHQcBThwBIgAEHQcBQhwBBAEdBwFBHAFwmpkxQR0HATwcAfGamZmZmZkBQB0H" |
| "AU8cARcHHQcBOxwBGAodBwFGHAFmFc1bB0oXNE0YBAILAVEcCRcAFyAXGhciFzIXGhcXFwEXHQIM" |
| "AVEcARgSARoADRYAFzQVAAQBBAEEYSQABAQBcJqZMUHxmpmZmZmZAUAXBxgKZhXNWwcBAAQBAQkA" |
| "gYAE7AcBCoQIAQqcCAEJ1AkEAfwJAAATAAAAAAAAAAEAAAAAAAAAAQAAAFIAAABwAAAAAgAAAB8A" |
| "AAC4AQAAAwAAABAAAAA0AgAABAAAAAMAAAD0AgAABQAAABIAAAAMAwAABwAAAAIAAACcAwAABgAA" |
| "AAEAAACkAwAACAAAAAEAAADEAwAAAxAAAAMAAADMAwAAASAAAAUAAADsAwAABiAAAAEAAAAkBQAA" |
| "ARAAAA0AAABMBQAAAiAAAFIAAADWBQAAAyAAAAUAAACoCgAABCAAAAQAAADeCgAABSAAAAEAAACd" |
| "CwAAACAAAAEAAADGCwAAABAAAAEAAADkCwAA" |
| }; |
| |
| TEST_F(DexFileVerifierTest, InvokeCustomDexSamples) { |
| for (size_t i = 0; i < arraysize(kInvokeCustomDexFiles); ++i) { |
| size_t length; |
| std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kInvokeCustomDexFiles[i], &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_TRUE(dex::Verify(dex_file.get(), |
| "good checksum, verify", |
| /*verify_checksum=*/true, |
| &error_msg)); |
| // TODO(oth): Test corruptions (b/35308502) |
| } |
| } |
| |
| TEST_F(DexFileVerifierTest, BadStaticFieldInitialValuesArray) { |
| // Generated DEX file version (037) from: |
| // |
| // .class public LBadStaticFieldInitialValuesArray; |
| // .super Ljava/lang/Object; |
| // |
| // # static fields |
| // .field static final c:C = 'c' |
| // .field static final i:I = 0x1 |
| // .field static final s:Ljava/lang/String; = "s" |
| // |
| // # direct methods |
| // .method public constructor <init>()V |
| // .registers 1 |
| // invoke-direct {p0}, Ljava/lang/Object;-><init>()V |
| // return-void |
| // .end method |
| // |
| // Output file was hex edited so that static field "i" has string typing in initial values array. |
| static const char kDexBase64[] = |
| "ZGV4CjAzNQBrMi4cCPcMvvXNRw0uI6RRubwMPwgEYXIsAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAL" |
| "AAAAcAAAAAYAAACcAAAAAQAAALQAAAADAAAAwAAAAAIAAADYAAAAAQAAAOgAAAAkAQAACAEAACAB" |
| "AAAoAQAAMAEAADMBAAA2AQAAOwEAAE8BAABjAQAAZgEAAGkBAABsAQAAAgAAAAMAAAAEAAAABQAA" |
| "AAYAAAAHAAAABwAAAAUAAAAAAAAAAgAAAAgAAAACAAEACQAAAAIABAAKAAAAAgAAAAAAAAADAAAA" |
| "AAAAAAIAAAABAAAAAwAAAAAAAAABAAAAAAAAAHsBAAB0AQAAAQABAAEAAABvAQAABAAAAHAQAQAA" |
| "AA4ABjxpbml0PgAGQS5qYXZhAAFDAAFJAANMQTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEv" |
| "bGFuZy9TdHJpbmc7AAFWAAFjAAFpAAFzAAEABw4AAwNjFwoXCgMAAQAAGAEYARgAgYAEiAIADQAA" |
| "AAAAAAABAAAAAAAAAAEAAAALAAAAcAAAAAIAAAAGAAAAnAAAAAMAAAABAAAAtAAAAAQAAAADAAAA" |
| "wAAAAAUAAAACAAAA2AAAAAYAAAABAAAA6AAAAAEgAAABAAAACAEAAAIgAAALAAAAIAEAAAMgAAAB" |
| "AAAAbwEAAAUgAAABAAAAdAEAAAAgAAABAAAAewEAAAAQAAABAAAAjAEAAA=="; |
| |
| 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(dex::Verify(dex_file.get(), |
| "bad static field initial values array", |
| /*verify_checksum=*/true, |
| &error_msg)); |
| } |
| |
| TEST_F(DexFileVerifierTest, GoodStaticFieldInitialValuesArray) { |
| // Generated DEX file version (037) from: |
| // |
| // .class public LGoodStaticFieldInitialValuesArray; |
| // .super Ljava/lang/Object; |
| // |
| // # static fields |
| // .field static final b:B = 0x1t |
| // .field static final c:C = 'c' |
| // .field static final d:D = 0.6 |
| // .field static final f:F = 0.5f |
| // .field static final i:I = 0x3 |
| // .field static final j:J = 0x4L |
| // .field static final l1:Ljava/lang/String; |
| // .field static final l2:Ljava/lang/String; = "s" |
| // .field static final l3:Ljava/lang/Class; = Ljava/lang/String; |
| // .field static final s:S = 0x2s |
| // .field static final z:Z = true |
| // |
| // # direct methods |
| // .method public constructor <init>()V |
| // .registers 1 |
| // invoke-direct {p0}, Ljava/lang/Object;-><init>()V |
| // return-void |
| // .end method |
| static const char kDexBase64[] = |
| "ZGV4CjAzNQAwWxLbdhFa1NGiFWjsy5fhUCHxe5QHtPY8AwAAcAAAAHhWNBIAAAAAAAAAAJwCAAAZ" |
| "AAAAcAAAAA0AAADUAAAAAQAAAAgBAAALAAAAFAEAAAIAAABsAQAAAQAAAHwBAACgAQAAnAEAAJwB" |
| "AACkAQAApwEAAKoBAACtAQAAsAEAALMBAAC2AQAA2wEAAO4BAAACAgAAFgIAABkCAAAcAgAAHwIA" |
| "ACICAAAlAgAAKAIAACsCAAAuAgAAMQIAADUCAAA5AgAAPQIAAEACAAABAAAAAgAAAAMAAAAEAAAA" |
| "BQAAAAYAAAAHAAAACAAAAAkAAAAKAAAACwAAAAwAAAANAAAADAAAAAsAAAAAAAAABgAAAA4AAAAG" |
| "AAEADwAAAAYAAgAQAAAABgADABEAAAAGAAQAEgAAAAYABQATAAAABgAJABQAAAAGAAkAFQAAAAYA" |
| "BwAWAAAABgAKABcAAAAGAAwAGAAAAAYAAAAAAAAACAAAAAAAAAAGAAAAAQAAAAgAAAAAAAAA////" |
| "/wAAAAB8AgAARAIAAAY8aW5pdD4AAUIAAUMAAUQAAUYAAUkAAUoAI0xHb29kU3RhdGljRmllbGRJ" |
| "bml0aWFsVmFsdWVzQXJyYXk7ABFMamF2YS9sYW5nL0NsYXNzOwASTGphdmEvbGFuZy9PYmplY3Q7" |
| "ABJMamF2YS9sYW5nL1N0cmluZzsAAVMAAVYAAVoAAWIAAWMAAWQAAWYAAWkAAWoAAmwxAAJsMgAC" |
| "bDMAAXMAAXoAAAsAAQNj8TMzMzMzM+M/ED8EAwYEHhcXGAkCAj8AAAAAAQABAAEAAAAAAAAABAAA" |
| "AHAQAQAAAA4ACwABAAAYARgBGAEYARgBGAEYARgBGAEYARgAgYAE5AQNAAAAAAAAAAEAAAAAAAAA" |
| "AQAAABkAAABwAAAAAgAAAA0AAADUAAAAAwAAAAEAAAAIAQAABAAAAAsAAAAUAQAABQAAAAIAAABs" |
| "AQAABgAAAAEAAAB8AQAAAiAAABkAAACcAQAABSAAAAEAAABEAgAAAxAAAAEAAABgAgAAASAAAAEA" |
| "AABkAgAAACAAAAEAAAB8AgAAABAAAAEAAACcAgAA"; |
| |
| 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_TRUE(dex::Verify(dex_file.get(), |
| "good static field initial values array", |
| /*verify_checksum=*/true, |
| &error_msg)); |
| } |
| |
| } // namespace art |