From 0711b2569a3ea321a48207cdfc41fb2a660924b9 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Sat, 22 Feb 2020 14:04:53 -0600 Subject: [PATCH 1/4] in -sizes mode, report sizes of unknown structures behind pointers. update structures --- library/xml | 2 +- plugins/devel/check-structures-sanity.cpp | 37 ++++++++++++++++++++--- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/library/xml b/library/xml index 874ad8936..f00084673 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 874ad8936e9f29cd616d76ebdc93fef958ded379 +Subproject commit f00084673d02e6a170c8ba77da46e456855ba5fa diff --git a/plugins/devel/check-structures-sanity.cpp b/plugins/devel/check-structures-sanity.cpp index a984c0745..538fa8b48 100644 --- a/plugins/devel/check-structures-sanity.cpp +++ b/plugins/devel/check-structures-sanity.cpp @@ -250,9 +250,14 @@ bool Checker::address_in_runtime_data(void *ptr) continue; } - // TODO: figure out how to differentiate statically-allocated pages from malloc'd data pages +#ifdef WIN32 + // TODO: figure out how to differentiate statically-allocated pages + // from malloc'd data pages UNEXPECTED; return false; +#else + return !strcmp(range.name, "[heap]"); +#endif } return false; @@ -569,7 +574,21 @@ void Checker::check_dispatch(const ToCheck & item) if (!item.identity) { // warn about bad pointers - check_access(item, item.ptr, df::identity_traits::get(), 1); + if (!check_access(item, item.ptr, df::identity_traits::get(), 1)) + { + return; + } + + if (sizes) + { + uint32_t tag = *reinterpret_cast(PTR_ADD(item.ptr, -8)); + if (tag == 0xdfdf4ac8) + { + size_t allocated_size = *reinterpret_cast(PTR_ADD(item.ptr - 1, -16)); + + FAIL("pointer to a block of " << allocated_size << " bytes of allocated memory"); + } + } return; } @@ -658,6 +677,16 @@ void Checker::check_primitive(const ToCheck & item) return; } + if (item.identity->getFullName() == "bool") + { + auto value = *reinterpret_cast(item.ptr); + if (value > 1 && value != 0xd2) + { + FAIL("invalid boolean value " << stl_sprintf("%d (0x%02x)", value, value)); + } + return; + } + // TODO: check other primitives? } @@ -965,7 +994,7 @@ void Checker::check_vector(const ToCheck & item, type_identity *item_identity, b FAIL("vector capacity (" << (capacity / ptrdiff_t(item_size)) << ") is less than its length (" << (length / ptrdiff_t(item_size)) << ")"); } - if (!item_identity && pointer) + if (!item_identity && pointer && !sizes) { // non-identified vector type in structures return; @@ -990,7 +1019,7 @@ void Checker::check_vector(const ToCheck & item, type_identity *item_identity, b local_ok = false; } - if (local_ok && check_access(item, reinterpret_cast(vector.start), item.identity, capacity) && item_identity) + if (local_ok && check_access(item, reinterpret_cast(vector.start), item.identity, capacity)) { auto ienum = static_cast(static_cast(item.identity)->getIndexEnumType()); queue_static_array(item, reinterpret_cast(vector.start), item_identity, ulength / item_size, pointer, ienum); From 37a8bf679e1aa2ee0eb5eeb2a8819a719b0974ae Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Sat, 22 Feb 2020 14:10:26 -0600 Subject: [PATCH 2/4] fix copy/paste error --- plugins/devel/check-structures-sanity.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/devel/check-structures-sanity.cpp b/plugins/devel/check-structures-sanity.cpp index 538fa8b48..0fb49b010 100644 --- a/plugins/devel/check-structures-sanity.cpp +++ b/plugins/devel/check-structures-sanity.cpp @@ -584,7 +584,7 @@ void Checker::check_dispatch(const ToCheck & item) uint32_t tag = *reinterpret_cast(PTR_ADD(item.ptr, -8)); if (tag == 0xdfdf4ac8) { - size_t allocated_size = *reinterpret_cast(PTR_ADD(item.ptr - 1, -16)); + size_t allocated_size = *reinterpret_cast(PTR_ADD(item.ptr, -16)); FAIL("pointer to a block of " << allocated_size << " bytes of allocated memory"); } From 5580d375aa6bf3655dabc4a752dc7ec37aa06d9b Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Sat, 22 Feb 2020 15:34:48 -0600 Subject: [PATCH 3/4] add more diagnostics that will help in the identification of stl-string fields --- library/xml | 2 +- plugins/devel/check-structures-sanity.cpp | 92 ++++++++++++++++++++++- 2 files changed, 90 insertions(+), 4 deletions(-) diff --git a/library/xml b/library/xml index f00084673..70e3a8988 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit f00084673d02e6a170c8ba77da46e456855ba5fa +Subproject commit 70e3a89883bece6d126e14eab051624b706cf987 diff --git a/plugins/devel/check-structures-sanity.cpp b/plugins/devel/check-structures-sanity.cpp index 0fb49b010..98057f5f2 100644 --- a/plugins/devel/check-structures-sanity.cpp +++ b/plugins/devel/check-structures-sanity.cpp @@ -88,7 +88,11 @@ public: private: 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 *, size_t); bool check_vtable(const ToCheck &, void *, type_identity *); @@ -241,11 +245,11 @@ bool Checker::check() #define PTR_ADD(base, offset) (reinterpret_cast(reinterpret_cast((base)) + static_cast((offset)))) -bool Checker::address_in_runtime_data(void *ptr) +bool Checker::address_in_runtime_data(const void *ptr) { for (auto & range : mapped) { - if (!range.isInRange(ptr)) + if (!range.isInRange(const_cast(ptr))) { continue; } @@ -263,6 +267,74 @@ bool Checker::address_in_runtime_data(void *ptr) 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(&empty_string)) + { + return reinterpret_cast(base); + } +#endif + + const struct string_data_inner + { + size_t length; + size_t capacity; + int32_t refcount; + } *str_data = static_cast(*base) - 1; + + bool heap_allocated = address_in_runtime_data(*base); + if (heap_allocated) + { + uint32_t tag = *reinterpret_cast(PTR_ADD(str_data, -8)); + if (tag == 0xdfdf4ac8) + { + size_t allocated_size = *reinterpret_cast(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(*base); + for (size_t i = 0; i < str_data->length; i++) + { + if (!*ptr++) + { + return nullptr; + } + } + + if (*ptr++) + { + return nullptr; + } + + return reinterpret_cast(base); +} +#endif + + bool Checker::check_access(const ToCheck & item, void *base, type_identity *identity) { 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"); } +#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; From 1e3e3829a701a2563fde43006269dfc923434d17 Mon Sep 17 00:00:00 2001 From: Ben Lubar Date: Sat, 22 Feb 2020 23:36:14 -0600 Subject: [PATCH 4/4] update structures --- library/xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/xml b/library/xml index 70e3a8988..7048df4a2 160000 --- a/library/xml +++ b/library/xml @@ -1 +1 @@ -Subproject commit 70e3a89883bece6d126e14eab051624b706cf987 +Subproject commit 7048df4a22fe59cab941a41dd8c6d0f4830f77b3