diff options
author | 2017-05-05 20:28:32 +0000 | |
---|---|---|
committer | 2017-05-05 20:28:33 +0000 | |
commit | 91f956c925f015b8cd355e8ec2a697a4ba69f7ab (patch) | |
tree | 32e541c4e3c4b420626d96a894a8a4d2d0dcf98b | |
parent | c286d17a84c0719fc672db4eef81a9429f7d6ce4 (diff) | |
parent | 326c1a28c3e5d9e7ea1b39d60608eab5481e38b6 (diff) |
Merge "Check static field initial value types match in dex file verifier."
-rw-r--r-- | runtime/dex_file_verifier.cc | 188 | ||||
-rw-r--r-- | runtime/dex_file_verifier.h | 36 | ||||
-rw-r--r-- | runtime/dex_file_verifier_test.cc | 101 |
3 files changed, 265 insertions, 60 deletions
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index 11b3cd025a..c18ab47739 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -922,12 +922,12 @@ bool DexFileVerifier::CheckEncodedAnnotation() { return true; } -bool DexFileVerifier::FindClassFlags(uint32_t index, - bool is_field, - dex::TypeIndex* class_type_index, - uint32_t* class_access_flags) { +bool DexFileVerifier::FindClassIndexAndDef(uint32_t index, + bool is_field, + dex::TypeIndex* class_type_index, + const DexFile::ClassDef** output_class_def) { DCHECK(class_type_index != nullptr); - DCHECK(class_access_flags != nullptr); + DCHECK(output_class_def != nullptr); // First check if the index is valid. if (index >= (is_field ? header_->field_ids_size_ : header_->method_ids_size_)) { @@ -957,7 +957,7 @@ bool DexFileVerifier::FindClassFlags(uint32_t index, for (size_t i = 0; i < header_->class_defs_size_; ++i) { const DexFile::ClassDef* class_def = class_def_begin + i; if (class_def->class_idx_ == *class_type_index) { - *class_access_flags = class_def->access_flags_; + *output_class_def = class_def; return true; } } @@ -966,13 +966,13 @@ bool DexFileVerifier::FindClassFlags(uint32_t index, return false; } -bool DexFileVerifier::CheckOrderAndGetClassFlags(bool is_field, - const char* type_descr, - uint32_t curr_index, - uint32_t prev_index, - bool* have_class, - dex::TypeIndex* class_type_index, - uint32_t* class_access_flags) { +bool DexFileVerifier::CheckOrderAndGetClassDef(bool is_field, + const char* type_descr, + uint32_t curr_index, + uint32_t prev_index, + bool* have_class, + dex::TypeIndex* class_type_index, + const DexFile::ClassDef** class_def) { if (curr_index < prev_index) { ErrorStringPrintf("out-of-order %s indexes %" PRIu32 " and %" PRIu32, type_descr, @@ -982,7 +982,7 @@ bool DexFileVerifier::CheckOrderAndGetClassFlags(bool is_field, } if (!*have_class) { - *have_class = FindClassFlags(curr_index, is_field, class_type_index, class_access_flags); + *have_class = FindClassIndexAndDef(curr_index, is_field, class_type_index, class_def); if (!*have_class) { // Should have really found one. ErrorStringPrintf("could not find declaring class for %s index %" PRIu32, @@ -994,34 +994,130 @@ bool DexFileVerifier::CheckOrderAndGetClassFlags(bool is_field, return true; } +bool DexFileVerifier::CheckStaticFieldTypes(const DexFile::ClassDef* class_def) { + if (class_def == nullptr) { + return true; + } + + ClassDataItemIterator field_it(*dex_file_, ptr_); + EncodedStaticFieldValueIterator array_it(*dex_file_, *class_def); + + for (; field_it.HasNextStaticField() && array_it.HasNext(); field_it.Next(), array_it.Next()) { + uint32_t index = field_it.GetMemberIndex(); + const DexFile::TypeId& type_id = dex_file_->GetTypeId(dex_file_->GetFieldId(index).type_idx_); + const char* field_type_name = + dex_file_->GetStringData(dex_file_->GetStringId(type_id.descriptor_idx_)); + Primitive::Type field_type = Primitive::GetType(field_type_name[0]); + EncodedArrayValueIterator::ValueType array_type = array_it.GetValueType(); + // Ensure this matches RuntimeEncodedStaticFieldValueIterator. + switch (array_type) { + case EncodedArrayValueIterator::ValueType::kBoolean: + if (field_type != Primitive::kPrimBoolean) { + ErrorStringPrintf("unexpected static field initial value type: 'Z' vs '%c'", + field_type_name[0]); + return false; + } + break; + case EncodedArrayValueIterator::ValueType::kByte: + if (field_type != Primitive::kPrimByte) { + ErrorStringPrintf("unexpected static field initial value type: 'B' vs '%c'", + field_type_name[0]); + return false; + } + break; + case EncodedArrayValueIterator::ValueType::kShort: + if (field_type != Primitive::kPrimShort) { + ErrorStringPrintf("unexpected static field initial value type: 'S' vs '%c'", + field_type_name[0]); + return false; + } + break; + case EncodedArrayValueIterator::ValueType::kChar: + if (field_type != Primitive::kPrimChar) { + ErrorStringPrintf("unexpected static field initial value type: 'C' vs '%c'", + field_type_name[0]); + return false; + } + break; + case EncodedArrayValueIterator::ValueType::kInt: + if (field_type != Primitive::kPrimInt) { + ErrorStringPrintf("unexpected static field initial value type: 'I' vs '%c'", + field_type_name[0]); + return false; + } + break; + case EncodedArrayValueIterator::ValueType::kLong: + if (field_type != Primitive::kPrimLong) { + ErrorStringPrintf("unexpected static field initial value type: 'J' vs '%c'", + field_type_name[0]); + return false; + } + break; + case EncodedArrayValueIterator::ValueType::kFloat: + if (field_type != Primitive::kPrimFloat) { + ErrorStringPrintf("unexpected static field initial value type: 'F' vs '%c'", + field_type_name[0]); + return false; + } + break; + case EncodedArrayValueIterator::ValueType::kDouble: + if (field_type != Primitive::kPrimDouble) { + ErrorStringPrintf("unexpected static field initial value type: 'D' vs '%c'", + field_type_name[0]); + return false; + } + break; + case EncodedArrayValueIterator::ValueType::kNull: + case EncodedArrayValueIterator::ValueType::kString: + case EncodedArrayValueIterator::ValueType::kType: + if (field_type != Primitive::kPrimNot) { + ErrorStringPrintf("unexpected static field initial value type: 'L' vs '%c'", + field_type_name[0]); + return false; + } + break; + default: + ErrorStringPrintf("unexpected static field initial value type: %x", array_type); + return false; + } + } + + if (array_it.HasNext()) { + ErrorStringPrintf("too many static field initial values"); + return false; + } + return true; +} + template <bool kStatic> bool DexFileVerifier::CheckIntraClassDataItemFields(ClassDataItemIterator* it, bool* have_class, dex::TypeIndex* class_type_index, - uint32_t* class_access_flags) { + const DexFile::ClassDef** class_def) { DCHECK(it != nullptr); // These calls use the raw access flags to check whether the whole dex field is valid. uint32_t prev_index = 0; for (; kStatic ? it->HasNextStaticField() : it->HasNextInstanceField(); it->Next()) { uint32_t curr_index = it->GetMemberIndex(); - if (!CheckOrderAndGetClassFlags(true, - kStatic ? "static field" : "instance field", - curr_index, - prev_index, - have_class, - class_type_index, - class_access_flags)) { + if (!CheckOrderAndGetClassDef(true, + kStatic ? "static field" : "instance field", + curr_index, + prev_index, + have_class, + class_type_index, + class_def)) { return false; } - prev_index = curr_index; - + DCHECK(class_def != nullptr); if (!CheckClassDataItemField(curr_index, it->GetRawMemberAccessFlags(), - *class_access_flags, + (*class_def)->access_flags_, *class_type_index, kStatic)) { return false; } + + prev_index = curr_index; } return true; @@ -1033,30 +1129,31 @@ bool DexFileVerifier::CheckIntraClassDataItemMethods( std::unordered_set<uint32_t>* direct_method_indexes, bool* have_class, dex::TypeIndex* class_type_index, - uint32_t* class_access_flags) { + const DexFile::ClassDef** class_def) { uint32_t prev_index = 0; for (; kDirect ? it->HasNextDirectMethod() : it->HasNextVirtualMethod(); it->Next()) { uint32_t curr_index = it->GetMemberIndex(); - if (!CheckOrderAndGetClassFlags(false, - kDirect ? "direct method" : "virtual method", - curr_index, - prev_index, - have_class, - class_type_index, - class_access_flags)) { + if (!CheckOrderAndGetClassDef(false, + kDirect ? "direct method" : "virtual method", + curr_index, + prev_index, + have_class, + class_type_index, + class_def)) { return false; } - prev_index = curr_index; - + DCHECK(class_def != nullptr); if (!CheckClassDataItemMethod(curr_index, it->GetRawMemberAccessFlags(), - *class_access_flags, + (*class_def)->access_flags_, *class_type_index, it->GetMethodCodeItemOffset(), direct_method_indexes, kDirect)) { return false; } + + prev_index = curr_index; } return true; @@ -1071,19 +1168,19 @@ bool DexFileVerifier::CheckIntraClassDataItem() { // as the lookup is expensive, cache the result. bool have_class = false; dex::TypeIndex class_type_index; - uint32_t class_access_flags; + const DexFile::ClassDef* class_def = nullptr; // Check fields. if (!CheckIntraClassDataItemFields<true>(&it, &have_class, &class_type_index, - &class_access_flags)) { + &class_def)) { return false; } if (!CheckIntraClassDataItemFields<false>(&it, &have_class, &class_type_index, - &class_access_flags)) { + &class_def)) { return false; } @@ -1092,18 +1189,25 @@ bool DexFileVerifier::CheckIntraClassDataItem() { &direct_method_indexes, &have_class, &class_type_index, - &class_access_flags)) { + &class_def)) { return false; } if (!CheckIntraClassDataItemMethods<false>(&it, &direct_method_indexes, &have_class, &class_type_index, - &class_access_flags)) { + &class_def)) { return false; } - ptr_ = it.EndDataPointer(); + const uint8_t* end_ptr = it.EndDataPointer(); + + // Check static field types against initial static values in encoded array. + if (!CheckStaticFieldTypes(class_def)) { + return false; + } + + ptr_ = end_ptr; return true; } diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h index 71b316c403..d1043c6841 100644 --- a/runtime/dex_file_verifier.h +++ b/runtime/dex_file_verifier.h @@ -86,13 +86,14 @@ class DexFileVerifier { uint32_t code_offset, std::unordered_set<uint32_t>* direct_method_indexes, bool expect_direct); - bool CheckOrderAndGetClassFlags(bool is_field, - const char* type_descr, - uint32_t curr_index, - uint32_t prev_index, - bool* have_class, - dex::TypeIndex* class_type_index, - uint32_t* class_access_flags); + bool CheckOrderAndGetClassDef(bool is_field, + const char* type_descr, + uint32_t curr_index, + uint32_t prev_index, + bool* have_class, + dex::TypeIndex* class_type_index, + const DexFile::ClassDef** class_def); + bool CheckStaticFieldTypes(const DexFile::ClassDef* class_def); bool CheckPadding(size_t offset, uint32_t aligned_offset); bool CheckEncodedValue(); @@ -106,7 +107,7 @@ class DexFileVerifier { bool CheckIntraClassDataItemFields(ClassDataItemIterator* it, bool* have_class, dex::TypeIndex* class_type_index, - uint32_t* class_access_flags); + const DexFile::ClassDef** class_def); // Check all methods of the given type from the given iterator. Load the class data from the first // method, if necessary (and return it), or use the given values. template <bool kDirect> @@ -114,7 +115,7 @@ class DexFileVerifier { std::unordered_set<uint32_t>* direct_method_indexes, bool* have_class, dex::TypeIndex* class_type_index, - uint32_t* class_access_flags); + const DexFile::ClassDef** class_def); bool CheckIntraCodeItem(); bool CheckIntraStringDataItem(); @@ -165,16 +166,15 @@ class DexFileVerifier { __attribute__((__format__(__printf__, 2, 3))) COLD_ATTR; bool FailureReasonIsSet() const { return failure_reason_.size() != 0; } - // Retrieve class index and class access flag from the given member. index is the member index, - // which is taken as either a field or a method index (as designated by is_field). The result, - // if the member and declaring class could be found, is stored in class_type_index and - // class_access_flags. - // This is an expensive lookup, as we have to find the class-def by type index, which is a + // Retrieve class index and class def from the given member. index is the member index, which is + // taken as either a field or a method index (as designated by is_field). The result, if the + // member and declaring class could be found, is stored in class_type_index and class_def. + // This is an expensive lookup, as we have to find the class def by type index, which is a // linear search. The output values should thus be cached by the caller. - bool FindClassFlags(uint32_t index, - bool is_field, - dex::TypeIndex* class_type_index, - uint32_t* class_access_flags); + bool FindClassIndexAndDef(uint32_t index, + bool is_field, + dex::TypeIndex* class_type_index, + const DexFile::ClassDef** output_class_def); // Check validity of the given access flags, interpreted for a field in the context of a class // with the given second access flags. diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc index 94b2615862..068e1223e5 100644 --- a/runtime/dex_file_verifier_test.cc +++ b/runtime/dex_file_verifier_test.cc @@ -2090,4 +2090,105 @@ TEST_F(DexFileVerifierTest, InvokeCustomDexSamples) { } } +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(DexFileVerifier::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(DexFileVerifier::Verify(dex_file.get(), + dex_file->Begin(), + dex_file->Size(), + "good static field initial values array", + /*verify_checksum*/ true, + &error_msg)); +} + } // namespace art |