Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ Template for new versions:
## Fixes

## Misc Improvements
- 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.

## Documentation

Expand Down
2 changes: 1 addition & 1 deletion library/DataDefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ const virtual_identity *virtual_identity::find(void *vtable)

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

auto name_it = (*name_lookup).find(name);
if (name_it != (*name_lookup).end()) {
Expand Down
17 changes: 17 additions & 0 deletions library/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ Process::~Process()

string Process::doReadClassName (void * vptr)
{
if (!checkValidAddress(vptr))
throw std::runtime_error(fmt::format("invalid vtable ptr {}", vptr));

char* rtti = Process::readPtr(((char*)vptr - sizeof(void*)));
#ifndef WIN32
char* typestring = Process::readPtr(rtti + sizeof(void*));
Expand Down Expand Up @@ -591,6 +594,20 @@ void Process::getMemRanges(vector<t_memrange>& ranges)
}
#endif

bool Process::checkValidAddress(void* ptr)
{
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
auto validate = [&] (t_memrange& r) {
uintptr_t lo = reinterpret_cast<uintptr_t>(r.start);
uintptr_t hi = reinterpret_cast<uintptr_t>(r.end);
return addr >= lo && addr < hi;
};
std::vector<t_memrange> mr;
getMemRanges(mr);
bool valid = std::any_of(mr.begin(), mr.end(), validate);
return valid;
}

uintptr_t Process::getBase()
{
#if WIN32
Expand Down
5 changes: 4 additions & 1 deletion library/include/MemAccess.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ namespace DFHack

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

/// check if an address has a mapping
bool checkValidAddress(void* ptr);

/// get the symbol table extension of this process
std::shared_ptr<DFHack::VersionInfo> getDescriptor()
{
Expand Down