check-structures-sanity: warn when an integer might be a pointer

develop
Ben Lubar 2020-03-11 11:20:10 -05:00
parent e2138a6cc2
commit eabff06eef
No known key found for this signature in database
GPG Key ID: 92939677AB59EDA4
5 changed files with 76 additions and 15 deletions

@ -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 *);

@ -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<df::integer_identity_base *>(cs.identity))
{
check_possible_pointer(item, cs);
// TODO check ints?
}
else if (auto float_id = dynamic_cast<df::float_identity_base *>(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<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)
{
// 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<void *>::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<void *>::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<const void *>(item, true);
QueueItem ptr_item(item, "?maybe_pointer?", ptr);
if (ptr && is_valid_dereference(ptr_item, 1, true))
{
check_unknown_pointer(ptr_item);
}
}
}

@ -37,6 +37,7 @@ static command_result command(color_ostream & out, std::vector<std::string> & 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<std::string> & 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<std::string> & pa
while (checker.process_queue())
{
if (out.is_console())
if (!checker.noprogress)
{
out << "checked " << checker.checked_count << " fields\r" << std::flush;
}

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

@ -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<const uint32_t *>(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<const string_data_inner *>(*base) - 1;
if (!is_valid_dereference(QueueItem("str", PTR_ADD(str_data, -16)), 16, true))
{
return nullptr;
}
uint32_t tag = *reinterpret_cast<const uint32_t *>(PTR_ADD(str_data, -8));
if (tag == 0xdfdf4ac8)
{