diff --git a/src/coreclr/debug/dbgutil/dbgutil.cpp b/src/coreclr/debug/dbgutil/dbgutil.cpp index 93011b9d1fe54b..04f77f4d9ca23e 100644 --- a/src/coreclr/debug/dbgutil/dbgutil.cpp +++ b/src/coreclr/debug/dbgutil/dbgutil.cpp @@ -446,12 +446,21 @@ HRESULT ReadFromDataTarget(ICorDebugDataTarget* pDataTarget, extern "C" bool TryGetSymbol(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, const char* symbolName, uint64_t* symbolAddress) { + *symbolAddress = 0; + DWORD exportTableRva; if (FAILED(GetMachineAndDirectoryAddress(dataTarget, baseAddress, IMAGE_DIRECTORY_ENTRY_EXPORT, nullptr, &exportTableRva))) { return false; } + // A module with no export directory reports a zero RVA here; there is nothing to search and + // reading at baseAddress would parse the PE headers as an IMAGE_EXPORT_DIRECTORY. + if (exportTableRva == 0) + { + return false; + } + // Manually read the export directory from the target to find the requested symbol. IMAGE_EXPORT_DIRECTORY exportDir{}; if (FAILED(ReadFromDataTarget(dataTarget, baseAddress + exportTableRva, (BYTE*)&exportDir, sizeof(exportDir)))) @@ -460,6 +469,7 @@ TryGetSymbol(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, const char* } uint32_t namePointerCount = VAL32(exportDir.NumberOfNames); + uint32_t exportAddressCount = VAL32(exportDir.NumberOfFunctions); uint32_t addressTableRVA = VAL32(exportDir.AddressOfFunctions); uint32_t ordinalTableRVA = VAL32(exportDir.AddressOfNameOrdinals); uint32_t nameTableRVA = VAL32(exportDir.AddressOfNames); @@ -481,7 +491,7 @@ TryGetSymbol(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, const char* } // Allocate a buffer for the memory that we'll read out of the target image. // We can allocate as much space as the target symbol name as we gracefully handle reading - // inaccessable memory. + // inaccessible memory. std::unique_ptr namePointer(new char[symbolNameLength + 1]); // If we fail to read the memory or the name doesn't match, then we don't have the right export. if (SUCCEEDED(ReadFromDataTarget(dataTarget, baseAddress + namePointerRVA, (BYTE*)namePointer.get(), (UINT32)symbolNameLength + 1)) @@ -493,6 +503,12 @@ TryGetSymbol(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, const char* { return false; } + // The ordinal indexes the export address table; reject an out-of-range value from + // untrusted image data before using it to compute a read offset. + if (ordinalForNamedExport >= exportAddressCount) + { + return false; + } // If the name matches, we should be able to get the export uint32_t exportRVA = 0; if (FAILED(ReadFromDataTarget(dataTarget, baseAddress + addressTableRVA + sizeof(uint32_t) * ordinalForNamedExport, (BYTE*)&exportRVA, sizeof(exportRVA)))) @@ -505,7 +521,6 @@ TryGetSymbol(ICorDebugDataTarget* dataTarget, uint64_t baseAddress, const char* } } - *symbolAddress = 0; return false; } #endif