diff options
author | 2016-09-16 13:37:32 +0000 | |
---|---|---|
committer | 2016-09-16 13:37:33 +0000 | |
commit | bbefcb3c8f10ce5567980b6f9905d92a90d18360 (patch) | |
tree | 78fee280229b104fa23708e93dce38bceae257b9 | |
parent | 31eb450500ae9d46e1cb253defd35c8d06539d4a (diff) | |
parent | 6f82fbddf69388180e4dca9bcb5ce2e183e42bfa (diff) |
Merge "Implement VerifierDeps encoding/decoding"
-rw-r--r-- | runtime/verifier/verifier_deps.cc | 148 | ||||
-rw-r--r-- | runtime/verifier/verifier_deps.h | 27 | ||||
-rw-r--r-- | runtime/verifier/verifier_deps_test.cc | 98 |
3 files changed, 257 insertions, 16 deletions
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc index 4953483ffe..350c838717 100644 --- a/runtime/verifier/verifier_deps.cc +++ b/runtime/verifier/verifier_deps.cc @@ -17,6 +17,7 @@ #include "verifier_deps.h" #include "compiler_callbacks.h" +#include "leb128.h" #include "mirror/class-inl.h" #include "runtime.h" @@ -317,5 +318,152 @@ void VerifierDeps::MaybeRecordAssignability(const DexFile& dex_file, } } +static inline uint32_t DecodeUint32WithOverflowCheck(const uint8_t** in, const uint8_t* end) { + CHECK_LT(*in, end); + return DecodeUnsignedLeb128(in); +} + +template<typename T1, typename T2> +static inline void EncodeTuple(std::vector<uint8_t>* out, const std::tuple<T1, T2>& t) { + EncodeUnsignedLeb128(out, std::get<0>(t)); + EncodeUnsignedLeb128(out, std::get<1>(t)); +} + +template<typename T1, typename T2> +static inline void DecodeTuple(const uint8_t** in, const uint8_t* end, std::tuple<T1, T2>* t) { + T1 v1 = static_cast<T1>(DecodeUint32WithOverflowCheck(in, end)); + T2 v2 = static_cast<T2>(DecodeUint32WithOverflowCheck(in, end)); + *t = std::make_tuple(v1, v2); +} + +template<typename T1, typename T2, typename T3> +static inline void EncodeTuple(std::vector<uint8_t>* out, const std::tuple<T1, T2, T3>& t) { + EncodeUnsignedLeb128(out, std::get<0>(t)); + EncodeUnsignedLeb128(out, std::get<1>(t)); + EncodeUnsignedLeb128(out, std::get<2>(t)); +} + +template<typename T1, typename T2, typename T3> +static inline void DecodeTuple(const uint8_t** in, const uint8_t* end, std::tuple<T1, T2, T3>* t) { + T1 v1 = static_cast<T1>(DecodeUint32WithOverflowCheck(in, end)); + T2 v2 = static_cast<T2>(DecodeUint32WithOverflowCheck(in, end)); + T3 v3 = static_cast<T2>(DecodeUint32WithOverflowCheck(in, end)); + *t = std::make_tuple(v1, v2, v3); +} + +template<typename T> +static inline void EncodeSet(std::vector<uint8_t>* out, const std::set<T>& set) { + EncodeUnsignedLeb128(out, set.size()); + for (const T& entry : set) { + EncodeTuple(out, entry); + } +} + +template<typename T> +static inline void DecodeSet(const uint8_t** in, const uint8_t* end, std::set<T>* set) { + DCHECK(set->empty()); + size_t num_entries = DecodeUint32WithOverflowCheck(in, end); + for (size_t i = 0; i < num_entries; ++i) { + T tuple; + DecodeTuple(in, end, &tuple); + set->emplace(tuple); + } +} + +static inline void EncodeStringVector(std::vector<uint8_t>* out, + const std::vector<std::string>& strings) { + EncodeUnsignedLeb128(out, strings.size()); + for (const std::string& str : strings) { + const uint8_t* data = reinterpret_cast<const uint8_t*>(str.c_str()); + size_t length = str.length() + 1; + out->insert(out->end(), data, data + length); + DCHECK_EQ(0u, out->back()); + } +} + +static inline void DecodeStringVector(const uint8_t** in, + const uint8_t* end, + std::vector<std::string>* strings) { + DCHECK(strings->empty()); + size_t num_strings = DecodeUint32WithOverflowCheck(in, end); + strings->reserve(num_strings); + for (size_t i = 0; i < num_strings; ++i) { + CHECK_LT(*in, end); + const char* string_start = reinterpret_cast<const char*>(*in); + strings->emplace_back(std::string(string_start)); + *in += strings->back().length() + 1; + } +} + +void VerifierDeps::Encode(std::vector<uint8_t>* buffer) const { + MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); + for (auto& entry : dex_deps_) { + EncodeStringVector(buffer, entry.second->strings_); + EncodeSet(buffer, entry.second->assignable_types_); + EncodeSet(buffer, entry.second->unassignable_types_); + EncodeSet(buffer, entry.second->classes_); + EncodeSet(buffer, entry.second->fields_); + EncodeSet(buffer, entry.second->direct_methods_); + EncodeSet(buffer, entry.second->virtual_methods_); + EncodeSet(buffer, entry.second->interface_methods_); + } +} + +VerifierDeps::VerifierDeps(const std::vector<const DexFile*>& dex_files, ArrayRef<uint8_t> data) + : VerifierDeps(dex_files) { + const uint8_t* data_start = data.data(); + const uint8_t* data_end = data_start + data.size(); + for (auto& entry : dex_deps_) { + DecodeStringVector(&data_start, data_end, &entry.second->strings_); + DecodeSet(&data_start, data_end, &entry.second->assignable_types_); + DecodeSet(&data_start, data_end, &entry.second->unassignable_types_); + DecodeSet(&data_start, data_end, &entry.second->classes_); + DecodeSet(&data_start, data_end, &entry.second->fields_); + DecodeSet(&data_start, data_end, &entry.second->direct_methods_); + DecodeSet(&data_start, data_end, &entry.second->virtual_methods_); + DecodeSet(&data_start, data_end, &entry.second->interface_methods_); + } + CHECK_LE(data_start, data_end); +} + +bool VerifierDeps::Equals(const VerifierDeps& rhs) const { + MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); + + if (dex_deps_.size() != rhs.dex_deps_.size()) { + return false; + } + + auto lhs_it = dex_deps_.begin(); + auto rhs_it = rhs.dex_deps_.begin(); + + for (; (lhs_it != dex_deps_.end()) && (rhs_it != rhs.dex_deps_.end()); lhs_it++, rhs_it++) { + const DexFile* lhs_dex_file = lhs_it->first; + const DexFile* rhs_dex_file = rhs_it->first; + if (lhs_dex_file != rhs_dex_file) { + return false; + } + + DexFileDeps* lhs_deps = lhs_it->second.get(); + DexFileDeps* rhs_deps = rhs_it->second.get(); + if (!lhs_deps->Equals(*rhs_deps)) { + return false; + } + } + + DCHECK((lhs_it == dex_deps_.end()) && (rhs_it == rhs.dex_deps_.end())); + return true; +} + +bool VerifierDeps::DexFileDeps::Equals(const VerifierDeps::DexFileDeps& rhs) const { + return (strings_ == rhs.strings_) && + (assignable_types_ == rhs.assignable_types_) && + (unassignable_types_ == rhs.unassignable_types_) && + (classes_ == rhs.classes_) && + (fields_ == rhs.fields_) && + (direct_methods_ == rhs.direct_methods_) && + (virtual_methods_ == rhs.virtual_methods_) && + (interface_methods_ == rhs.interface_methods_); +} + } // namespace verifier } // namespace art diff --git a/runtime/verifier/verifier_deps.h b/runtime/verifier/verifier_deps.h index da63d671b0..dc8dfaf2f1 100644 --- a/runtime/verifier/verifier_deps.h +++ b/runtime/verifier/verifier_deps.h @@ -23,6 +23,7 @@ #include "art_field.h" #include "art_method.h" +#include "base/array_ref.h" #include "base/mutex.h" #include "method_resolution_kind.h" #include "os.h" @@ -84,14 +85,23 @@ class VerifierDeps { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::verifier_deps_lock_); + // Serialize the recorded dependencies and store the data into `buffer`. + void Encode(std::vector<uint8_t>* buffer) const + REQUIRES(!Locks::verifier_deps_lock_); + private: static constexpr uint16_t kUnresolvedMarker = static_cast<uint16_t>(-1); + // Only used in tests to reconstruct the data structure from serialized data. + VerifierDeps(const std::vector<const DexFile*>& dex_files, ArrayRef<uint8_t> data) + REQUIRES(!Locks::verifier_deps_lock_); + using ClassResolutionBase = std::tuple<uint32_t, uint16_t>; struct ClassResolution : public ClassResolutionBase { + ClassResolution() = default; + ClassResolution(const ClassResolution&) = default; ClassResolution(uint32_t type_idx, uint16_t access_flags) : ClassResolutionBase(type_idx, access_flags) {} - ClassResolution(const ClassResolution&) = default; bool IsResolved() const { return GetAccessFlags() != kUnresolvedMarker; } uint32_t GetDexTypeIndex() const { return std::get<0>(*this); } @@ -100,9 +110,10 @@ class VerifierDeps { using FieldResolutionBase = std::tuple<uint32_t, uint16_t, uint32_t>; struct FieldResolution : public FieldResolutionBase { + FieldResolution() = default; + FieldResolution(const FieldResolution&) = default; FieldResolution(uint32_t field_idx, uint16_t access_flags, uint32_t declaring_class_idx) : FieldResolutionBase(field_idx, access_flags, declaring_class_idx) {} - FieldResolution(const FieldResolution&) = default; bool IsResolved() const { return GetAccessFlags() != kUnresolvedMarker; } uint32_t GetDexFieldIndex() const { return std::get<0>(*this); } @@ -112,9 +123,10 @@ class VerifierDeps { using MethodResolutionBase = std::tuple<uint32_t, uint16_t, uint32_t>; struct MethodResolution : public MethodResolutionBase { + MethodResolution() = default; + MethodResolution(const MethodResolution&) = default; MethodResolution(uint32_t method_idx, uint16_t access_flags, uint32_t declaring_class_idx) : MethodResolutionBase(method_idx, access_flags, declaring_class_idx) {} - MethodResolution(const MethodResolution&) = default; bool IsResolved() const { return GetAccessFlags() != kUnresolvedMarker; } uint32_t GetDexMethodIndex() const { return std::get<0>(*this); } @@ -124,9 +136,10 @@ class VerifierDeps { using TypeAssignabilityBase = std::tuple<uint32_t, uint32_t>; struct TypeAssignability : public std::tuple<uint32_t, uint32_t> { + TypeAssignability() = default; + TypeAssignability(const TypeAssignability&) = default; TypeAssignability(uint32_t destination_idx, uint32_t source_idx) : TypeAssignabilityBase(destination_idx, source_idx) {} - TypeAssignability(const TypeAssignability&) = default; uint32_t GetDestination() const { return std::get<0>(*this); } uint32_t GetSource() const { return std::get<1>(*this); } @@ -150,6 +163,8 @@ class VerifierDeps { std::set<MethodResolution> direct_methods_; std::set<MethodResolution> virtual_methods_; std::set<MethodResolution> interface_methods_; + + bool Equals(const DexFileDeps& rhs) const; }; // Finds the DexFileDep instance associated with `dex_file`, or nullptr if @@ -215,12 +230,16 @@ class VerifierDeps { REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!Locks::verifier_deps_lock_); + bool Equals(const VerifierDeps& rhs) const + REQUIRES(!Locks::verifier_deps_lock_); + // Map from DexFiles into dependencies collected from verification of their methods. std::map<const DexFile*, std::unique_ptr<DexFileDeps>> dex_deps_ GUARDED_BY(Locks::verifier_deps_lock_); friend class VerifierDepsTest; ART_FRIEND_TEST(VerifierDepsTest, StringToId); + ART_FRIEND_TEST(VerifierDepsTest, EncodeDecode); }; } // namespace verifier diff --git a/runtime/verifier/verifier_deps_test.cc b/runtime/verifier/verifier_deps_test.cc index 41a9ad31c1..bbaf59fef6 100644 --- a/runtime/verifier/verifier_deps_test.cc +++ b/runtime/verifier/verifier_deps_test.cc @@ -59,11 +59,21 @@ class VerifierDepsTest : public CommonRuntimeTest { StackHandleScope<1> hs(Thread::Current()); Handle<mirror::ClassLoader> class_loader_handle( hs.NewHandle(soa->Decode<mirror::ClassLoader*>(class_loader_))); - mirror::Class* result = class_linker_->FindClass(Thread::Current(), - name.c_str(), - class_loader_handle); - DCHECK(result != nullptr) << name; - return result; + mirror::Class* klass = class_linker_->FindClass(Thread::Current(), + name.c_str(), + class_loader_handle); + if (klass == nullptr) { + DCHECK(Thread::Current()->IsExceptionPending()); + Thread::Current()->ClearException(); + } + return klass; + } + + void SetVerifierDeps(const std::vector<const DexFile*>& dex_files) { + verifier_deps_.reset(new verifier::VerifierDeps(dex_files)); + VerifierDepsCompilerCallbacks* callbacks = + reinterpret_cast<VerifierDepsCompilerCallbacks*>(callbacks_.get()); + callbacks->SetVerifierDeps(verifier_deps_.get()); } void LoadDexFile(ScopedObjectAccess* soa) REQUIRES_SHARED(Locks::mutator_lock_) { @@ -72,16 +82,13 @@ class VerifierDepsTest : public CommonRuntimeTest { CHECK_EQ(dex_files.size(), 1u); dex_file_ = dex_files.front(); + SetVerifierDeps(dex_files); + mirror::ClassLoader* loader = soa->Decode<mirror::ClassLoader*>(class_loader_); class_linker_->RegisterDexFile(*dex_file_, loader); klass_Main_ = FindClassByName("LMain;", soa); CHECK(klass_Main_ != nullptr); - - verifier_deps_.reset(new verifier::VerifierDeps(dex_files)); - VerifierDepsCompilerCallbacks* callbacks = - reinterpret_cast<VerifierDepsCompilerCallbacks*>(callbacks_.get()); - callbacks->SetVerifierDeps(verifier_deps_.get()); } bool VerifyMethod(const std::string& method_name) { @@ -138,15 +145,40 @@ class VerifierDepsTest : public CommonRuntimeTest { return !verifier.HasFailures(); } + void VerifyDexFile() { + std::string error_msg; + ScopedObjectAccess soa(Thread::Current()); + + LoadDexFile(&soa); + SetVerifierDeps({ dex_file_ }); + + for (size_t i = 0; i < dex_file_->NumClassDefs(); i++) { + const char* descriptor = dex_file_->GetClassDescriptor(dex_file_->GetClassDef(i)); + mirror::Class* klass = FindClassByName(descriptor, &soa); + if (klass != nullptr) { + MethodVerifier::VerifyClass(Thread::Current(), + klass, + nullptr, + true, + HardFailLogMode::kLogWarning, + &error_msg); + } + } + } + bool TestAssignabilityRecording(const std::string& dst, const std::string& src, bool is_strict, bool is_assignable) { ScopedObjectAccess soa(Thread::Current()); LoadDexFile(&soa); + mirror::Class* klass_dst = FindClassByName(dst, &soa); + DCHECK(klass_dst != nullptr); + mirror::Class* klass_src = FindClassByName(src, &soa); + DCHECK(klass_src != nullptr); verifier_deps_->AddAssignability(*dex_file_, - FindClassByName(dst, &soa), - FindClassByName(src, &soa), + klass_dst, + klass_src, is_strict, is_assignable); return true; @@ -316,6 +348,34 @@ class VerifierDepsTest : public CommonRuntimeTest { return false; } + size_t NumberOfCompiledDexFiles() { + MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); + return verifier_deps_->dex_deps_.size(); + } + + size_t HasEachKindOfRecord() { + MutexLock mu(Thread::Current(), *Locks::verifier_deps_lock_); + + bool has_strings = false; + bool has_assignability = false; + bool has_classes = false; + bool has_fields = false; + bool has_methods = false; + + for (auto& entry : verifier_deps_->dex_deps_) { + has_strings |= !entry.second->strings_.empty(); + has_assignability |= !entry.second->assignable_types_.empty(); + has_assignability |= !entry.second->unassignable_types_.empty(); + has_classes |= !entry.second->classes_.empty(); + has_fields |= !entry.second->fields_.empty(); + has_methods |= !entry.second->direct_methods_.empty(); + has_methods |= !entry.second->virtual_methods_.empty(); + has_methods |= !entry.second->interface_methods_.empty(); + } + + return has_strings && has_assignability && has_classes && has_fields && has_methods; + } + std::unique_ptr<verifier::VerifierDeps> verifier_deps_; const DexFile* dex_file_; jobject class_loader_; @@ -982,5 +1042,19 @@ TEST_F(VerifierDepsTest, InvokeSuper_ThisNotAssignable) { "virtual", "Ljava/lang/Integer;", "intValue", "()I", true, "public", "Ljava/lang/Integer;")); } +TEST_F(VerifierDepsTest, EncodeDecode) { + VerifyDexFile(); + + ASSERT_EQ(1u, NumberOfCompiledDexFiles()); + ASSERT_TRUE(HasEachKindOfRecord()); + + std::vector<uint8_t> buffer; + verifier_deps_->Encode(&buffer); + ASSERT_FALSE(buffer.empty()); + + VerifierDeps decoded_deps({ dex_file_ }, ArrayRef<uint8_t>(buffer)); + ASSERT_TRUE(verifier_deps_->Equals(decoded_deps)); +} + } // namespace verifier } // namespace art |