handle overlapping structures in check-structures-sanity better

update structures
develop
Ben Lubar 2020-03-12 22:16:30 -05:00
parent d456e3db7a
commit 39486083f5
No known key found for this signature in database
GPG Key ID: 92939677AB59EDA4
4 changed files with 106 additions and 63 deletions

@ -1 +1 @@
Subproject commit 08e114638f3785fd2bee01714be9eca3c1bc99a5 Subproject commit 8ec203292e0aff9dce2a514d4bee01ba722f16de

@ -44,7 +44,7 @@ struct CheckedStructure
CheckedStructure(const struct_field_info *); CheckedStructure(const struct_field_info *);
size_t full_size() const; 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 #define MIN_SIZE_FOR_SUGGEST 64
@ -84,7 +84,7 @@ public:
bool maybepointer; bool maybepointer;
Checker(color_ostream & out); Checker(color_ostream & out);
bool queue_item(const QueueItem & item, const CheckedStructure & cs); bool queue_item(const QueueItem & item, CheckedStructure cs);
void queue_globals(); void queue_globals();
bool process_queue(); bool process_queue();

@ -30,52 +30,50 @@ color_ostream & Checker::fail(int line, const QueueItem & item, const CheckedStr
return out; 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 UNEXPECTED;
auto existing = data.at(item.ptr); return false;
if (cs.identity != existing.second.identity) }
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<virtual_identity *>(cs.identity)->is_instance(static_cast<virtual_ptr>(const_cast<void *>(item.ptr)))) auto actual_identity = virtual_identity::get(reinterpret_cast<virtual_ptr>(const_cast<void *>(item.ptr)));
if (static_cast<virtual_identity *>(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 ptr_end = PTR_ADD(item.ptr, cs.full_size());
auto prev = data.lower_bound(item.ptr); auto prev = data.lower_bound(item.ptr);
if (prev != data.cbegin()) if (prev != data.cbegin() && uintptr_t(prev->first) > uintptr_t(item.ptr))
{ {
prev--; 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<struct_identity *>(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 // 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); 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++) for (auto overlap = overlap_start; overlap != overlap_end; overlap++)
{ {
auto offset = uintptr_t(overlap->first) - uintptr_t(item.ptr); 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 // TODO
FAIL("TODO: handle merging structures: " << overlap->second.first << " overlaps " << item.path << " (forward)"); 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; return;
} }
auto union_type = static_cast<union_identity *>(cs.identity);
auto union_data_ptr = reinterpret_cast<const uint8_t *>(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<enum_identity *>(tag_cs.identity); auto tag_identity = static_cast<enum_identity *>(tag_cs.identity);
auto tag_value = get_int_value(tag_item, tag_identity->getBaseType()); 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); auto tag_name = get_enum_item_key(tag_identity, tag_value);
if (!tag_name) if (!tag_name)
{ {
@ -627,7 +677,6 @@ void Checker::dispatch_tagged_union(const QueueItem & item, const QueueItem & ta
return; return;
} }
auto union_type = static_cast<union_identity *>(cs.identity);
for (auto field = union_type->getFields(); field->mode != struct_field_info::END; field++) for (auto field = union_type->getFields(); field->mode != struct_field_info::END; field++)
{ {
if (strcmp(field->name, *tag_name)) if (strcmp(field->name, *tag_name))
@ -644,25 +693,10 @@ void Checker::dispatch_tagged_union(const QueueItem & item, const QueueItem & ta
return; return;
} }
auto union_data_ptr = reinterpret_cast<const uint8_t *>(item.ptr); // don't ask for fields if it's all padding
uint8_t padding_byte = *union_data_ptr; if (all_padding)
if (padding_byte == 0x00 || padding_byte == 0xd2 || padding_byte == 0xff)
{ {
bool all_padding = true; return;
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;
}
} }
FAIL("tagged union missing " << *tag_name << " field to match tag (accessed as " << tag_item.path << ") value (" << tag_value << ")"); FAIL("tagged union missing " << *tag_name << " field to match tag (accessed as " << tag_item.path << ") value (" << tag_value << ")");

@ -107,21 +107,30 @@ size_t CheckedStructure::full_size() const
return size; 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) if (!identity)
return nullptr; return false;
if (offset == 0 && identity == type.identity)
return true;
if (offset >= identity->byte_size() && offset < full_size()) if (offset >= identity->byte_size() && offset < full_size())
offset %= identity->byte_size(); offset %= identity->byte_size();
else if (offset >= identity->byte_size()) else if (offset >= identity->byte_size())
return nullptr; return false;
if (identity->type() == IDTYPE_BUFFER)
{
auto target = static_cast<container_identity *>(identity)->getItemType();
return CheckedStructure(target, 0).has_type_at_offset(type, offset % target->byte_size());
}
auto st = dynamic_cast<struct_identity *>(identity); auto st = dynamic_cast<struct_identity *>(identity);
if (!st) if (!st)
{ {
UNEXPECTED; UNEXPECTED;
return nullptr; return false;
} }
for (auto p = st; p; p = p->getParent()) 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++) 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; continue;
if (field->offset == offset && CheckedStructure(field).identity == type.identity) if (field->offset > offset)
return field; continue;
if (auto subfield = CheckedStructure(field).find_field_at_offset_with_type(offset - field->offset, type)) if (CheckedStructure(field).has_type_at_offset(type, offset - field->offset))
return subfield; return true;
} }
} }
return nullptr; return false;
} }
type_identity *Checker::wrap_in_stl_ptr_vector(type_identity *base) type_identity *Checker::wrap_in_stl_ptr_vector(type_identity *base)