Implement VerifierDeps encoding/decoding
This patch implements serialization and deserialization of the data
recorded by VerifierDeps.
Test: m test-art-host-gtest-verifier_deps_test
Bug: 30937355
Change-Id: I19320b8e70d5c5128653d09a5cdb5b6f677a2f2d
diff --git a/runtime/verifier/verifier_deps.cc b/runtime/verifier/verifier_deps.cc
index 4953483..350c838 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 @@
}
}
+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 da63d67..dc8dfaf 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 41a9ad3..bbaf59f 100644
--- a/runtime/verifier/verifier_deps_test.cc
+++ b/runtime/verifier/verifier_deps_test.cc
@@ -59,11 +59,21 @@
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 @@
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 @@
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 @@
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 @@
"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