diff --git a/plugins/devel/check-structures-sanity/check-structures-sanity.h b/plugins/devel/check-structures-sanity/check-structures-sanity.h index 69307098b..b173c45a0 100644 --- a/plugins/devel/check-structures-sanity/check-structures-sanity.h +++ b/plugins/devel/check-structures-sanity/check-structures-sanity.h @@ -33,12 +33,14 @@ struct CheckedStructure { type_identity *identity; size_t count; + size_t allocated_count; enum_identity *eid; bool ptr_is_array; + bool inside_structure; CheckedStructure(); explicit CheckedStructure(type_identity *, size_t = 0); - CheckedStructure(type_identity *, size_t, enum_identity *); + CheckedStructure(type_identity *, size_t, enum_identity *, bool); CheckedStructure(const struct_field_info *); size_t full_size() const; @@ -105,7 +107,7 @@ public: } int64_t get_int_value(const QueueItem & item, type_identity *type, bool quiet = false); const char *get_vtable_name(const QueueItem & item, const CheckedStructure & cs, bool quiet = false); - std::pair validate_vector_size(const QueueItem & item, const CheckedStructure & cs, bool quiet = false); + std::pair validate_vector_size(const QueueItem & item, const CheckedStructure & cs, bool quiet = false); size_t get_allocated_size(const QueueItem & item); #ifndef WIN32 // this function doesn't make sense on windows, where std::string is not pointer-sized. diff --git a/plugins/devel/check-structures-sanity/dispatch.cpp b/plugins/devel/check-structures-sanity/dispatch.cpp index 10fdb3ee3..7c290e853 100644 --- a/plugins/devel/check-structures-sanity/dispatch.cpp +++ b/plugins/devel/check-structures-sanity/dispatch.cpp @@ -160,6 +160,44 @@ void Checker::dispatch_item(const QueueItem & base, const CheckedStructure & cs) return; } + if (sizes && !cs.inside_structure) + { + if (auto allocated_size = get_allocated_size(base)) + { + auto expected_size = cs.identity->byte_size(); + if (cs.allocated_count) + expected_size *= cs.allocated_count; + else if (cs.count) + expected_size *= cs.count; + + if (cs.identity->type() == IDTYPE_CLASS && get_vtable_name(base, cs, true)) + { + if (cs.count) + { + UNEXPECTED; + } + + auto virtual_type = virtual_identity::get(static_cast(const_cast(base.ptr))); + expected_size = virtual_type->byte_size(); + } + + auto & item = base; + + if (allocated_size > expected_size) + { + FAIL("identified structure is too small (expected " << expected_size << " bytes, but there are " << allocated_size << " bytes allocated)"); + } + else if (allocated_size < expected_size) + { + FAIL("identified structure is too big (expected " << expected_size << " bytes, but there are " << allocated_size << " bytes allocated)"); + } + } + else + { + UNEXPECTED; + } + } + auto ptr = base.ptr; auto size = cs.identity->byte_size(); for (size_t i = 0; i < cs.count; i++) @@ -293,28 +331,6 @@ void Checker::dispatch_pointer(const QueueItem & item, const CheckedStructure & CheckedStructure target_cs(target); - if (auto allocated_size = get_allocated_size(target_item)) - { - auto expected_size = target->byte_size(); - if (target->type() == IDTYPE_CLASS && get_vtable_name(target_item, target_cs, true)) - { - auto target_virtual = virtual_identity::get(static_cast(const_cast(target_item.ptr))); - expected_size = target_virtual->byte_size(); - } - if (cs.ptr_is_array && allocated_size % expected_size == 0) - { - target_cs.count = allocated_size / expected_size; - } - else if (allocated_size > expected_size) - { - FAIL("identified structure is too small (expected " << expected_size << " bytes, but there are " << allocated_size << " bytes allocated)"); - } - else if (allocated_size < expected_size) - { - FAIL("identified structure is too big (expected " << expected_size << " bytes, but there are " << allocated_size << " bytes allocated)"); - } - } - // 256 is an arbitrarily chosen size threshold if (cs.count || target->byte_size() <= 256) { @@ -564,7 +580,7 @@ void Checker::dispatch_buffer(const QueueItem & item, const CheckedStructure & c auto identity = static_cast(cs.identity); auto item_identity = identity->getItemType(); - dispatch_item(item, CheckedStructure(item_identity, identity->byte_size() / item_identity->byte_size(), static_cast(identity->getIndexEnumType()))); + dispatch_item(item, CheckedStructure(item_identity, identity->byte_size() / item_identity->byte_size(), static_cast(identity->getIndexEnumType()), cs.inside_structure)); } void Checker::dispatch_stl_ptr_vector(const QueueItem & item, const CheckedStructure & cs) { @@ -657,17 +673,17 @@ void Checker::dispatch_tagged_union_vector(const QueueItem & item, const QueueIt // invalid vectors (already warned) return; } - if (!vec_union.second && !vec_tag.second) + if (!vec_union.second.count && !vec_tag.second.count) { // empty vectors return; } - if (vec_union.second != vec_tag.second) + if (vec_union.second.count != vec_tag.second.count) { - FAIL("tagged union vector is " << vec_union.second << " elements, but tag vector (accessed as " << tag_item.path << ") is " << vec_tag.second << " elements"); + FAIL("tagged union vector is " << vec_union.second.count << " elements, but tag vector (accessed as " << tag_item.path << ") is " << vec_tag.second.count << " elements"); } - for (size_t i = 0; i < vec_union.second && i < vec_tag.second; i++) + for (size_t i = 0; i < vec_union.second.count && i < vec_tag.second.count; i++) { dispatch_tagged_union(QueueItem(item, i, vec_union.first), QueueItem(tag_item, i, vec_tag.first), union_item_cs, tag_item_cs); vec_union.first = PTR_ADD(vec_union.first, union_item_cs.identity->byte_size()); @@ -691,7 +707,7 @@ void Checker::dispatch_untagged_union(const QueueItem & item, const CheckedStruc { // it's 16 bytes on 64-bit linux due to a messy header in libgraphics // but only the first 8 bytes are ever used - dispatch_primitive(item, CheckedStructure(df::identity_traits::get())); + dispatch_primitive(item, CheckedStructure(df::identity_traits::get(), 0, nullptr, cs.inside_structure)); return; } @@ -700,7 +716,7 @@ void Checker::dispatch_untagged_union(const QueueItem & item, const CheckedStruc void Checker::check_unknown_pointer(const QueueItem & item) { - const static CheckedStructure cs(nullptr, 0); + const static CheckedStructure cs(nullptr, 0, nullptr, true); if (auto allocated_size = get_allocated_size(item)) { FAIL("pointer to a block of " << allocated_size << " bytes of allocated memory"); @@ -748,17 +764,16 @@ void Checker::check_stl_vector(const QueueItem & item, type_identity *item_ident return; } - if (vec_items.first && vec_items.second) + if (vec_items.first && vec_items.second.count) { QueueItem items_item(item.path, vec_items.first); - CheckedStructure items_cs(item_identity, vec_items.second, static_cast(eid)); - queue_item(items_item, items_cs); + queue_item(items_item, vec_items.second); } } void Checker::check_stl_string(const QueueItem & item) { - const static CheckedStructure cs(df::identity_traits::get()); + const static CheckedStructure cs(df::identity_traits::get(), 0, nullptr, true); #ifdef WIN32 struct string_data diff --git a/plugins/devel/check-structures-sanity/types.cpp b/plugins/devel/check-structures-sanity/types.cpp index 90e361be8..045bca2d0 100644 --- a/plugins/devel/check-structures-sanity/types.cpp +++ b/plugins/devel/check-structures-sanity/types.cpp @@ -19,14 +19,16 @@ CheckedStructure::CheckedStructure() : { } CheckedStructure::CheckedStructure(type_identity *identity, size_t count) : - CheckedStructure(identity, count, nullptr) + CheckedStructure(identity, count, nullptr, false) { } -CheckedStructure::CheckedStructure(type_identity *identity, size_t count, enum_identity *eid) : +CheckedStructure::CheckedStructure(type_identity *identity, size_t count, enum_identity *eid, bool inside_structure) : identity(identity), count(count), + allocated_count(0), eid(eid), - ptr_is_array(false) + ptr_is_array(false), + inside_structure(inside_structure) { } CheckedStructure::CheckedStructure(const struct_field_info *field) : @@ -39,6 +41,7 @@ CheckedStructure::CheckedStructure(const struct_field_info *field) : identity = field->type; eid = field->eid; + inside_structure = true; switch (field->mode) { case struct_field_info::END: diff --git a/plugins/devel/check-structures-sanity/validate.cpp b/plugins/devel/check-structures-sanity/validate.cpp index 8249a169d..cdf0fe64e 100644 --- a/plugins/devel/check-structures-sanity/validate.cpp +++ b/plugins/devel/check-structures-sanity/validate.cpp @@ -191,8 +191,9 @@ const char *Checker::get_vtable_name(const QueueItem & item, const CheckedStruct return nullptr; } -std::pair Checker::validate_vector_size(const QueueItem & item, const CheckedStructure & cs, bool quiet) +std::pair Checker::validate_vector_size(const QueueItem & item, const CheckedStructure & cs, bool quiet) { + using ret_type = std::pair; struct vector_data { uintptr_t start; @@ -206,7 +207,7 @@ std::pair Checker::validate_vector_size(const QueueItem & ptrdiff_t capacity = vector.end_of_storage - vector.start; bool local_ok = true; - auto item_size = cs.full_size(); + auto item_size = cs.identity ? cs.identity->byte_size() : 0; if (!item_size) { item_size = 1; @@ -263,7 +264,7 @@ std::pair Checker::validate_vector_size(const QueueItem & { FAIL("vector has null pointer but capacity " << (capacity / item_size)); } - return std::make_pair(nullptr, 0); + return ret_type(); } auto start_ptr = reinterpret_cast(vector.start); @@ -273,7 +274,14 @@ std::pair Checker::validate_vector_size(const QueueItem & local_ok = false; } - return local_ok ? std::make_pair(start_ptr, ulength / item_size) : std::make_pair(nullptr, 0); + if (!local_ok) + { + return ret_type(); + } + + CheckedStructure ret_cs(cs.identity, ulength / item_size); + ret_cs.allocated_count = ucapacity / item_size; + return std::make_pair(start_ptr, ret_cs); } size_t Checker::get_allocated_size(const QueueItem & item)