/* * 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 "sys/mman.h" #include "zlib.h" #include #include #include "base/unix_file/fd_file.h" #include "base/bit_utils.h" #include "base/macros.h" #include "common_runtime_test.h" #include "dex_file-inl.h" #include "dex_file_types.h" #include "leb128.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" #include "utils.h" namespace art { // 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(dex_file->Begin()) + offset) = '7'; } static void FixUpChecksum(uint8_t* dex_file) { DexFile::Header* header = reinterpret_cast(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 CommonRuntimeTest { protected: DexFile* GetDexFile(const uint8_t* dex_bytes, size_t length) { return new DexFile(dex_bytes, length, "tmp", 0, nullptr); } void VerifyModification(const char* dex_file_base64_content, const char* location, const std::function& f, const char* expected_error) { size_t length; std::unique_ptr dex_bytes(DecodeBase64(dex_file_base64_content, &length)); CHECK(dex_bytes != nullptr); // Note: `dex_file` will be destroyed before `dex_bytes`. std::unique_ptr dex_file(GetDexFile(dex_bytes.get(), length)); f(dex_file.get()); FixUpChecksum(const_cast(dex_file->Begin())); static constexpr bool kVerifyChecksum = true; std::string error_msg; bool success = DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(), 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 OpenDexFileBase64(const char* base64, const char* location, std::string* error_msg) { // decode base64 CHECK(base64 != nullptr); size_t length; std::unique_ptr dex_bytes(DecodeBase64(base64, &length)); CHECK(dex_bytes.get() != nullptr); // write to provided file std::unique_ptr file(OS::CreateEmptyFile(location)); CHECK(file.get() != nullptr); if (!file->WriteFully(dex_bytes.get(), length)) { PLOG(FATAL) << "Failed to write base64 as dex file"; } if (file->FlushCloseOrErase() != 0) { PLOG(FATAL) << "Could not flush and close test file."; } file.reset(); // read dex file ScopedObjectAccess soa(Thread::Current()); std::vector> tmp; bool success = DexFile::Open(location, location, true, error_msg, &tmp); CHECK(success) << error_msg; EXPECT_EQ(1U, tmp.size()); std::unique_ptr dex_file = std::move(tmp[0]); EXPECT_EQ(PROT_READ, dex_file->GetPermissions()); EXPECT_TRUE(dex_file->IsReadOnly()); return dex_file; } // To generate a base64 encoded Dex file (such as kGoodTestDex, below) // from Smali files, use: // // smali -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) { ScratchFile tmp; std::string error_msg; std::unique_ptr raw(OpenDexFileBase64(kGoodTestDex, tmp.GetFilename().c_str(), &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) { DexFile::MethodId* method_id = const_cast(&dex_file->GetMethodId(0)); method_id->class_idx_ = dex::TypeIndex(0xFF); }, "could not find declaring class for direct method index 0"); // Proto idx error. VerifyModification( kGoodTestDex, "method_id_proto_idx", [](DexFile* dex_file) { DexFile::MethodId* method_id = const_cast(&dex_file->GetMethodId(0)); method_id->proto_idx_ = 0xFF; }, "inter_method_id_item proto_idx"); // Name idx error. VerifyModification( kGoodTestDex, "method_id_name_idx", [](DexFile* dex_file) { DexFile::MethodId* method_id = const_cast(&dex_file->GetMethodId(0)); method_id->name_idx_ = 0xFF; }, "String index not available for method flags verification"); } // 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 ()V // .registers 1 // return-void // .end method // // .method public constructor ()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) { const DexFile::ClassDef& class_def = dex_file->GetClassDef(0); const uint8_t* class_data = dex_file->GetClassData(class_def); ClassDataItemIterator it(*dex_file, class_data); const uint8_t* trailing = class_data; // Need to manually decode the four entries. DataPointer() doesn't work for this, as the first // element has already been loaded into the iterator. DecodeUnsignedLeb128(&trailing); DecodeUnsignedLeb128(&trailing); DecodeUnsignedLeb128(&trailing); DecodeUnsignedLeb128(&trailing); // Skip all fields. while (it.HasNextStaticField() || it.HasNextInstanceField()) { trailing = it.DataPointer(); it.Next(); } while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { uint32_t method_index = it.GetMemberIndex(); uint32_t name_index = dex_file->GetMethodId(method_index).name_idx_; const DexFile::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; } DecodeUnsignedLeb128(&trailing); return trailing; } trailing = it.DataPointer(); it.Next(); } 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(FindMethodData(dex_file, method)); CHECK(method_flags_ptr != nullptr) << method; // Unroll this, as we only have three bytes, anyways. uint8_t base1 = static_cast(mask & 0x7F); *(method_flags_ptr++) = (base1 | 0x80); mask >>= 7; uint8_t base2 = static_cast(mask & 0x7F); *(method_flags_ptr++) = (base2 | 0x80); mask >>= 7; uint8_t base3 = static_cast(mask & 0x7F); *method_flags_ptr = base3; } static uint32_t GetMethodFlags(DexFile* dex_file, const char* method) { const uint8_t* method_flags_ptr = const_cast(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(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, "", ~kAccConstructor); ApplyMaskToMethodFlags(dex_file, "", ~kAccConstructor); }, nullptr); constexpr const char* kConstructors[] = { "", ""}; 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"); } // 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, "", ~kAccPublic); OrMaskToMethodFlags(dex_file, "", 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, "", ~kAccPublic); OrMaskToMethodFlags(dex_file, "", mask); }, "Method may have only one of public/protected/private"); } // 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, "", ~kAccPublic); OrMaskToMethodFlags(dex_file, "", kAccStatic); }, "Constructor 1(LMethodFlags;.) 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, "", ~kAccPublic); OrMaskToMethodFlags(dex_file, "", kInitNotAllowed[i]); }, "Constructor 1(LMethodFlags;.) 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[] = { "", "", "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); uint32_t method_idx; FindMethodData(dex_file, "foo", &method_idx); auto* method_id = const_cast(&dex_file->GetMethodId(method_idx)); method_id->name_idx_ = dex_file->NumStringIds(); }, "Method may have only one of public/protected/private, LMethodFlags;.(error)"); } // 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 ()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) { const DexFile::ClassDef& class_def = dex_file->GetClassDef(0); const uint8_t* class_data = dex_file->GetClassData(class_def); ClassDataItemIterator it(*dex_file, class_data); const uint8_t* trailing = class_data; // Need to manually decode the four entries. DataPointer() doesn't work for this, as the first // element has already been loaded into the iterator. DecodeUnsignedLeb128(&trailing); DecodeUnsignedLeb128(&trailing); DecodeUnsignedLeb128(&trailing); DecodeUnsignedLeb128(&trailing); while (it.HasNextStaticField() || it.HasNextInstanceField()) { uint32_t field_index = it.GetMemberIndex(); uint32_t name_index = dex_file->GetFieldId(field_index).name_idx_; const DexFile::StringId& string_id = dex_file->GetStringId(name_index); const char* str = dex_file->GetStringData(string_id); if (strcmp(name, str) == 0) { DecodeUnsignedLeb128(&trailing); return trailing; } trailing = it.DataPointer(); it.Next(); } 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(FindFieldData(dex_file, field)); CHECK(field_flags_ptr != nullptr) << field; // Unroll this, as we only have three bytes, anyways. uint8_t base1 = static_cast(mask & 0x7F); *(field_flags_ptr++) = (base1 | 0x80); mask >>= 7; uint8_t base2 = static_cast(mask & 0x7F); *(field_flags_ptr++) = (base2 | 0x80); mask >>= 7; uint8_t base3 = static_cast(mask & 0x7F); *field_flags_ptr = base3; } static uint32_t GetFieldFlags(DexFile* dex_file, const char* field) { const uint8_t* field_flags_ptr = const_cast(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 ()V // .registers 1 // // .prologue // .line 1 // invoke-direct {p0}, Ljava/lang/Object;->()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. ScratchFile tmp; std::string error_msg; std::unique_ptr raw(OpenDexFileBase64(kDebugInfoTestDex, tmp.GetFilename().c_str(), &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(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. ScratchFile tmp; std::string error_msg; std::unique_ptr raw(OpenDexFileBase64(kGoodTestDex, tmp.GetFilename().c_str(), &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( reinterpret_cast(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. ScratchFile tmp; std::string error_msg; std::unique_ptr raw(OpenDexFileBase64(kProtoOrderingTestDex, tmp.GetFilename().c_str(), &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_ + 1u, dex_file->GetMethodId(method_idx + 1).proto_idx_); // Their return types should be the same. uint32_t proto1_idx = dex_file->GetMethodId(method_idx).proto_idx_; const DexFile::ProtoId& proto1 = dex_file->GetProtoId(proto1_idx); const DexFile::ProtoId& proto2 = dex_file->GetProtoId(proto1_idx + 1u); 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(proto1.parameters_off_), const_cast(proto2.parameters_off_)); std::swap(const_cast(proto1.shorty_idx_), const_cast(proto2.shorty_idx_)); } else { // Copy the proto parameters and shorty to create duplicate proto id. const_cast(proto1.parameters_off_) = proto2.parameters_off_; const_cast(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 --api-level 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 dex_bytes(DecodeBase64(kGoodTestDex, &length)); CHECK(dex_bytes != nullptr); // Note: `dex_file` will be destroyed before `dex_bytes`. std::unique_ptr dex_file(GetDexFile(dex_bytes.get(), length)); std::string error_msg; // Good checksum: all pass. EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(), "good checksum, no verify", /*verify_checksum*/ false, &error_msg)); EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(), "good checksum, verify", /*verify_checksum*/ true, &error_msg)); // Bad checksum: !verify_checksum passes verify_checksum fails. DexFile::Header* header = reinterpret_cast( const_cast(dex_file->Begin())); header->checksum_ = 0; EXPECT_TRUE(DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(), "bad checksum, no verify", /*verify_checksum*/ false, &error_msg)); EXPECT_FALSE(DexFileVerifier::Verify(dex_file.get(), dex_file->Begin(), dex_file->Size(), "bad checksum, verify", /*verify_checksum*/ true, &error_msg)); EXPECT_NE(error_msg.find("Bad checksum"), std::string::npos) << error_msg; } } // namespace art