2176 lines
		
	
	
		
			92 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			2176 lines
		
	
	
		
			92 KiB
		
	
	
	
		
			C++
		
	
	
	
/*
 | 
						|
 * 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) {
 | 
						|
    return new StandardDexFile(dex_bytes, length, "tmp", 0, nullptr, nullptr);
 | 
						|
  }
 | 
						|
 | 
						|
  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(),
 | 
						|
                               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<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;
 | 
						|
  const DexFileLoader dex_file_loader;
 | 
						|
  DexFileLoaderErrorCode error_code;
 | 
						|
  bool success = dex_file_loader.OpenAll(dex_bytes.get(),
 | 
						|
                                         length,
 | 
						|
                                         location,
 | 
						|
                                         /* 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(),
 | 
						|
                          dex_file->Begin(),
 | 
						|
                          dex_file->Size(),
 | 
						|
                          "good checksum, no verify",
 | 
						|
                          /*verify_checksum=*/ false,
 | 
						|
                          &error_msg));
 | 
						|
  EXPECT_TRUE(dex::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<DexFile::Header*>(
 | 
						|
      const_cast<uint8_t*>(dex_file->Begin()));
 | 
						|
  header->checksum_ = 0;
 | 
						|
  EXPECT_TRUE(dex::Verify(dex_file.get(),
 | 
						|
                          dex_file->Begin(),
 | 
						|
                          dex_file->Size(),
 | 
						|
                          "bad checksum, no verify",
 | 
						|
                          /*verify_checksum=*/ false,
 | 
						|
                          &error_msg));
 | 
						|
  EXPECT_FALSE(dex::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;
 | 
						|
}
 | 
						|
 | 
						|
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(),
 | 
						|
                           dex_file->Begin(),
 | 
						|
                           dex_file->Size(),
 | 
						|
                           "bad static method name",
 | 
						|
                           /*verify_checksum=*/ true,
 | 
						|
                           &error_msg));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(DexFileVerifierTest, BadVirtualMethodName) {
 | 
						|
  // Generated DEX file version (037) from:
 | 
						|
  //
 | 
						|
  //  .class public LBadVirtualName;
 | 
						|
  //  .super Ljava/lang/Object;
 | 
						|
  //
 | 
						|
  //  .method public <bad_name> (II)V
 | 
						|
  //     .registers 2
 | 
						|
  //     return-void
 | 
						|
  //  .end method
 | 
						|
  //
 | 
						|
  //  .method public constructor <init>()V
 | 
						|
  //      .registers 1
 | 
						|
  //      invoke-direct {p0}, Ljava/lang/Object;-><init>()V
 | 
						|
  //      return-void
 | 
						|
  //  .end method
 | 
						|
  //
 | 
						|
  static const char kDexBase64[] =
 | 
						|
      "ZGV4CjAzNwDcPC8B2E7kYTZmeHX2u2IqrpWV9EXBHpE8AgAAcAAAAHhWNBIAAAAAAAAAAJwBAAAI"
 | 
						|
      "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABMAQAA8AAAAPAA"
 | 
						|
      "AAD8AAAABAEAABkBAAAcAQAALgEAAEIBAABFAQAAAwAAAAQAAAAFAAAABgAAAAYAAAADAAAAAAAA"
 | 
						|
      "AAcAAAADAAAATAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAACAAAA"
 | 
						|
      "AAAAAI4BAAAAAAAACjxiYWRfbmFtZT4ABjxpbml0PgATQmFkVmlydHVhbE5hbWUuamF2YQABSQAQ"
 | 
						|
      "TEJhZFZpcnR1YWxOYW1lOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAANWSUkAAAACAAAAAAAAAAAA"
 | 
						|
      "AAABAAcOAAACAAAHAAABAAEAAQAAAFgBAAAEAAAAcBACAAAADgADAAMAAAAAAF0BAAABAAAADgAA"
 | 
						|
      "AAEBAYGABOQCAAH8Ag0AAAAAAAAAAQAAAAAAAAABAAAACAAAAHAAAAACAAAABAAAAJAAAAADAAAA"
 | 
						|
      "AgAAAKAAAAAFAAAAAwAAALgAAAAGAAAAAQAAANAAAAACIAAACAAAAPAAAAABEAAAAQAAAEwBAAAD"
 | 
						|
      "EAAAAQAAAFQBAAADIAAAAgAAAFgBAAABIAAAAgAAAGQBAAAAIAAAAQAAAI4BAAAAEAAAAQAAAJwB"
 | 
						|
      "AAA=";
 | 
						|
 | 
						|
  size_t length;
 | 
						|
  std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length));
 | 
						|
  CHECK(dex_bytes != nullptr);
 | 
						|
  // Note: `dex_file` will be destroyed before `dex_bytes`.
 | 
						|
  std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
 | 
						|
  std::string error_msg;
 | 
						|
  EXPECT_FALSE(dex::Verify(dex_file.get(),
 | 
						|
                           dex_file->Begin(),
 | 
						|
                           dex_file->Size(),
 | 
						|
                           "bad virtual method name",
 | 
						|
                           /*verify_checksum=*/ true,
 | 
						|
                           &error_msg));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(DexFileVerifierTest, BadClinitSignature) {
 | 
						|
  // Generated DEX file version (037) from:
 | 
						|
  //
 | 
						|
  //  .class public LOneClinitBadSig;
 | 
						|
  //  .super Ljava/lang/Object;
 | 
						|
  //
 | 
						|
  //  .method public static constructor <clinit>(II)V
 | 
						|
  //     .registers 2
 | 
						|
  //     return-void
 | 
						|
  //  .end method
 | 
						|
  //
 | 
						|
  //  .method public constructor <init>()V
 | 
						|
  //      .registers 1
 | 
						|
  //      invoke-direct {p0}, Ljava/lang/Object;-><init>()V
 | 
						|
  //      return-void
 | 
						|
  //  .end method
 | 
						|
  //
 | 
						|
  static const char kDexBase64[] =
 | 
						|
      "ZGV4CjAzNwBNOwTbfJmWq5eMOlxUY4EICGiEGJMVg8RAAgAAcAAAAHhWNBIAAAAAAAAAAKABAAAI"
 | 
						|
      "AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAABQAQAA8AAAAPAA"
 | 
						|
      "AAD6AAAAAgEAAAUBAAAYAQAALAEAAEIBAABFAQAAAgAAAAMAAAAEAAAABgAAAAYAAAADAAAAAAAA"
 | 
						|
      "AAcAAAADAAAATAEAAAEAAQAAAAAAAQAAAAEAAAACAAAAAQAAAAEAAAABAAAAAgAAAAAAAAAFAAAA"
 | 
						|
      "AAAAAJABAAAAAAAACDxjbGluaXQ+AAY8aW5pdD4AAUkAEUxPbmVDbGluaXRCYWRTaWc7ABJMamF2"
 | 
						|
      "YS9sYW5nL09iamVjdDsAFE9uZUNsaW5pdEJhZFNpZy5qYXZhAAFWAANWSUkAAAACAAAAAAAAAAAA"
 | 
						|
      "AAAAAgAABwABAAcOAAACAAIAAAAAAFgBAAABAAAADgAAAAEAAQABAAAAXgEAAAQAAABwEAIAAAAO"
 | 
						|
      "AAAAAgAAiYAE5AIBgYAE+AINAAAAAAAAAAEAAAAAAAAAAQAAAAgAAABwAAAAAgAAAAQAAACQAAAA"
 | 
						|
      "AwAAAAIAAACgAAAABQAAAAMAAAC4AAAABgAAAAEAAADQAAAAAiAAAAgAAADwAAAAARAAAAEAAABM"
 | 
						|
      "AQAAAxAAAAEAAABUAQAAAyAAAAIAAABYAQAAASAAAAIAAABkAQAAACAAAAEAAACQAQAAABAAAAEA"
 | 
						|
      "AACgAQAA";
 | 
						|
 | 
						|
  size_t length;
 | 
						|
  std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length));
 | 
						|
  CHECK(dex_bytes != nullptr);
 | 
						|
  // Note: `dex_file` will be destroyed before `dex_bytes`.
 | 
						|
  std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
 | 
						|
  std::string error_msg;
 | 
						|
  EXPECT_FALSE(dex::Verify(dex_file.get(),
 | 
						|
                           dex_file->Begin(),
 | 
						|
                           dex_file->Size(),
 | 
						|
                           "bad clinit signature",
 | 
						|
                           /*verify_checksum=*/ true,
 | 
						|
                           &error_msg));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(DexFileVerifierTest, BadClinitSignatureAgain) {
 | 
						|
  // Generated DEX file version (037) from:
 | 
						|
  //
 | 
						|
  //  .class public LOneClinitBadSigAgain;
 | 
						|
  //  .super Ljava/lang/Object;
 | 
						|
  //
 | 
						|
  //  .method public static constructor <clinit>()I
 | 
						|
  //     .registers 1
 | 
						|
  //     const/4 v0, 1
 | 
						|
  //     return v0
 | 
						|
  //  .end method
 | 
						|
  //
 | 
						|
  //  .method public constructor <init>()V
 | 
						|
  //      .registers 1
 | 
						|
  //      invoke-direct {p0}, Ljava/lang/Object;-><init>()V
 | 
						|
  //      return-void
 | 
						|
  //  .end method
 | 
						|
  //
 | 
						|
  static const char kDexBase64[] =
 | 
						|
      "ZGV4CjAzNwBfPcPu5NVwKUqZIu/YR8xqVlVD5UzTk0gEAgAAcAAAAHhWNBIAAAAAAAAAAIgBAAAH"
 | 
						|
      "AAAAcAAAAAQAAACMAAAAAgAAAJwAAAAAAAAAAAAAAAMAAAC0AAAAAQAAAMwAAAAYAQAA7AAAAOwA"
 | 
						|
      "AAD2AAAA/gAAAAEBAAAZAQAALQEAAEgBAAACAAAAAwAAAAQAAAAGAAAAAgAAAAAAAAAAAAAABgAA"
 | 
						|
      "AAMAAAAAAAAAAQAAAAAAAAABAAEAAQAAAAIAAQABAAAAAQAAAAEAAAACAAAAAAAAAAUAAAAAAAAA"
 | 
						|
      "eAEAAAAAAAAIPGNsaW5pdD4ABjxpbml0PgABSQAWTE9uZUNsaW5pdEJhZFNpZ0FnYWluOwASTGph"
 | 
						|
      "dmEvbGFuZy9PYmplY3Q7ABlPbmVDbGluaXRCYWRTaWdBZ2Fpbi5qYXZhAAFWAAABAAAAAAAAAAAA"
 | 
						|
      "AAACAAAAEhAPAAEAAQABAAAAAAAAAAQAAABwEAIAAAAOAAAAAgAAiYAEzAIBgYAE4AIKAAAAAAAA"
 | 
						|
      "AAEAAAAAAAAAAQAAAAcAAABwAAAAAgAAAAQAAACMAAAAAwAAAAIAAACcAAAABQAAAAMAAAC0AAAA"
 | 
						|
      "BgAAAAEAAADMAAAAAiAAAAcAAADsAAAAASAAAAIAAABMAQAAACAAAAEAAAB4AQAAABAAAAEAAACI"
 | 
						|
      "AQAA";
 | 
						|
 | 
						|
  size_t length;
 | 
						|
  std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length));
 | 
						|
  CHECK(dex_bytes != nullptr);
 | 
						|
  // Note: `dex_file` will be destroyed before `dex_bytes`.
 | 
						|
  std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
 | 
						|
  std::string error_msg;
 | 
						|
  EXPECT_FALSE(dex::Verify(dex_file.get(),
 | 
						|
                           dex_file->Begin(),
 | 
						|
                           dex_file->Size(),
 | 
						|
                           "bad clinit signature",
 | 
						|
                           /*verify_checksum=*/ true,
 | 
						|
                           &error_msg));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(DexFileVerifierTest, BadInitSignature) {
 | 
						|
  // Generated DEX file version (037) from:
 | 
						|
  //
 | 
						|
  //  .class public LBadInitSig;
 | 
						|
  //  .super Ljava/lang/Object;
 | 
						|
  //
 | 
						|
  //  .method public constructor <init>()I
 | 
						|
  //      .registers 1
 | 
						|
  //      invoke-direct {p0}, Ljava/lang/Object;-><init>()V
 | 
						|
  //      const v0, 1
 | 
						|
  //      return v0
 | 
						|
  //  .end method
 | 
						|
  //
 | 
						|
  static const char kDexBase64[] =
 | 
						|
      "ZGV4CjAzNwCdMdeh1KoHWamF2Prq32LF39YZ78fV7q+wAQAAcAAAAHhWNBIAAAAAAAAAADQBAAAF"
 | 
						|
      "AAAAcAAAAAQAAACEAAAAAgAAAJQAAAAAAAAAAAAAAAIAAACsAAAAAQAAALwAAADUAAAA3AAAANwA"
 | 
						|
      "AADkAAAA5wAAAPUAAAAJAQAAAQAAAAIAAAADAAAABAAAAAEAAAAAAAAAAAAAAAQAAAADAAAAAAAA"
 | 
						|
      "AAEAAAAAAAAAAgABAAAAAAABAAAAAQAAAAIAAAAAAAAA/////wAAAAAqAQAAAAAAAAY8aW5pdD4A"
 | 
						|
      "AUkADExCYWRJbml0U2lnOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAEAAQABAAAAAAAAAAcAAABw"
 | 
						|
      "EAEAAAAUAAEAAAAPAAAAAQAAgYAEjAIKAAAAAAAAAAEAAAAAAAAAAQAAAAUAAABwAAAAAgAAAAQA"
 | 
						|
      "AACEAAAAAwAAAAIAAACUAAAABQAAAAIAAACsAAAABgAAAAEAAAC8AAAAAiAAAAUAAADcAAAAASAA"
 | 
						|
      "AAEAAAAMAQAAACAAAAEAAAAqAQAAABAAAAEAAAA0AQAA";
 | 
						|
 | 
						|
  size_t length;
 | 
						|
  std::unique_ptr<uint8_t[]> dex_bytes(DecodeBase64(kDexBase64, &length));
 | 
						|
  CHECK(dex_bytes != nullptr);
 | 
						|
  // Note: `dex_file` will be destroyed before `dex_bytes`.
 | 
						|
  std::unique_ptr<DexFile> dex_file(GetDexFile(dex_bytes.get(), length));
 | 
						|
  std::string error_msg;
 | 
						|
  EXPECT_FALSE(dex::Verify(dex_file.get(),
 | 
						|
                           dex_file->Begin(),
 | 
						|
                           dex_file->Size(),
 | 
						|
                           "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(),
 | 
						|
                            dex_file->Begin(),
 | 
						|
                            dex_file->Size(),
 | 
						|
                            "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(),
 | 
						|
                           dex_file->Begin(),
 | 
						|
                           dex_file->Size(),
 | 
						|
                           "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(),
 | 
						|
                          dex_file->Begin(),
 | 
						|
                          dex_file->Size(),
 | 
						|
                          "good static field initial values array",
 | 
						|
                          /*verify_checksum=*/ true,
 | 
						|
                          &error_msg));
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace art
 |