Skip to content

Commit 5d56bdd

Browse files
authored
Merge pull request #5706 from ab9rf/vtable-check
add vtable validation
2 parents 92bd364 + 4b5abe3 commit 5d56bdd

File tree

4 files changed

+23
-2
lines changed

4 files changed

+23
-2
lines changed

docs/changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ Template for new versions:
6161
## Fixes
6262

6363
## Misc Improvements
64+
- Core: DFHack now validates vtable pointers in objects read from memory and will throw an exception instead of crashing when an invalid vtable pointer is encountered. This makes it easier to identify which DF data structure contains corrupted data when this manifests in the form of a bad vtable pointer, and shifts blame for such crashes from DFHack to DF.
6465

6566
## Documentation
6667

library/DataDefs.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ const virtual_identity *virtual_identity::find(void *vtable)
370370

371371
// If using a reader/writer lock, re-grab as write here, and recheck
372372
Core &core = Core::getInstance();
373-
std::string name = core.p->doReadClassName(vtable);
373+
std::string name = core.p->readClassName(vtable);
374374

375375
auto name_it = (*name_lookup).find(name);
376376
if (name_it != (*name_lookup).end()) {

library/Process.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,9 @@ Process::~Process()
236236

237237
string Process::doReadClassName (void * vptr)
238238
{
239+
if (!checkValidAddress(vptr))
240+
throw std::runtime_error(fmt::format("invalid vtable ptr {}", vptr));
241+
239242
char* rtti = Process::readPtr(((char*)vptr - sizeof(void*)));
240243
#ifndef WIN32
241244
char* typestring = Process::readPtr(rtti + sizeof(void*));
@@ -591,6 +594,20 @@ void Process::getMemRanges(vector<t_memrange>& ranges)
591594
}
592595
#endif
593596

597+
bool Process::checkValidAddress(void* ptr)
598+
{
599+
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
600+
auto validate = [&] (t_memrange& r) {
601+
uintptr_t lo = reinterpret_cast<uintptr_t>(r.start);
602+
uintptr_t hi = reinterpret_cast<uintptr_t>(r.end);
603+
return addr >= lo && addr < hi;
604+
};
605+
std::vector<t_memrange> mr;
606+
getMemRanges(mr);
607+
bool valid = std::any_of(mr.begin(), mr.end(), validate);
608+
return valid;
609+
}
610+
594611
uintptr_t Process::getBase()
595612
{
596613
#if WIN32

library/include/MemAccess.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ namespace DFHack
221221

222222
std::string readClassName(void* vptr)
223223
{
224-
std::map<void*, std::string>::iterator it = classNameCache.find(vptr);
224+
auto it = classNameCache.find(vptr);
225225
if (it != classNameCache.end())
226226
return it->second;
227227
return classNameCache[vptr] = doReadClassName(vptr);
@@ -247,6 +247,9 @@ namespace DFHack
247247
/// get virtual memory ranges of the process (what is mapped where)
248248
static void getMemRanges(std::vector<t_memrange>& ranges);
249249

250+
/// check if an address has a mapping
251+
bool checkValidAddress(void* ptr);
252+
250253
/// get the symbol table extension of this process
251254
std::shared_ptr<DFHack::VersionInfo> getDescriptor()
252255
{

0 commit comments

Comments
 (0)