add more diagnostics that will help in the identification of stl-string fields

develop
Ben Lubar 2020-02-22 15:34:48 -06:00
parent 37a8bf679e
commit 5580d375aa
No known key found for this signature in database
GPG Key ID: 92939677AB59EDA4
2 changed files with 90 additions and 4 deletions

@ -1 +1 @@
Subproject commit f00084673d02e6a170c8ba77da46e456855ba5fa Subproject commit 70e3a89883bece6d126e14eab051624b706cf987

@ -88,7 +88,11 @@ public:
private: private:
bool ok; bool ok;
bool address_in_runtime_data(void *); bool address_in_runtime_data(const void *);
#ifndef WIN32
// this function doesn't make sense on windows, where std::string is not pointer-sized.
const std::string *check_possible_stl_string_pointer(const void *const*);
#endif
bool check_access(const ToCheck &, void *, type_identity *); bool check_access(const ToCheck &, void *, type_identity *);
bool check_access(const ToCheck &, void *, type_identity *, size_t); bool check_access(const ToCheck &, void *, type_identity *, size_t);
bool check_vtable(const ToCheck &, void *, type_identity *); bool check_vtable(const ToCheck &, void *, type_identity *);
@ -241,11 +245,11 @@ bool Checker::check()
#define PTR_ADD(base, offset) (reinterpret_cast<void *>(reinterpret_cast<uintptr_t>((base)) + static_cast<ptrdiff_t>((offset)))) #define PTR_ADD(base, offset) (reinterpret_cast<void *>(reinterpret_cast<uintptr_t>((base)) + static_cast<ptrdiff_t>((offset))))
bool Checker::address_in_runtime_data(void *ptr) bool Checker::address_in_runtime_data(const void *ptr)
{ {
for (auto & range : mapped) for (auto & range : mapped)
{ {
if (!range.isInRange(ptr)) if (!range.isInRange(const_cast<void *>(ptr)))
{ {
continue; continue;
} }
@ -263,6 +267,74 @@ bool Checker::address_in_runtime_data(void *ptr)
return false; return false;
} }
#ifndef WIN32
const std::string *Checker::check_possible_stl_string_pointer(const void *const*base)
{
#ifdef DFHACK64
// on 64-bit linux, empty string is statically allocated.
// on 32-bit linux, empty string is heap-allocated.
std::string empty_string;
if (*base == *reinterpret_cast<void **>(&empty_string))
{
return reinterpret_cast<const std::string *>(base);
}
#endif
const struct string_data_inner
{
size_t length;
size_t capacity;
int32_t refcount;
} *str_data = static_cast<const string_data_inner *>(*base) - 1;
bool heap_allocated = address_in_runtime_data(*base);
if (heap_allocated)
{
uint32_t tag = *reinterpret_cast<const uint32_t *>(PTR_ADD(str_data, -8));
if (tag == 0xdfdf4ac8)
{
size_t allocated_size = *reinterpret_cast<const size_t *>(PTR_ADD(str_data, -16));
size_t expected_size = sizeof(*str_data) + str_data->capacity + 1;
if (allocated_size != expected_size)
{
return nullptr;
}
}
else
{
return nullptr;
}
}
else if (!str_data->length)
{
return nullptr;
}
if (str_data->capacity < str_data->length)
{
return nullptr;
}
const char *ptr = reinterpret_cast<const char *>(*base);
for (size_t i = 0; i < str_data->length; i++)
{
if (!*ptr++)
{
return nullptr;
}
}
if (*ptr++)
{
return nullptr;
}
return reinterpret_cast<const std::string *>(base);
}
#endif
bool Checker::check_access(const ToCheck & item, void *base, type_identity *identity) bool Checker::check_access(const ToCheck & item, void *base, type_identity *identity)
{ {
return check_access(item, base, identity, identity ? identity->byte_size() : 0); return check_access(item, base, identity, identity ? identity->byte_size() : 0);
@ -588,6 +660,20 @@ void Checker::check_dispatch(const ToCheck & item)
FAIL("pointer to a block of " << allocated_size << " bytes of allocated memory"); FAIL("pointer to a block of " << allocated_size << " bytes of allocated memory");
} }
#ifndef WIN32
else if (auto str = check_possible_stl_string_pointer(&item.ptr))
{
FAIL("untyped pointer is actually stl-string with value \"" << *str << "\" (length " << str->length() << ")");
}
#endif
else if (address_in_runtime_data(item.ptr))
{
FAIL("pointer to heap memory, but no size information (part of some STL type?)");
}
else
{
FAIL("pointer to non-heap memory (probably incorrect)");
}
} }
return; return;