check-structures-sanity: keep track of whether data is within a larger structure

develop
Ben Lubar 2020-03-11 17:49:34 -05:00
parent eabff06eef
commit 469c49c8b9
No known key found for this signature in database
GPG Key ID: 92939677AB59EDA4
4 changed files with 70 additions and 42 deletions

@ -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<const void *, size_t> validate_vector_size(const QueueItem & item, const CheckedStructure & cs, bool quiet = false);
std::pair<const void *, CheckedStructure> 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.

@ -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<virtual_ptr>(const_cast<void *>(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<virtual_ptr>(const_cast<void *>(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<container_identity *>(cs.identity);
auto item_identity = identity->getItemType();
dispatch_item(item, CheckedStructure(item_identity, identity->byte_size() / item_identity->byte_size(), static_cast<enum_identity *>(identity->getIndexEnumType())));
dispatch_item(item, CheckedStructure(item_identity, identity->byte_size() / item_identity->byte_size(), static_cast<enum_identity *>(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<int64_t>::get()));
dispatch_primitive(item, CheckedStructure(df::identity_traits<int64_t>::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<enum_identity *>(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<std::string>::get());
const static CheckedStructure cs(df::identity_traits<std::string>::get(), 0, nullptr, true);
#ifdef WIN32
struct string_data

@ -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:

@ -191,8 +191,9 @@ const char *Checker::get_vtable_name(const QueueItem & item, const CheckedStruct
return nullptr;
}
std::pair<const void *, size_t> Checker::validate_vector_size(const QueueItem & item, const CheckedStructure & cs, bool quiet)
std::pair<const void *, CheckedStructure> Checker::validate_vector_size(const QueueItem & item, const CheckedStructure & cs, bool quiet)
{
using ret_type = std::pair<const void *, CheckedStructure>;
struct vector_data
{
uintptr_t start;
@ -206,7 +207,7 @@ std::pair<const void *, size_t> 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<const void *, size_t> 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<const void *>(vector.start);
@ -273,7 +274,14 @@ std::pair<const void *, size_t> 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)