From 39486083f5c5d823353be9567363c1530bbe7840 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Thu, 12 Mar 2020 22:16:30 -0500 Subject: [PATCH] handle overlapping structures in check-structures-sanity better update structures --- library/xml | 2 +- .../check-structures-sanity.h | 4 +- .../check-structures-sanity/dispatch.cpp | 134 +++++++++++------- .../devel/check-structures-sanity/types.cpp | 29 ++-- 4 files changed, 106 insertions(+), 63 deletions(-) diff --git a/library/xml b/library/xml index 08e114638..8ec203292 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 08e114638f3785fd2bee01714be9eca3c1bc99a5 +Subproject commit 8ec203292e0aff9dce2a514d4bee01ba722f16de diff --git a/plugins/devel/check-structures-sanity/check-structures-sanity.h b/plugins/devel/check-structures-sanity/check-structures-sanity.h index 8a68d39e4..45d882fef 100644 --- a/plugins/devel/check-structures-sanity/check-structures-sanity.h +++ b/plugins/devel/check-structures-sanity/check-structures-sanity.h @@ -44,7 +44,7 @@ struct CheckedStructure CheckedStructure(const struct_field_info *); size_t full_size() const; - const struct_field_info *find_field_at_offset_with_type(size_t, const CheckedStructure &) const; + bool has_type_at_offset(const CheckedStructure &, size_t) const; }; #define MIN_SIZE_FOR_SUGGEST 64 @@ -84,7 +84,7 @@ public: bool maybepointer; Checker(color_ostream & out); - bool queue_item(const QueueItem & item, const CheckedStructure & cs); + bool queue_item(const QueueItem & item, CheckedStructure cs); void queue_globals(); bool process_queue(); diff --git a/plugins/devel/check-structures-sanity/dispatch.cpp b/plugins/devel/check-structures-sanity/dispatch.cpp index c2b7beada..1fd85ce52 100644 --- a/plugins/devel/check-structures-sanity/dispatch.cpp +++ b/plugins/devel/check-structures-sanity/dispatch.cpp @@ -30,52 +30,50 @@ color_ostream & Checker::fail(int line, const QueueItem & item, const CheckedStr return out; } -bool Checker::queue_item(const QueueItem & item, const CheckedStructure & cs) +bool Checker::queue_item(const QueueItem & item, CheckedStructure cs) { - if (data.count(item.ptr)) + if (!cs.identity) { - // already checked - auto existing = data.at(item.ptr); - if (cs.identity != existing.second.identity) + UNEXPECTED; + return false; + } + + if (cs.identity->type() == IDTYPE_CLASS) + { + if (cs.count) + { + UNEXPECTED; + } + + if (get_vtable_name(item, cs, true)) { - if (cs.identity->type() == IDTYPE_CLASS && existing.second.identity->type() == IDTYPE_CLASS && get_vtable_name(item, cs, false) && static_cast(cs.identity)->is_instance(static_cast(const_cast(item.ptr)))) + auto actual_identity = virtual_identity::get(reinterpret_cast(const_cast(item.ptr))); + if (static_cast(cs.identity)->is_subclass(actual_identity)) { - return false; + cs.identity = actual_identity; } - - FAIL("TODO: handle merging structures: " << data.at(item.ptr).first << " overlaps " << item.path << " (same pointer)"); } - return false; } auto ptr_end = PTR_ADD(item.ptr, cs.full_size()); auto prev = data.lower_bound(item.ptr); - if (prev != data.cbegin()) + if (prev != data.cbegin() && uintptr_t(prev->first) > uintptr_t(item.ptr)) { prev--; - if (uintptr_t(prev->first) + prev->second.second.full_size() > uintptr_t(item.ptr)) + } + if (prev != data.cend() && uintptr_t(prev->first) <= uintptr_t(item.ptr) && uintptr_t(prev->first) + prev->second.second.full_size() > uintptr_t(item.ptr)) + { + auto offset = uintptr_t(item.ptr) - uintptr_t(prev->first); + if (!prev->second.second.has_type_at_offset(cs, offset)) { - // placeholder algorithm - if (auto sid = dynamic_cast(prev->second.second.identity)) - { - auto offset = uintptr_t(item.ptr) - uintptr_t(prev->first); - for (auto field = sid->getFields(); field->mode != struct_field_info::END; field++) - { - if (field->offset == offset) - { - if (field->mode == struct_field_info::SUBSTRUCT && field->type == cs.identity) - { - return false; - } - - UNEXPECTED; - } - } - } // TODO - FAIL("TODO: handle merging structures: " << prev->second.first << " overlaps " << item.path << " (backward)"); + FAIL("TODO: handle merging structures: " << item.path << " overlaps " << prev->second.first << " (backward)"); + return false; } + + // we've already checked this structure, or we're currently queued to do so + return false; } auto overlap_start = data.lower_bound(item.ptr); @@ -83,7 +81,7 @@ bool Checker::queue_item(const QueueItem & item, const CheckedStructure & cs) for (auto overlap = overlap_start; overlap != overlap_end; overlap++) { auto offset = uintptr_t(overlap->first) - uintptr_t(item.ptr); - if (!cs.find_field_at_offset_with_type(offset, overlap->second.second)) + if (!cs.has_type_at_offset(overlap->second.second, offset)) { // TODO FAIL("TODO: handle merging structures: " << overlap->second.first << " overlaps " << item.path << " (forward)"); @@ -612,8 +610,60 @@ void Checker::dispatch_tagged_union(const QueueItem & item, const QueueItem & ta return; } + auto union_type = static_cast(cs.identity); + auto union_data_ptr = reinterpret_cast(item.ptr); + uint8_t padding_byte = *union_data_ptr; + bool all_padding = false; + if (padding_byte == 0x00 || padding_byte == 0xd2 || padding_byte == 0xff) + { + all_padding = true; + for (size_t i = 0; i < union_type->byte_size(); i++) + { + if (union_data_ptr[i] != padding_byte) + { + all_padding = false; + break; + } + } + } + auto tag_identity = static_cast(tag_cs.identity); auto tag_value = get_int_value(tag_item, tag_identity->getBaseType()); + if (all_padding && padding_byte == 0xd2) + { + // special case: completely uninitialized + switch (tag_identity->byte_size()) + { + case 1: + if (tag_value == 0xd2) + { + return; + } + break; + case 2: + if (tag_value == 0xd2d2) + { + return; + } + break; + case 4: + if (tag_value == 0xd2d2d2d2) + { + return; + } + break; + case 8: + if (uint64_t(tag_value) == 0xd2d2d2d2d2d2d2d2) + { + return; + } + break; + default: + UNEXPECTED; + break; + } + } + auto tag_name = get_enum_item_key(tag_identity, tag_value); if (!tag_name) { @@ -627,7 +677,6 @@ void Checker::dispatch_tagged_union(const QueueItem & item, const QueueItem & ta return; } - auto union_type = static_cast(cs.identity); for (auto field = union_type->getFields(); field->mode != struct_field_info::END; field++) { if (strcmp(field->name, *tag_name)) @@ -644,25 +693,10 @@ void Checker::dispatch_tagged_union(const QueueItem & item, const QueueItem & ta return; } - auto union_data_ptr = reinterpret_cast(item.ptr); - uint8_t padding_byte = *union_data_ptr; - if (padding_byte == 0x00 || padding_byte == 0xd2 || padding_byte == 0xff) + // don't ask for fields if it's all padding + if (all_padding) { - bool all_padding = true; - for (size_t i = 0; i < union_type->byte_size(); i++) - { - if (union_data_ptr[i] != padding_byte) - { - all_padding = false; - break; - } - } - - // don't ask for fields if it's all padding - if (all_padding) - { - return; - } + return; } FAIL("tagged union missing " << *tag_name << " field to match tag (accessed as " << tag_item.path << ") value (" << tag_value << ")"); diff --git a/plugins/devel/check-structures-sanity/types.cpp b/plugins/devel/check-structures-sanity/types.cpp index 4f5e8ca3a..24d33301e 100644 --- a/plugins/devel/check-structures-sanity/types.cpp +++ b/plugins/devel/check-structures-sanity/types.cpp @@ -107,21 +107,30 @@ size_t CheckedStructure::full_size() const return size; } -const struct_field_info *CheckedStructure::find_field_at_offset_with_type(size_t offset, const CheckedStructure & type) const +bool CheckedStructure::has_type_at_offset(const CheckedStructure & type, size_t offset) const { if (!identity) - return nullptr; + return false; + + if (offset == 0 && identity == type.identity) + return true; if (offset >= identity->byte_size() && offset < full_size()) offset %= identity->byte_size(); else if (offset >= identity->byte_size()) - return nullptr; + return false; + + if (identity->type() == IDTYPE_BUFFER) + { + auto target = static_cast(identity)->getItemType(); + return CheckedStructure(target, 0).has_type_at_offset(type, offset % target->byte_size()); + } auto st = dynamic_cast(identity); if (!st) { UNEXPECTED; - return nullptr; + return false; } for (auto p = st; p; p = p->getParent()) @@ -132,18 +141,18 @@ const struct_field_info *CheckedStructure::find_field_at_offset_with_type(size_t for (auto field = fields; field->mode != struct_field_info::END; field++) { - if (field->offset > offset) + if (field->mode == struct_field_info::OBJ_METHOD || field->mode == struct_field_info::CLASS_METHOD) continue; - if (field->offset == offset && CheckedStructure(field).identity == type.identity) - return field; + if (field->offset > offset) + continue; - if (auto subfield = CheckedStructure(field).find_field_at_offset_with_type(offset - field->offset, type)) - return subfield; + if (CheckedStructure(field).has_type_at_offset(type, offset - field->offset)) + return true; } } - return nullptr; + return false; } type_identity *Checker::wrap_in_stl_ptr_vector(type_identity *base)