Check static field initial value types match in dex file verifier.
Can cause segmentation faults in the gc if object and primitive types
are mixed.
Bug: 34115871
Test: mm test-art-host-gtest-dex_file_verifier_test
Change-Id: If88fd7be51eeca28e2a106964fa3c29f79aee307
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 11b3cd0..c18ab47 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -922,12 +922,12 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
// 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 @@
&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 71b316c..d1043c6 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -86,13 +86,14 @@
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 @@
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 @@
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 @@
__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 94b2615..068e122 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -2090,4 +2090,105 @@
}
}
+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