diff --git a/plugins/devel/check-structures-sanity/check-structures-sanity.h b/plugins/devel/check-structures-sanity/check-structures-sanity.h index 6946faa3f..69307098b 100644 --- a/plugins/devel/check-structures-sanity/check-structures-sanity.h +++ b/plugins/devel/check-structures-sanity/check-structures-sanity.h @@ -34,6 +34,7 @@ struct CheckedStructure type_identity *identity; size_t count; enum_identity *eid; + bool ptr_is_array; CheckedStructure(); explicit CheckedStructure(type_identity *, size_t = 0); @@ -76,6 +77,7 @@ public: bool sizes; bool unnamed; bool failfast; + bool noprogress; Checker(color_ostream & out); bool queue_item(const QueueItem & item, const CheckedStructure & cs); @@ -133,6 +135,7 @@ private: void check_unknown_pointer(const QueueItem &); void check_stl_vector(const QueueItem &, type_identity *, type_identity *); void check_stl_string(const QueueItem &); + void check_possible_pointer(const QueueItem &, const CheckedStructure &); friend struct CheckedStructure; static type_identity *wrap_in_pointer(type_identity *); diff --git a/plugins/devel/check-structures-sanity/dispatch.cpp b/plugins/devel/check-structures-sanity/dispatch.cpp index 1e02aaf9f..10fdb3ee3 100644 --- a/plugins/devel/check-structures-sanity/dispatch.cpp +++ b/plugins/devel/check-structures-sanity/dispatch.cpp @@ -11,7 +11,8 @@ Checker::Checker(color_ostream & out) : enums(false), sizes(false), unnamed(false), - failfast(false) + failfast(false), + noprogress(!out.is_console()) { Core::getInstance().p->getMemRanges(mapped); } @@ -252,6 +253,8 @@ void Checker::dispatch_primitive(const QueueItem & item, const CheckedStructure } else if (auto int_id = dynamic_cast(cs.identity)) { + check_possible_pointer(item, cs); + // TODO check ints? } else if (auto float_id = dynamic_cast(cs.identity)) @@ -288,22 +291,46 @@ void Checker::dispatch_pointer(const QueueItem & item, const CheckedStructure & return; } + 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) { // target is small, or we are inside an array of pointers; handle now - if (queue_item(target_item, CheckedStructure(target))) + if (queue_item(target_item, target_cs)) { // we insert it into the queue to make sure we're not stuck in a loop // get it back out of the queue to prevent the queue growing too big queue.pop_back(); - dispatch_item(target_item, CheckedStructure(target)); + dispatch_item(target_item, target_cs); } } else { // target is large and not part of an array; handle later - queue_item(target_item, CheckedStructure(target)); + queue_item(target_item, target_cs); } } void Checker::dispatch_container(const QueueItem & item, const CheckedStructure & cs) @@ -350,6 +377,8 @@ void Checker::dispatch_bit_container(const QueueItem & item, const CheckedStruct } void Checker::dispatch_bitfield(const QueueItem & item, const CheckedStructure & cs) { + check_possible_pointer(item, cs); + if (!enums) { return; @@ -418,6 +447,8 @@ void Checker::dispatch_bitfield(const QueueItem & item, const CheckedStructure & } void Checker::dispatch_enum(const QueueItem & item, const CheckedStructure & cs) { + check_possible_pointer(item, cs); + if (!enums) { return; @@ -669,7 +700,7 @@ void Checker::dispatch_untagged_union(const QueueItem & item, const CheckedStruc void Checker::check_unknown_pointer(const QueueItem & item) { - CheckedStructure cs(df::identity_traits::get()); + const static CheckedStructure cs(nullptr, 0); if (auto allocated_size = get_allocated_size(item)) { FAIL("pointer to a block of " << allocated_size << " bytes of allocated memory"); @@ -680,13 +711,14 @@ void Checker::check_unknown_pointer(const QueueItem & item) // check recursively if it's the right size for a pointer // or if it starts with what might be a valid pointer - if (allocated_size == sizeof(void *) || (allocated_size > sizeof(void *) && is_valid_dereference(item, cs, true))) + QueueItem ptr_item(item, "?ptr?", item.ptr); + if (allocated_size == sizeof(void *) || (allocated_size > sizeof(void *) && is_valid_dereference(ptr_item, 1, true))) { - QueueItem ptr_item(item, "?ptr?", item.ptr); - if (queue_item(ptr_item, cs)) + CheckedStructure ptr_cs(df::identity_traits::get()); + if (queue_item(ptr_item, ptr_cs)) { queue.pop_back(); - dispatch_pointer(ptr_item, cs); + dispatch_pointer(ptr_item, ptr_cs); } } } @@ -805,3 +837,15 @@ void Checker::check_stl_string(const QueueItem & item) } #endif } +void Checker::check_possible_pointer(const QueueItem & item, const CheckedStructure & cs) +{ + if (sizes && uintptr_t(item.ptr) % sizeof(void *) == 0) + { + auto ptr = validate_and_dereference(item, true); + QueueItem ptr_item(item, "?maybe_pointer?", ptr); + if (ptr && is_valid_dereference(ptr_item, 1, true)) + { + check_unknown_pointer(ptr_item); + } + } +} diff --git a/plugins/devel/check-structures-sanity/main.cpp b/plugins/devel/check-structures-sanity/main.cpp index 8303b0b4e..f29e8f201 100644 --- a/plugins/devel/check-structures-sanity/main.cpp +++ b/plugins/devel/check-structures-sanity/main.cpp @@ -37,6 +37,7 @@ static command_result command(color_ostream & out, std::vector & pa CoreSuspender suspend; Checker checker(out); + // check parameters with values first #define VAL_PARAM(name, expr_using_value) \ auto name ## _idx = std::find(parameters.begin(), parameters.end(), "-" #name); \ @@ -73,6 +74,7 @@ static command_result command(color_ostream & out, std::vector & pa BOOL_PARAM(sizes); BOOL_PARAM(unnamed); BOOL_PARAM(failfast); + BOOL_PARAM(noprogress); #undef BOOL_PARAM if (parameters.size() > 1) @@ -117,7 +119,7 @@ static command_result command(color_ostream & out, std::vector & pa while (checker.process_queue()) { - if (out.is_console()) + if (!checker.noprogress) { out << "checked " << checker.checked_count << " fields\r" << std::flush; } diff --git a/plugins/devel/check-structures-sanity/types.cpp b/plugins/devel/check-structures-sanity/types.cpp index dc3ccb390..90e361be8 100644 --- a/plugins/devel/check-structures-sanity/types.cpp +++ b/plugins/devel/check-structures-sanity/types.cpp @@ -19,15 +19,14 @@ CheckedStructure::CheckedStructure() : { } CheckedStructure::CheckedStructure(type_identity *identity, size_t count) : - identity(identity), - count(count), - eid(nullptr) + CheckedStructure(identity, count, nullptr) { } CheckedStructure::CheckedStructure(type_identity *identity, size_t count, enum_identity *eid) : identity(identity), count(count), - eid(eid) + eid(eid), + ptr_is_array(false) { } CheckedStructure::CheckedStructure(const struct_field_info *field) : @@ -60,7 +59,10 @@ CheckedStructure::CheckedStructure(const struct_field_info *field) : count = field->count; break; case struct_field_info::POINTER: - // TODO: check flags (stored in field->count) + if (field->count & PTRFLAG_IS_ARRAY) + { + ptr_is_array = true; + } identity = Checker::wrap_in_pointer(field->type); break; case struct_field_info::STATIC_ARRAY: diff --git a/plugins/devel/check-structures-sanity/validate.cpp b/plugins/devel/check-structures-sanity/validate.cpp index 9b089cb79..8249a169d 100644 --- a/plugins/devel/check-structures-sanity/validate.cpp +++ b/plugins/devel/check-structures-sanity/validate.cpp @@ -283,6 +283,11 @@ size_t Checker::get_allocated_size(const QueueItem & item) return 0; } + if (uintptr_t(item.ptr) % 32 != 16) + { + return 0; + } + uint32_t tag = *reinterpret_cast(PTR_ADD(item.ptr, -8)); if (tag == 0xdfdf4ac8) { @@ -308,6 +313,11 @@ const std::string *Checker::validate_stl_string_pointer(const void *const* base) int32_t refcount; } *str_data = static_cast(*base) - 1; + if (!is_valid_dereference(QueueItem("str", PTR_ADD(str_data, -16)), 16, true)) + { + return nullptr; + } + uint32_t tag = *reinterpret_cast(PTR_ADD(str_data, -8)); if (tag == 0xdfdf4ac8) {