I have analyzed the most recurrent crashes together thanks to the DLL. The heap corruption is reported as a double free. I have identified that the last called functions in the stack belong to functions from the [JsonCpp](https://github.com/open-source-parsers/jsoncpp) library. I have also restored the Firefox part of the stack. The crash occurs in third-party code that gets called through `PR_GetAddrInfoByName`, they are probably hooking `getaddrinfo` from `ws2_32.dll` or something like this. The full call stack looks like this: ``` 00 000000cd`343ce370 00007ffb`951cf673 ntdll!RtlReportFatalFailure+0x9 01 000000cd`343ce3c0 00007ffb`951d83f2 ntdll!RtlReportCriticalFailure+0x97 02 000000cd`343ce4b0 00007ffb`951d86da ntdll!RtlpHeapHandleError+0x12 03 000000cd`343ce4e0 00007ffb`951de361 ntdll!RtlpHpHeapHandleError+0x7a 04 000000cd`343ce510 00007ffb`950f5bf0 ntdll!RtlpLogHeapFailure+0x45 05 000000cd`343ce540 00007ffb`950f47b1 ntdll!RtlpFreeHeapInternal+0x4e0 06 000000cd`343ce600 00007ffb`542850cc ntdll!RtlFreeHeap+0x51 wslbdhm64+0x350cc _free_base wslbdhm64+0x5eeb8 Json::Value::~Json::Value wslbdhm64+0x5ef84 Json::Value::"DestructiveMoveAssignment" wslbdhm64+0x657a6 DoStuff wslbdhm64+0x65e8b wslbdhm64+0x1de43 wslbdhm64+0x1d9d8 wslbdhm64+0x160db wslbdhm64+0x6829e wslbdhm64+0x64faf [ dynamic code ] 00 00000016`760dede0 00007ffb`49b4f32a nss3!PR_GetAddrInfoByName+0xc3 [/builds/worker/checkouts/gecko/nsprpub/pr/src/misc/prnetdb.c @ 2171] 01 (Inline Function) --------`-------- xul!mozilla::net::_GetAddrInfo_Portable+0x5f [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 241] 02 00000016`760dee60 00007ffb`4a2a284b xul!mozilla::net::GetAddrInfo+0x15a [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 374] 03 00000016`760df0a0 00007ffb`4a1e4471 xul!nsHostResolver::ThreadFunc+0x43b [/builds/worker/checkouts/gecko/netwerk/dns/nsHostResolver.cpp @ 1727] 04 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::applyImpl+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1147] 05 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::apply+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1153] 06 00000016`760df200 00007ffb`4a211f3e xul!mozilla::detail::RunnableMethodImpl<RefPtr<nsObserverService>,void (nsObserverService::*)(),1,0>::Run+0x21 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1203] 07 00000016`760df230 00007ffb`4b1c1aa8 xul!nsThreadPool::Run+0x29e [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp @ 311] 08 (Inline Function) --------`-------- xul!nsThread::ProcessNextEvent+0x17b0 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 1198] 09 00000016`760df3d0 00007ffb`4b1ad48f xul!NS_ProcessNextEvent+0x1808 [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp @ 466] 0a 00000016`760df760 00007ffb`4a3aba5f xul!mozilla::ipc::MessagePumpForNonMainThreads::Run+0xcf [/builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp @ 301] 0b (Inline Function) --------`-------- xul!MessageLoop::RunInternal+0x16 [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 381] 0c 00000016`760df800 00007ffb`499ef54e xul!MessageLoop::RunHandler+0x2f [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 375] 0d 00000016`760df850 00007ffb`4a20d757 xul!MessageLoop::Run+0x4e [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 357] 0e 00000016`760df8b0 00007ffb`63d244bc xul!nsThread::ThreadFunc+0xd7 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 385] 0f 00000016`760dfa60 00007ffb`63d39241 nss3!_PR_NativeRunThread+0x13c [/builds/worker/checkouts/gecko/nsprpub/pr/src/threads/combined/pruthr.c @ 421] 10 00000016`760dfae0 00007ffb`c2701bb2 nss3!pr_root+0x11 [/builds/worker/checkouts/gecko/nsprpub/pr/src/md/windows/w95thred.c @ 140] 11 00000016`760dfb10 00007ffb`c3657614 ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x42 12 00000016`760dfb40 00007ffb`9782e8a8 kernel32!BaseThreadInitThunk+0x14 13 (Inline Function) --------`-------- mozglue!mozilla::interceptor::FuncHook<mozilla::interceptor::WindowsDllInterceptor<mozilla::interceptor::VMSharingPolicyShared>,void (*)(int, void *, void *)>::operator()+0x15 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/nsWindowsDllInterceptor.h @ 150] 14 00000016`760dfb70 00007ffb`c4b426a1 mozglue!patched_BaseThreadInitThunk+0x28 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/WindowsDllBlocklist.cpp @ 587] 15 00000016`760dfbe0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 ``` I believe the code is doing a move assignment to a global or static variable (that's what I called `DestructiveMoveAssignment` in the call stack), like the assignment in the `ThreadFunc` below: ``` #include "json/json.h" #include <windows.h> constexpr auto nThreads = 2; constexpr auto nIterations = 0x1000; Json::Value gValue; DWORD ThreadFunc(void* aParam) { for (size_t i = 0; i < 0x1000; ++i) { gValue[Json::String("")] = Json::Value("hello"); } return 0; } int main() { auto bResult = HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (!bResult) { return 1; } HANDLE threads[nThreads]{}; for (auto& thread : threads) { DWORD threadId = 0; thread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, &threadId); if (!thread) { return 1; } } WaitForMultipleObjects(nThreads, threads, TRUE, INFINITE); return 0; } ``` Such a move assignment is racy, as the example program illustrates; the example program forces the race condition and crashes with a double free heap corruption: ``` 00 00000038`b59ff3e0 00007fff`5989c213 ntdll!RtlReportFatalFailure+0x9 01 00000038`b59ff430 00007fff`598a52aa ntdll!RtlReportCriticalFailure+0x97 02 00000038`b59ff520 00007fff`598a558a ntdll!RtlpHeapHandleError+0x12 03 00000038`b59ff550 00007fff`598b1585 ntdll!RtlpHpHeapHandleError+0x7a 04 00000038`b59ff580 00007fff`597cc59c ntdll!RtlpLogHeapFailure+0x45 05 00000038`b59ff5b0 00007fff`597cb1e1 ntdll!RtlpFreeHeapInternal+0x84c 06 00000038`b59ff670 00007fff`56c637eb ntdll!RtlFreeHeap+0x51 07 00000038`b59ff6b0 00007ff7`49f34604 ucrtbase!_free_base+0x1b 08 00000038`b59ff6e0 00007ff7`49f354be MyJson!Json::releasePrefixedStringValue+0x14 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 182] 09 00000038`b59ff710 00007ff7`49f350d3 MyJson!Json::Value::releasePayload+0x4e [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 1018] 0a 00000038`b59ff760 00007ff7`49f315e1 MyJson!Json::Value::~Value+0x13 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 443] 0b 00000038`b59ff790 00007fff`58f726bd MyJson!ThreadFunc+0xb1 [C:\repos\MyJson\MyJson\MyJson.cpp @ 13] 0c 00000038`b59ff840 00007fff`597edfb8 KERNEL32!BaseThreadInitThunk+0x1d 0d 00000038`b59ff870 00000000`00000000 ntdll!RtlUserThreadStart+0x28 ``` I will try to contact the vendor to confirm if they do have code that looks like this and if they could add a mutex around it. Can anyone confirm that `PR_GetAddrInfoByName` can indeed get called by different threads? Thanks! (I haven't analyzed the other crashes for the moment.)
Bug 1758641 Comment 5 Edit History
Note: The actual edited comment in the bug view page will always show the original commenter’s name and original timestamp.
I have analyzed the most recurrent crashes together thanks to the DLL. The heap corruption is reported as a double free. I have identified that the last called functions in the stack belong to the statically-linked [JsonCpp](https://github.com/open-source-parsers/jsoncpp) library. I have also restored the Firefox part of the stack. The crash occurs in third-party code that gets called through `PR_GetAddrInfoByName`, they are probably hooking `getaddrinfo` from `ws2_32.dll` or something like this. The full call stack looks like this: ``` 00 000000cd`343ce370 00007ffb`951cf673 ntdll!RtlReportFatalFailure+0x9 01 000000cd`343ce3c0 00007ffb`951d83f2 ntdll!RtlReportCriticalFailure+0x97 02 000000cd`343ce4b0 00007ffb`951d86da ntdll!RtlpHeapHandleError+0x12 03 000000cd`343ce4e0 00007ffb`951de361 ntdll!RtlpHpHeapHandleError+0x7a 04 000000cd`343ce510 00007ffb`950f5bf0 ntdll!RtlpLogHeapFailure+0x45 05 000000cd`343ce540 00007ffb`950f47b1 ntdll!RtlpFreeHeapInternal+0x4e0 06 000000cd`343ce600 00007ffb`542850cc ntdll!RtlFreeHeap+0x51 wslbdhm64+0x350cc _free_base wslbdhm64+0x5eeb8 Json::Value::~Json::Value wslbdhm64+0x5ef84 Json::Value::"DestructiveMoveAssignment" wslbdhm64+0x657a6 DoStuff wslbdhm64+0x65e8b wslbdhm64+0x1de43 wslbdhm64+0x1d9d8 wslbdhm64+0x160db wslbdhm64+0x6829e wslbdhm64+0x64faf [ dynamic code ] 00 00000016`760dede0 00007ffb`49b4f32a nss3!PR_GetAddrInfoByName+0xc3 [/builds/worker/checkouts/gecko/nsprpub/pr/src/misc/prnetdb.c @ 2171] 01 (Inline Function) --------`-------- xul!mozilla::net::_GetAddrInfo_Portable+0x5f [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 241] 02 00000016`760dee60 00007ffb`4a2a284b xul!mozilla::net::GetAddrInfo+0x15a [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 374] 03 00000016`760df0a0 00007ffb`4a1e4471 xul!nsHostResolver::ThreadFunc+0x43b [/builds/worker/checkouts/gecko/netwerk/dns/nsHostResolver.cpp @ 1727] 04 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::applyImpl+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1147] 05 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::apply+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1153] 06 00000016`760df200 00007ffb`4a211f3e xul!mozilla::detail::RunnableMethodImpl<RefPtr<nsObserverService>,void (nsObserverService::*)(),1,0>::Run+0x21 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1203] 07 00000016`760df230 00007ffb`4b1c1aa8 xul!nsThreadPool::Run+0x29e [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp @ 311] 08 (Inline Function) --------`-------- xul!nsThread::ProcessNextEvent+0x17b0 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 1198] 09 00000016`760df3d0 00007ffb`4b1ad48f xul!NS_ProcessNextEvent+0x1808 [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp @ 466] 0a 00000016`760df760 00007ffb`4a3aba5f xul!mozilla::ipc::MessagePumpForNonMainThreads::Run+0xcf [/builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp @ 301] 0b (Inline Function) --------`-------- xul!MessageLoop::RunInternal+0x16 [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 381] 0c 00000016`760df800 00007ffb`499ef54e xul!MessageLoop::RunHandler+0x2f [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 375] 0d 00000016`760df850 00007ffb`4a20d757 xul!MessageLoop::Run+0x4e [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 357] 0e 00000016`760df8b0 00007ffb`63d244bc xul!nsThread::ThreadFunc+0xd7 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 385] 0f 00000016`760dfa60 00007ffb`63d39241 nss3!_PR_NativeRunThread+0x13c [/builds/worker/checkouts/gecko/nsprpub/pr/src/threads/combined/pruthr.c @ 421] 10 00000016`760dfae0 00007ffb`c2701bb2 nss3!pr_root+0x11 [/builds/worker/checkouts/gecko/nsprpub/pr/src/md/windows/w95thred.c @ 140] 11 00000016`760dfb10 00007ffb`c3657614 ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x42 12 00000016`760dfb40 00007ffb`9782e8a8 kernel32!BaseThreadInitThunk+0x14 13 (Inline Function) --------`-------- mozglue!mozilla::interceptor::FuncHook<mozilla::interceptor::WindowsDllInterceptor<mozilla::interceptor::VMSharingPolicyShared>,void (*)(int, void *, void *)>::operator()+0x15 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/nsWindowsDllInterceptor.h @ 150] 14 00000016`760dfb70 00007ffb`c4b426a1 mozglue!patched_BaseThreadInitThunk+0x28 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/WindowsDllBlocklist.cpp @ 587] 15 00000016`760dfbe0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 ``` I believe the code is doing a move assignment to a global or static variable (that's what I called `DestructiveMoveAssignment` in the call stack), like the assignment in the `ThreadFunc` below: ```c++ #include "json/json.h" #include <windows.h> constexpr auto nThreads = 2; constexpr auto nIterations = 0x1000; Json::Value gValue; DWORD ThreadFunc(void* aParam) { for (size_t i = 0; i < 0x1000; ++i) { gValue[Json::String("")] = Json::Value("hello"); } return 0; } int main() { auto bResult = HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (!bResult) { return 1; } HANDLE threads[nThreads]{}; for (auto& thread : threads) { DWORD threadId = 0; thread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, &threadId); if (!thread) { return 1; } } WaitForMultipleObjects(nThreads, threads, TRUE, INFINITE); return 0; } ``` Such a move assignment is racy, as the example program illustrates; the example program forces the race condition and crashes with a double free heap corruption: ``` 00 00000038`b59ff3e0 00007fff`5989c213 ntdll!RtlReportFatalFailure+0x9 01 00000038`b59ff430 00007fff`598a52aa ntdll!RtlReportCriticalFailure+0x97 02 00000038`b59ff520 00007fff`598a558a ntdll!RtlpHeapHandleError+0x12 03 00000038`b59ff550 00007fff`598b1585 ntdll!RtlpHpHeapHandleError+0x7a 04 00000038`b59ff580 00007fff`597cc59c ntdll!RtlpLogHeapFailure+0x45 05 00000038`b59ff5b0 00007fff`597cb1e1 ntdll!RtlpFreeHeapInternal+0x84c 06 00000038`b59ff670 00007fff`56c637eb ntdll!RtlFreeHeap+0x51 07 00000038`b59ff6b0 00007ff7`49f34604 ucrtbase!_free_base+0x1b 08 00000038`b59ff6e0 00007ff7`49f354be MyJson!Json::releasePrefixedStringValue+0x14 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 182] 09 00000038`b59ff710 00007ff7`49f350d3 MyJson!Json::Value::releasePayload+0x4e [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 1018] 0a 00000038`b59ff760 00007ff7`49f315e1 MyJson!Json::Value::~Value+0x13 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 443] 0b 00000038`b59ff790 00007fff`58f726bd MyJson!ThreadFunc+0xb1 [C:\repos\MyJson\MyJson\MyJson.cpp @ 13] 0c 00000038`b59ff840 00007fff`597edfb8 KERNEL32!BaseThreadInitThunk+0x1d 0d 00000038`b59ff870 00000000`00000000 ntdll!RtlUserThreadStart+0x28 ``` I will try to contact the vendor to confirm if they do have code that looks like this and if they could add a mutex around it. Can anyone confirm that `PR_GetAddrInfoByName` can indeed get called by different threads? Thanks! (I haven't analyzed the other crashes for the moment.)
I have analyzed the most recurrent crashes together thanks to the DLL. The heap corruption is reported as a double free. I have identified that the last called functions in the stack belong to the statically-linked [JsonCpp](https://github.com/open-source-parsers/jsoncpp) library. I have also restored the Firefox part of the stack. The crash occurs in third-party code that gets called through `PR_GetAddrInfoByName`, they are probably hooking `getaddrinfo` from `ws2_32.dll` or something like this. The full call stack looks like this: ``` 00 000000cd`343ce370 00007ffb`951cf673 ntdll!RtlReportFatalFailure+0x9 01 000000cd`343ce3c0 00007ffb`951d83f2 ntdll!RtlReportCriticalFailure+0x97 02 000000cd`343ce4b0 00007ffb`951d86da ntdll!RtlpHeapHandleError+0x12 03 000000cd`343ce4e0 00007ffb`951de361 ntdll!RtlpHpHeapHandleError+0x7a 04 000000cd`343ce510 00007ffb`950f5bf0 ntdll!RtlpLogHeapFailure+0x45 05 000000cd`343ce540 00007ffb`950f47b1 ntdll!RtlpFreeHeapInternal+0x4e0 06 000000cd`343ce600 00007ffb`542850cc ntdll!RtlFreeHeap+0x51 wslbdhm64+0x350cc _free_base wslbdhm64+0x5eeb8 Json::Value::~Json::Value wslbdhm64+0x5ef84 Json::Value::"DestructiveMoveAssignment" wslbdhm64+0x657a6 DoStuff wslbdhm64+0x65e8b wslbdhm64+0x1de43 wslbdhm64+0x1d9d8 wslbdhm64+0x160db wslbdhm64+0x6829e wslbdhm64+0x64faf [ dynamic code ] 00 00000016`760dede0 00007ffb`49b4f32a nss3!PR_GetAddrInfoByName+0xc3 [/builds/worker/checkouts/gecko/nsprpub/pr/src/misc/prnetdb.c @ 2171] 01 (Inline Function) --------`-------- xul!mozilla::net::_GetAddrInfo_Portable+0x5f [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 241] 02 00000016`760dee60 00007ffb`4a2a284b xul!mozilla::net::GetAddrInfo+0x15a [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 374] 03 00000016`760df0a0 00007ffb`4a1e4471 xul!nsHostResolver::ThreadFunc+0x43b [/builds/worker/checkouts/gecko/netwerk/dns/nsHostResolver.cpp @ 1727] 04 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::applyImpl+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1147] 05 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::apply+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1153] 06 00000016`760df200 00007ffb`4a211f3e xul!mozilla::detail::RunnableMethodImpl<RefPtr<nsObserverService>,void (nsObserverService::*)(),1,0>::Run+0x21 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1203] 07 00000016`760df230 00007ffb`4b1c1aa8 xul!nsThreadPool::Run+0x29e [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp @ 311] 08 (Inline Function) --------`-------- xul!nsThread::ProcessNextEvent+0x17b0 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 1198] 09 00000016`760df3d0 00007ffb`4b1ad48f xul!NS_ProcessNextEvent+0x1808 [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp @ 466] 0a 00000016`760df760 00007ffb`4a3aba5f xul!mozilla::ipc::MessagePumpForNonMainThreads::Run+0xcf [/builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp @ 301] 0b (Inline Function) --------`-------- xul!MessageLoop::RunInternal+0x16 [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 381] 0c 00000016`760df800 00007ffb`499ef54e xul!MessageLoop::RunHandler+0x2f [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 375] 0d 00000016`760df850 00007ffb`4a20d757 xul!MessageLoop::Run+0x4e [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 357] 0e 00000016`760df8b0 00007ffb`63d244bc xul!nsThread::ThreadFunc+0xd7 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 385] 0f 00000016`760dfa60 00007ffb`63d39241 nss3!_PR_NativeRunThread+0x13c [/builds/worker/checkouts/gecko/nsprpub/pr/src/threads/combined/pruthr.c @ 421] 10 00000016`760dfae0 00007ffb`c2701bb2 nss3!pr_root+0x11 [/builds/worker/checkouts/gecko/nsprpub/pr/src/md/windows/w95thred.c @ 140] 11 00000016`760dfb10 00007ffb`c3657614 ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x42 12 00000016`760dfb40 00007ffb`9782e8a8 kernel32!BaseThreadInitThunk+0x14 13 (Inline Function) --------`-------- mozglue!mozilla::interceptor::FuncHook<mozilla::interceptor::WindowsDllInterceptor<mozilla::interceptor::VMSharingPolicyShared>,void (*)(int, void *, void *)>::operator()+0x15 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/nsWindowsDllInterceptor.h @ 150] 14 00000016`760dfb70 00007ffb`c4b426a1 mozglue!patched_BaseThreadInitThunk+0x28 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/WindowsDllBlocklist.cpp @ 587] 15 00000016`760dfbe0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 ``` From analyzing the code in the DLL, I believe the code is doing a move assignment to a global or static variable (that's what I called `DestructiveMoveAssignment` in the call stack), like the assignment in the `ThreadFunc` below: ```c++ #include "json/json.h" #include <windows.h> constexpr auto nThreads = 2; constexpr auto nIterations = 0x1000; Json::Value gValue; DWORD ThreadFunc(void* aParam) { for (size_t i = 0; i < 0x1000; ++i) { gValue[Json::String("")] = Json::Value("hello"); } return 0; } int main() { auto bResult = HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (!bResult) { return 1; } HANDLE threads[nThreads]{}; for (auto& thread : threads) { DWORD threadId = 0; thread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, &threadId); if (!thread) { return 1; } } WaitForMultipleObjects(nThreads, threads, TRUE, INFINITE); return 0; } ``` Such a move assignment is racy, as the example program illustrates; the example program forces the race condition and crashes with a double free heap corruption: ``` 00 00000038`b59ff3e0 00007fff`5989c213 ntdll!RtlReportFatalFailure+0x9 01 00000038`b59ff430 00007fff`598a52aa ntdll!RtlReportCriticalFailure+0x97 02 00000038`b59ff520 00007fff`598a558a ntdll!RtlpHeapHandleError+0x12 03 00000038`b59ff550 00007fff`598b1585 ntdll!RtlpHpHeapHandleError+0x7a 04 00000038`b59ff580 00007fff`597cc59c ntdll!RtlpLogHeapFailure+0x45 05 00000038`b59ff5b0 00007fff`597cb1e1 ntdll!RtlpFreeHeapInternal+0x84c 06 00000038`b59ff670 00007fff`56c637eb ntdll!RtlFreeHeap+0x51 07 00000038`b59ff6b0 00007ff7`49f34604 ucrtbase!_free_base+0x1b 08 00000038`b59ff6e0 00007ff7`49f354be MyJson!Json::releasePrefixedStringValue+0x14 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 182] 09 00000038`b59ff710 00007ff7`49f350d3 MyJson!Json::Value::releasePayload+0x4e [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 1018] 0a 00000038`b59ff760 00007ff7`49f315e1 MyJson!Json::Value::~Value+0x13 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 443] 0b 00000038`b59ff790 00007fff`58f726bd MyJson!ThreadFunc+0xb1 [C:\repos\MyJson\MyJson\MyJson.cpp @ 13] 0c 00000038`b59ff840 00007fff`597edfb8 KERNEL32!BaseThreadInitThunk+0x1d 0d 00000038`b59ff870 00000000`00000000 ntdll!RtlUserThreadStart+0x28 ``` I will try to contact the vendor to confirm if they do have code that looks like this and if they could add a mutex around it. Can anyone confirm that `PR_GetAddrInfoByName` can indeed get called by different threads? Thanks! (I haven't analyzed the other crashes for the moment.)
I have analyzed the most recurrent crashes together thanks to the DLL. The heap corruption is reported as a double free. I have identified that the last called functions in the stack belong to the statically-linked [JsonCpp](https://github.com/open-source-parsers/jsoncpp) library. I have also restored the Firefox part of the stack. The crash occurs in third-party code that gets called through `PR_GetAddrInfoByName`, they are probably hooking `getaddrinfo` from `ws2_32.dll` or something like this. The full call stack looks like this: ``` 00 000000cd`343ce370 00007ffb`951cf673 ntdll!RtlReportFatalFailure+0x9 01 000000cd`343ce3c0 00007ffb`951d83f2 ntdll!RtlReportCriticalFailure+0x97 02 000000cd`343ce4b0 00007ffb`951d86da ntdll!RtlpHeapHandleError+0x12 03 000000cd`343ce4e0 00007ffb`951de361 ntdll!RtlpHpHeapHandleError+0x7a 04 000000cd`343ce510 00007ffb`950f5bf0 ntdll!RtlpLogHeapFailure+0x45 05 000000cd`343ce540 00007ffb`950f47b1 ntdll!RtlpFreeHeapInternal+0x4e0 06 000000cd`343ce600 00007ffb`542850cc ntdll!RtlFreeHeap+0x51 wslbdhm64+0x350cc _free_base wslbdhm64+0x5eeb8 Json::Value::~Json::Value wslbdhm64+0x5ef84 Json::Value::"DestructiveMoveAssignment" wslbdhm64+0x657a6 DoStuff wslbdhm64+0x65e8b wslbdhm64+0x1de43 wslbdhm64+0x1d9d8 wslbdhm64+0x160db wslbdhm64+0x6829e wslbdhm64+0x64faf [ dynamic code ] 00 00000016`760dede0 00007ffb`49b4f32a nss3!PR_GetAddrInfoByName+0xc3 [/builds/worker/checkouts/gecko/nsprpub/pr/src/misc/prnetdb.c @ 2171] 01 (Inline Function) --------`-------- xul!mozilla::net::_GetAddrInfo_Portable+0x5f [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 241] 02 00000016`760dee60 00007ffb`4a2a284b xul!mozilla::net::GetAddrInfo+0x15a [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 374] 03 00000016`760df0a0 00007ffb`4a1e4471 xul!nsHostResolver::ThreadFunc+0x43b [/builds/worker/checkouts/gecko/netwerk/dns/nsHostResolver.cpp @ 1727] 04 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::applyImpl+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1147] 05 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::apply+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1153] 06 00000016`760df200 00007ffb`4a211f3e xul!mozilla::detail::RunnableMethodImpl<RefPtr<nsObserverService>,void (nsObserverService::*)(),1,0>::Run+0x21 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1203] 07 00000016`760df230 00007ffb`4b1c1aa8 xul!nsThreadPool::Run+0x29e [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp @ 311] 08 (Inline Function) --------`-------- xul!nsThread::ProcessNextEvent+0x17b0 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 1198] 09 00000016`760df3d0 00007ffb`4b1ad48f xul!NS_ProcessNextEvent+0x1808 [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp @ 466] 0a 00000016`760df760 00007ffb`4a3aba5f xul!mozilla::ipc::MessagePumpForNonMainThreads::Run+0xcf [/builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp @ 301] 0b (Inline Function) --------`-------- xul!MessageLoop::RunInternal+0x16 [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 381] 0c 00000016`760df800 00007ffb`499ef54e xul!MessageLoop::RunHandler+0x2f [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 375] 0d 00000016`760df850 00007ffb`4a20d757 xul!MessageLoop::Run+0x4e [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 357] 0e 00000016`760df8b0 00007ffb`63d244bc xul!nsThread::ThreadFunc+0xd7 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 385] 0f 00000016`760dfa60 00007ffb`63d39241 nss3!_PR_NativeRunThread+0x13c [/builds/worker/checkouts/gecko/nsprpub/pr/src/threads/combined/pruthr.c @ 421] 10 00000016`760dfae0 00007ffb`c2701bb2 nss3!pr_root+0x11 [/builds/worker/checkouts/gecko/nsprpub/pr/src/md/windows/w95thred.c @ 140] 11 00000016`760dfb10 00007ffb`c3657614 ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x42 12 00000016`760dfb40 00007ffb`9782e8a8 kernel32!BaseThreadInitThunk+0x14 13 (Inline Function) --------`-------- mozglue!mozilla::interceptor::FuncHook<mozilla::interceptor::WindowsDllInterceptor<mozilla::interceptor::VMSharingPolicyShared>,void (*)(int, void *, void *)>::operator()+0x15 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/nsWindowsDllInterceptor.h @ 150] 14 00000016`760dfb70 00007ffb`c4b426a1 mozglue!patched_BaseThreadInitThunk+0x28 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/WindowsDllBlocklist.cpp @ 587] 15 00000016`760dfbe0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 ``` From analyzing the code in the DLL, I believe the code is doing a move assignment to a global or static variable (that's what I called `DestructiveMoveAssignment` in the call stack), like the assignment in the `ThreadFunc` below: ```c++ #include "json/json.h" #include <windows.h> constexpr auto nThreads = 2; constexpr auto nIterations = 0x1000; Json::Value gValue; DWORD ThreadFunc(void* aParam) { for (size_t i = 0; i < nIterations; ++i) { gValue[Json::String("")] = Json::Value("hello"); } return 0; } int main() { auto bResult = HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (!bResult) { return 1; } HANDLE threads[nThreads]{}; for (auto& thread : threads) { DWORD threadId = 0; thread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, &threadId); if (!thread) { return 1; } } WaitForMultipleObjects(nThreads, threads, TRUE, INFINITE); return 0; } ``` Such a move assignment is racy, as the example program illustrates; the example program forces the race condition and crashes with a double free heap corruption: ``` 00 00000038`b59ff3e0 00007fff`5989c213 ntdll!RtlReportFatalFailure+0x9 01 00000038`b59ff430 00007fff`598a52aa ntdll!RtlReportCriticalFailure+0x97 02 00000038`b59ff520 00007fff`598a558a ntdll!RtlpHeapHandleError+0x12 03 00000038`b59ff550 00007fff`598b1585 ntdll!RtlpHpHeapHandleError+0x7a 04 00000038`b59ff580 00007fff`597cc59c ntdll!RtlpLogHeapFailure+0x45 05 00000038`b59ff5b0 00007fff`597cb1e1 ntdll!RtlpFreeHeapInternal+0x84c 06 00000038`b59ff670 00007fff`56c637eb ntdll!RtlFreeHeap+0x51 07 00000038`b59ff6b0 00007ff7`49f34604 ucrtbase!_free_base+0x1b 08 00000038`b59ff6e0 00007ff7`49f354be MyJson!Json::releasePrefixedStringValue+0x14 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 182] 09 00000038`b59ff710 00007ff7`49f350d3 MyJson!Json::Value::releasePayload+0x4e [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 1018] 0a 00000038`b59ff760 00007ff7`49f315e1 MyJson!Json::Value::~Value+0x13 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 443] 0b 00000038`b59ff790 00007fff`58f726bd MyJson!ThreadFunc+0xb1 [C:\repos\MyJson\MyJson\MyJson.cpp @ 13] 0c 00000038`b59ff840 00007fff`597edfb8 KERNEL32!BaseThreadInitThunk+0x1d 0d 00000038`b59ff870 00000000`00000000 ntdll!RtlUserThreadStart+0x28 ``` I will try to contact the vendor to confirm if they do have code that looks like this and if they could add a mutex around it. Can anyone confirm that `PR_GetAddrInfoByName` can indeed get called by different threads? Thanks! (I haven't analyzed the other crashes for the moment.)
I have analyzed the most recurrent crashes thanks to the DLL. The heap corruption is reported as a double free. I have identified that the last called functions in the stack belong to the statically-linked [JsonCpp](https://github.com/open-source-parsers/jsoncpp) library. I have also restored the Firefox part of the stack. The crash occurs in third-party code that gets called through `PR_GetAddrInfoByName`, they are probably hooking `getaddrinfo` from `ws2_32.dll` or something like this. The full call stack looks like this: ``` 00 000000cd`343ce370 00007ffb`951cf673 ntdll!RtlReportFatalFailure+0x9 01 000000cd`343ce3c0 00007ffb`951d83f2 ntdll!RtlReportCriticalFailure+0x97 02 000000cd`343ce4b0 00007ffb`951d86da ntdll!RtlpHeapHandleError+0x12 03 000000cd`343ce4e0 00007ffb`951de361 ntdll!RtlpHpHeapHandleError+0x7a 04 000000cd`343ce510 00007ffb`950f5bf0 ntdll!RtlpLogHeapFailure+0x45 05 000000cd`343ce540 00007ffb`950f47b1 ntdll!RtlpFreeHeapInternal+0x4e0 06 000000cd`343ce600 00007ffb`542850cc ntdll!RtlFreeHeap+0x51 wslbdhm64+0x350cc _free_base wslbdhm64+0x5eeb8 Json::Value::~Json::Value wslbdhm64+0x5ef84 Json::Value::"DestructiveMoveAssignment" wslbdhm64+0x657a6 DoStuff wslbdhm64+0x65e8b wslbdhm64+0x1de43 wslbdhm64+0x1d9d8 wslbdhm64+0x160db wslbdhm64+0x6829e wslbdhm64+0x64faf [ dynamic code ] 00 00000016`760dede0 00007ffb`49b4f32a nss3!PR_GetAddrInfoByName+0xc3 [/builds/worker/checkouts/gecko/nsprpub/pr/src/misc/prnetdb.c @ 2171] 01 (Inline Function) --------`-------- xul!mozilla::net::_GetAddrInfo_Portable+0x5f [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 241] 02 00000016`760dee60 00007ffb`4a2a284b xul!mozilla::net::GetAddrInfo+0x15a [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 374] 03 00000016`760df0a0 00007ffb`4a1e4471 xul!nsHostResolver::ThreadFunc+0x43b [/builds/worker/checkouts/gecko/netwerk/dns/nsHostResolver.cpp @ 1727] 04 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::applyImpl+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1147] 05 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::apply+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1153] 06 00000016`760df200 00007ffb`4a211f3e xul!mozilla::detail::RunnableMethodImpl<RefPtr<nsObserverService>,void (nsObserverService::*)(),1,0>::Run+0x21 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1203] 07 00000016`760df230 00007ffb`4b1c1aa8 xul!nsThreadPool::Run+0x29e [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp @ 311] 08 (Inline Function) --------`-------- xul!nsThread::ProcessNextEvent+0x17b0 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 1198] 09 00000016`760df3d0 00007ffb`4b1ad48f xul!NS_ProcessNextEvent+0x1808 [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp @ 466] 0a 00000016`760df760 00007ffb`4a3aba5f xul!mozilla::ipc::MessagePumpForNonMainThreads::Run+0xcf [/builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp @ 301] 0b (Inline Function) --------`-------- xul!MessageLoop::RunInternal+0x16 [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 381] 0c 00000016`760df800 00007ffb`499ef54e xul!MessageLoop::RunHandler+0x2f [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 375] 0d 00000016`760df850 00007ffb`4a20d757 xul!MessageLoop::Run+0x4e [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 357] 0e 00000016`760df8b0 00007ffb`63d244bc xul!nsThread::ThreadFunc+0xd7 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 385] 0f 00000016`760dfa60 00007ffb`63d39241 nss3!_PR_NativeRunThread+0x13c [/builds/worker/checkouts/gecko/nsprpub/pr/src/threads/combined/pruthr.c @ 421] 10 00000016`760dfae0 00007ffb`c2701bb2 nss3!pr_root+0x11 [/builds/worker/checkouts/gecko/nsprpub/pr/src/md/windows/w95thred.c @ 140] 11 00000016`760dfb10 00007ffb`c3657614 ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x42 12 00000016`760dfb40 00007ffb`9782e8a8 kernel32!BaseThreadInitThunk+0x14 13 (Inline Function) --------`-------- mozglue!mozilla::interceptor::FuncHook<mozilla::interceptor::WindowsDllInterceptor<mozilla::interceptor::VMSharingPolicyShared>,void (*)(int, void *, void *)>::operator()+0x15 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/nsWindowsDllInterceptor.h @ 150] 14 00000016`760dfb70 00007ffb`c4b426a1 mozglue!patched_BaseThreadInitThunk+0x28 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/WindowsDllBlocklist.cpp @ 587] 15 00000016`760dfbe0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 ``` From analyzing the code in the DLL, I believe the code is doing a move assignment to a global or static variable (that's what I called `DestructiveMoveAssignment` in the call stack), like the assignment in the `ThreadFunc` below: ```c++ #include "json/json.h" #include <windows.h> constexpr auto nThreads = 2; constexpr auto nIterations = 0x1000; Json::Value gValue; DWORD ThreadFunc(void* aParam) { for (size_t i = 0; i < nIterations; ++i) { gValue[Json::String("")] = Json::Value("hello"); } return 0; } int main() { auto bResult = HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (!bResult) { return 1; } HANDLE threads[nThreads]{}; for (auto& thread : threads) { DWORD threadId = 0; thread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, &threadId); if (!thread) { return 1; } } WaitForMultipleObjects(nThreads, threads, TRUE, INFINITE); return 0; } ``` Such a move assignment is racy, as the example program illustrates; the example program forces the race condition and crashes with a double free heap corruption: ``` 00 00000038`b59ff3e0 00007fff`5989c213 ntdll!RtlReportFatalFailure+0x9 01 00000038`b59ff430 00007fff`598a52aa ntdll!RtlReportCriticalFailure+0x97 02 00000038`b59ff520 00007fff`598a558a ntdll!RtlpHeapHandleError+0x12 03 00000038`b59ff550 00007fff`598b1585 ntdll!RtlpHpHeapHandleError+0x7a 04 00000038`b59ff580 00007fff`597cc59c ntdll!RtlpLogHeapFailure+0x45 05 00000038`b59ff5b0 00007fff`597cb1e1 ntdll!RtlpFreeHeapInternal+0x84c 06 00000038`b59ff670 00007fff`56c637eb ntdll!RtlFreeHeap+0x51 07 00000038`b59ff6b0 00007ff7`49f34604 ucrtbase!_free_base+0x1b 08 00000038`b59ff6e0 00007ff7`49f354be MyJson!Json::releasePrefixedStringValue+0x14 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 182] 09 00000038`b59ff710 00007ff7`49f350d3 MyJson!Json::Value::releasePayload+0x4e [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 1018] 0a 00000038`b59ff760 00007ff7`49f315e1 MyJson!Json::Value::~Value+0x13 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 443] 0b 00000038`b59ff790 00007fff`58f726bd MyJson!ThreadFunc+0xb1 [C:\repos\MyJson\MyJson\MyJson.cpp @ 13] 0c 00000038`b59ff840 00007fff`597edfb8 KERNEL32!BaseThreadInitThunk+0x1d 0d 00000038`b59ff870 00000000`00000000 ntdll!RtlUserThreadStart+0x28 ``` I will try to contact the vendor to confirm if they do have code that looks like this and if they could add a mutex around it. Can anyone confirm that `PR_GetAddrInfoByName` can indeed get called by different threads? Thanks! (I haven't analyzed the other crashes for the moment.)
I have analyzed the most recurrent crash thanks to the DLL. The heap corruption is reported as a double free. I have identified that the last called functions in the stack belong to the statically-linked [JsonCpp](https://github.com/open-source-parsers/jsoncpp) library. I have also restored the Firefox part of the stack. The crash occurs in third-party code that gets called through `PR_GetAddrInfoByName`, they are probably hooking `getaddrinfo` from `ws2_32.dll` or something like this. The full call stack looks like this: ``` 00 000000cd`343ce370 00007ffb`951cf673 ntdll!RtlReportFatalFailure+0x9 01 000000cd`343ce3c0 00007ffb`951d83f2 ntdll!RtlReportCriticalFailure+0x97 02 000000cd`343ce4b0 00007ffb`951d86da ntdll!RtlpHeapHandleError+0x12 03 000000cd`343ce4e0 00007ffb`951de361 ntdll!RtlpHpHeapHandleError+0x7a 04 000000cd`343ce510 00007ffb`950f5bf0 ntdll!RtlpLogHeapFailure+0x45 05 000000cd`343ce540 00007ffb`950f47b1 ntdll!RtlpFreeHeapInternal+0x4e0 06 000000cd`343ce600 00007ffb`542850cc ntdll!RtlFreeHeap+0x51 wslbdhm64+0x350cc _free_base wslbdhm64+0x5eeb8 Json::Value::~Json::Value wslbdhm64+0x5ef84 Json::Value::"DestructiveMoveAssignment" wslbdhm64+0x657a6 DoStuff wslbdhm64+0x65e8b wslbdhm64+0x1de43 wslbdhm64+0x1d9d8 wslbdhm64+0x160db wslbdhm64+0x6829e wslbdhm64+0x64faf [ dynamic code ] 00 00000016`760dede0 00007ffb`49b4f32a nss3!PR_GetAddrInfoByName+0xc3 [/builds/worker/checkouts/gecko/nsprpub/pr/src/misc/prnetdb.c @ 2171] 01 (Inline Function) --------`-------- xul!mozilla::net::_GetAddrInfo_Portable+0x5f [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 241] 02 00000016`760dee60 00007ffb`4a2a284b xul!mozilla::net::GetAddrInfo+0x15a [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 374] 03 00000016`760df0a0 00007ffb`4a1e4471 xul!nsHostResolver::ThreadFunc+0x43b [/builds/worker/checkouts/gecko/netwerk/dns/nsHostResolver.cpp @ 1727] 04 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::applyImpl+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1147] 05 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::apply+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1153] 06 00000016`760df200 00007ffb`4a211f3e xul!mozilla::detail::RunnableMethodImpl<RefPtr<nsObserverService>,void (nsObserverService::*)(),1,0>::Run+0x21 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1203] 07 00000016`760df230 00007ffb`4b1c1aa8 xul!nsThreadPool::Run+0x29e [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp @ 311] 08 (Inline Function) --------`-------- xul!nsThread::ProcessNextEvent+0x17b0 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 1198] 09 00000016`760df3d0 00007ffb`4b1ad48f xul!NS_ProcessNextEvent+0x1808 [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp @ 466] 0a 00000016`760df760 00007ffb`4a3aba5f xul!mozilla::ipc::MessagePumpForNonMainThreads::Run+0xcf [/builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp @ 301] 0b (Inline Function) --------`-------- xul!MessageLoop::RunInternal+0x16 [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 381] 0c 00000016`760df800 00007ffb`499ef54e xul!MessageLoop::RunHandler+0x2f [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 375] 0d 00000016`760df850 00007ffb`4a20d757 xul!MessageLoop::Run+0x4e [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 357] 0e 00000016`760df8b0 00007ffb`63d244bc xul!nsThread::ThreadFunc+0xd7 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 385] 0f 00000016`760dfa60 00007ffb`63d39241 nss3!_PR_NativeRunThread+0x13c [/builds/worker/checkouts/gecko/nsprpub/pr/src/threads/combined/pruthr.c @ 421] 10 00000016`760dfae0 00007ffb`c2701bb2 nss3!pr_root+0x11 [/builds/worker/checkouts/gecko/nsprpub/pr/src/md/windows/w95thred.c @ 140] 11 00000016`760dfb10 00007ffb`c3657614 ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x42 12 00000016`760dfb40 00007ffb`9782e8a8 kernel32!BaseThreadInitThunk+0x14 13 (Inline Function) --------`-------- mozglue!mozilla::interceptor::FuncHook<mozilla::interceptor::WindowsDllInterceptor<mozilla::interceptor::VMSharingPolicyShared>,void (*)(int, void *, void *)>::operator()+0x15 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/nsWindowsDllInterceptor.h @ 150] 14 00000016`760dfb70 00007ffb`c4b426a1 mozglue!patched_BaseThreadInitThunk+0x28 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/WindowsDllBlocklist.cpp @ 587] 15 00000016`760dfbe0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 ``` From analyzing the code in the DLL, I believe the code is doing a move assignment to a global or static variable (that's what I called `DestructiveMoveAssignment` in the call stack), like the assignment in the `ThreadFunc` below: ```c++ #include "json/json.h" #include <windows.h> constexpr auto nThreads = 2; constexpr auto nIterations = 0x1000; Json::Value gValue; DWORD ThreadFunc(void* aParam) { for (size_t i = 0; i < nIterations; ++i) { gValue[Json::String("")] = Json::Value("hello"); } return 0; } int main() { auto bResult = HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (!bResult) { return 1; } HANDLE threads[nThreads]{}; for (auto& thread : threads) { DWORD threadId = 0; thread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, &threadId); if (!thread) { return 1; } } WaitForMultipleObjects(nThreads, threads, TRUE, INFINITE); return 0; } ``` Such a move assignment is racy, as the example program illustrates; the example program forces the race condition and crashes with a double free heap corruption: ``` 00 00000038`b59ff3e0 00007fff`5989c213 ntdll!RtlReportFatalFailure+0x9 01 00000038`b59ff430 00007fff`598a52aa ntdll!RtlReportCriticalFailure+0x97 02 00000038`b59ff520 00007fff`598a558a ntdll!RtlpHeapHandleError+0x12 03 00000038`b59ff550 00007fff`598b1585 ntdll!RtlpHpHeapHandleError+0x7a 04 00000038`b59ff580 00007fff`597cc59c ntdll!RtlpLogHeapFailure+0x45 05 00000038`b59ff5b0 00007fff`597cb1e1 ntdll!RtlpFreeHeapInternal+0x84c 06 00000038`b59ff670 00007fff`56c637eb ntdll!RtlFreeHeap+0x51 07 00000038`b59ff6b0 00007ff7`49f34604 ucrtbase!_free_base+0x1b 08 00000038`b59ff6e0 00007ff7`49f354be MyJson!Json::releasePrefixedStringValue+0x14 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 182] 09 00000038`b59ff710 00007ff7`49f350d3 MyJson!Json::Value::releasePayload+0x4e [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 1018] 0a 00000038`b59ff760 00007ff7`49f315e1 MyJson!Json::Value::~Value+0x13 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 443] 0b 00000038`b59ff790 00007fff`58f726bd MyJson!ThreadFunc+0xb1 [C:\repos\MyJson\MyJson\MyJson.cpp @ 13] 0c 00000038`b59ff840 00007fff`597edfb8 KERNEL32!BaseThreadInitThunk+0x1d 0d 00000038`b59ff870 00000000`00000000 ntdll!RtlUserThreadStart+0x28 ``` I will try to contact the vendor to confirm if they do have code that looks like this and if they could add a mutex around it. Can anyone confirm that `PR_GetAddrInfoByName` can indeed get called by different threads? Thanks! (I haven't analyzed the other crashes for the moment.)
I have analyzed the most recurrent crash thanks to the DLL. The heap corruption is reported as a double free. I have identified that the last called functions in the stack belong to the statically-linked [JsonCpp](https://github.com/open-source-parsers/jsoncpp) library. I have also restored the Firefox part of the stack. The crash occurs in third-party code that gets called through `PR_GetAddrInfoByName`, they are probably hooking `getaddrinfo` from `ws2_32.dll` or something like this. The full call stack looks like this: ``` 00 000000cd`343ce370 00007ffb`951cf673 ntdll!RtlReportFatalFailure+0x9 01 000000cd`343ce3c0 00007ffb`951d83f2 ntdll!RtlReportCriticalFailure+0x97 02 000000cd`343ce4b0 00007ffb`951d86da ntdll!RtlpHeapHandleError+0x12 03 000000cd`343ce4e0 00007ffb`951de361 ntdll!RtlpHpHeapHandleError+0x7a 04 000000cd`343ce510 00007ffb`950f5bf0 ntdll!RtlpLogHeapFailure+0x45 05 000000cd`343ce540 00007ffb`950f47b1 ntdll!RtlpFreeHeapInternal+0x4e0 06 000000cd`343ce600 00007ffb`542850cc ntdll!RtlFreeHeap+0x51 wslbdhm64+0x350cc _free_base wslbdhm64+0x5eeb8 Json::Value::~Json::Value wslbdhm64+0x5ef84 Json::Value::"DestructiveMoveAssignment" wslbdhm64+0x657a6 DoStuff wslbdhm64+0x65e8b wslbdhm64+0x1de43 wslbdhm64+0x1d9d8 wslbdhm64+0x160db wslbdhm64+0x6829e wslbdhm64+0x64faf [ dynamic code ] 00 00000016`760dede0 00007ffb`49b4f32a nss3!PR_GetAddrInfoByName+0xc3 [/builds/worker/checkouts/gecko/nsprpub/pr/src/misc/prnetdb.c @ 2171] 01 (Inline Function) --------`-------- xul!mozilla::net::_GetAddrInfo_Portable+0x5f [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 241] 02 00000016`760dee60 00007ffb`4a2a284b xul!mozilla::net::GetAddrInfo+0x15a [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 374] 03 00000016`760df0a0 00007ffb`4a1e4471 xul!nsHostResolver::ThreadFunc+0x43b [/builds/worker/checkouts/gecko/netwerk/dns/nsHostResolver.cpp @ 1727] 04 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::applyImpl+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1147] 05 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::apply+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1153] 06 00000016`760df200 00007ffb`4a211f3e xul!mozilla::detail::RunnableMethodImpl<RefPtr<nsObserverService>,void (nsObserverService::*)(),1,0>::Run+0x21 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1203] 07 00000016`760df230 00007ffb`4b1c1aa8 xul!nsThreadPool::Run+0x29e [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp @ 311] 08 (Inline Function) --------`-------- xul!nsThread::ProcessNextEvent+0x17b0 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 1198] 09 00000016`760df3d0 00007ffb`4b1ad48f xul!NS_ProcessNextEvent+0x1808 [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp @ 466] 0a 00000016`760df760 00007ffb`4a3aba5f xul!mozilla::ipc::MessagePumpForNonMainThreads::Run+0xcf [/builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp @ 301] 0b (Inline Function) --------`-------- xul!MessageLoop::RunInternal+0x16 [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 381] 0c 00000016`760df800 00007ffb`499ef54e xul!MessageLoop::RunHandler+0x2f [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 375] 0d 00000016`760df850 00007ffb`4a20d757 xul!MessageLoop::Run+0x4e [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 357] 0e 00000016`760df8b0 00007ffb`63d244bc xul!nsThread::ThreadFunc+0xd7 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 385] 0f 00000016`760dfa60 00007ffb`63d39241 nss3!_PR_NativeRunThread+0x13c [/builds/worker/checkouts/gecko/nsprpub/pr/src/threads/combined/pruthr.c @ 421] 10 00000016`760dfae0 00007ffb`c2701bb2 nss3!pr_root+0x11 [/builds/worker/checkouts/gecko/nsprpub/pr/src/md/windows/w95thred.c @ 140] 11 00000016`760dfb10 00007ffb`c3657614 ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x42 12 00000016`760dfb40 00007ffb`9782e8a8 kernel32!BaseThreadInitThunk+0x14 13 (Inline Function) --------`-------- mozglue!mozilla::interceptor::FuncHook<mozilla::interceptor::WindowsDllInterceptor<mozilla::interceptor::VMSharingPolicyShared>,void (*)(int, void *, void *)>::operator()+0x15 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/nsWindowsDllInterceptor.h @ 150] 14 00000016`760dfb70 00007ffb`c4b426a1 mozglue!patched_BaseThreadInitThunk+0x28 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/WindowsDllBlocklist.cpp @ 587] 15 00000016`760dfbe0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 ``` From analyzing the code in the DLL, I believe the code is doing a move assignment to a global or static variable (that's what I called `DestructiveMoveAssignment` in the call stack), like the assignment in the `ThreadFunc` below: ```c++ #include "json/json.h" #include <windows.h> constexpr auto nThreads = 2; constexpr auto nIterations = 0x1000; Json::Value gValue; DWORD ThreadFunc(void* aParam) { for (size_t i = 0; i < nIterations; ++i) { gValue[Json::String("")] = Json::Value("hello"); } return 0; } int main() { auto bResult = HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (!bResult) { return 1; } HANDLE threads[nThreads]{}; for (auto& thread : threads) { DWORD threadId = 0; thread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, &threadId); if (!thread) { return 1; } } WaitForMultipleObjects(nThreads, threads, TRUE, INFINITE); return 0; } ``` Such a move assignment is racy, as the example program illustrates; the example program forces the race condition and crashes with a double free heap corruption: ``` 00 00000038`b59ff3e0 00007fff`5989c213 ntdll!RtlReportFatalFailure+0x9 01 00000038`b59ff430 00007fff`598a52aa ntdll!RtlReportCriticalFailure+0x97 02 00000038`b59ff520 00007fff`598a558a ntdll!RtlpHeapHandleError+0x12 03 00000038`b59ff550 00007fff`598b1585 ntdll!RtlpHpHeapHandleError+0x7a 04 00000038`b59ff580 00007fff`597cc59c ntdll!RtlpLogHeapFailure+0x45 05 00000038`b59ff5b0 00007fff`597cb1e1 ntdll!RtlpFreeHeapInternal+0x84c 06 00000038`b59ff670 00007fff`56c637eb ntdll!RtlFreeHeap+0x51 07 00000038`b59ff6b0 00007ff7`49f34604 ucrtbase!_free_base+0x1b 08 00000038`b59ff6e0 00007ff7`49f354be MyJson!Json::releasePrefixedStringValue+0x14 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 182] 09 00000038`b59ff710 00007ff7`49f350d3 MyJson!Json::Value::releasePayload+0x4e [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 1018] 0a 00000038`b59ff760 00007ff7`49f315e1 MyJson!Json::Value::~Value+0x13 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 443] 0b 00000038`b59ff790 00007fff`58f726bd MyJson!ThreadFunc+0xb1 [C:\repos\MyJson\MyJson\MyJson.cpp @ 13] 0c 00000038`b59ff840 00007fff`597edfb8 KERNEL32!BaseThreadInitThunk+0x1d 0d 00000038`b59ff870 00000000`00000000 ntdll!RtlUserThreadStart+0x28 ``` I will try to contact the vendor to confirm if they do have code that looks like this and if they could add a mutex around it. Can anyone confirm that `PR_GetAddrInfoByName` can indeed potentially get called simultaneously by different threads? Thanks! (I haven't analyzed the other crashes for the moment.)
I have analyzed the most recurrent crash thanks to the DLL. The heap corruption is reported as a double free. I have identified that the last called functions in the stack belong to the statically-linked [JsonCpp](https://github.com/open-source-parsers/jsoncpp) library. I have also restored the Firefox part of the stack. The crash occurs in third-party code that gets called through `PR_GetAddrInfoByName`, they are probably hooking `getaddrinfo` from `ws2_32.dll` or something like this. The full call stack looks like this: ``` 00 000000cd`343ce370 00007ffb`951cf673 ntdll!RtlReportFatalFailure+0x9 01 000000cd`343ce3c0 00007ffb`951d83f2 ntdll!RtlReportCriticalFailure+0x97 02 000000cd`343ce4b0 00007ffb`951d86da ntdll!RtlpHeapHandleError+0x12 03 000000cd`343ce4e0 00007ffb`951de361 ntdll!RtlpHpHeapHandleError+0x7a 04 000000cd`343ce510 00007ffb`950f5bf0 ntdll!RtlpLogHeapFailure+0x45 05 000000cd`343ce540 00007ffb`950f47b1 ntdll!RtlpFreeHeapInternal+0x4e0 06 000000cd`343ce600 00007ffb`542850cc ntdll!RtlFreeHeap+0x51 wslbdhm64+0x350cc _free_base wslbdhm64+0x5eeb8 Json::Value::~Json::Value wslbdhm64+0x5ef84 Json::Value::"DestructiveMoveAssignment" wslbdhm64+0x657a6 DoStuff wslbdhm64+0x65e8b wslbdhm64+0x1de43 wslbdhm64+0x1d9d8 wslbdhm64+0x160db wslbdhm64+0x6829e wslbdhm64+0x64faf [ dynamic code ] 00 00000016`760dede0 00007ffb`49b4f32a nss3!PR_GetAddrInfoByName+0xc3 [/builds/worker/checkouts/gecko/nsprpub/pr/src/misc/prnetdb.c @ 2171] 01 (Inline Function) --------`-------- xul!mozilla::net::_GetAddrInfo_Portable+0x5f [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 241] 02 00000016`760dee60 00007ffb`4a2a284b xul!mozilla::net::GetAddrInfo+0x15a [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 374] 03 00000016`760df0a0 00007ffb`4a1e4471 xul!nsHostResolver::ThreadFunc+0x43b [/builds/worker/checkouts/gecko/netwerk/dns/nsHostResolver.cpp @ 1727] 04 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::applyImpl+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1147] 05 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::apply+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1153] 06 00000016`760df200 00007ffb`4a211f3e xul!mozilla::detail::RunnableMethodImpl<RefPtr<nsObserverService>,void (nsObserverService::*)(),1,0>::Run+0x21 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1203] 07 00000016`760df230 00007ffb`4b1c1aa8 xul!nsThreadPool::Run+0x29e [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp @ 311] 08 (Inline Function) --------`-------- xul!nsThread::ProcessNextEvent+0x17b0 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 1198] 09 00000016`760df3d0 00007ffb`4b1ad48f xul!NS_ProcessNextEvent+0x1808 [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp @ 466] 0a 00000016`760df760 00007ffb`4a3aba5f xul!mozilla::ipc::MessagePumpForNonMainThreads::Run+0xcf [/builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp @ 301] 0b (Inline Function) --------`-------- xul!MessageLoop::RunInternal+0x16 [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 381] 0c 00000016`760df800 00007ffb`499ef54e xul!MessageLoop::RunHandler+0x2f [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 375] 0d 00000016`760df850 00007ffb`4a20d757 xul!MessageLoop::Run+0x4e [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 357] 0e 00000016`760df8b0 00007ffb`63d244bc xul!nsThread::ThreadFunc+0xd7 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 385] 0f 00000016`760dfa60 00007ffb`63d39241 nss3!_PR_NativeRunThread+0x13c [/builds/worker/checkouts/gecko/nsprpub/pr/src/threads/combined/pruthr.c @ 421] 10 00000016`760dfae0 00007ffb`c2701bb2 nss3!pr_root+0x11 [/builds/worker/checkouts/gecko/nsprpub/pr/src/md/windows/w95thred.c @ 140] 11 00000016`760dfb10 00007ffb`c3657614 ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x42 12 00000016`760dfb40 00007ffb`9782e8a8 kernel32!BaseThreadInitThunk+0x14 13 (Inline Function) --------`-------- mozglue!mozilla::interceptor::FuncHook<mozilla::interceptor::WindowsDllInterceptor<mozilla::interceptor::VMSharingPolicyShared>,void (*)(int, void *, void *)>::operator()+0x15 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/nsWindowsDllInterceptor.h @ 150] 14 00000016`760dfb70 00007ffb`c4b426a1 mozglue!patched_BaseThreadInitThunk+0x28 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/WindowsDllBlocklist.cpp @ 587] 15 00000016`760dfbe0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 ``` From analyzing the code in the DLL, I believe the code is doing a move assignment to a subvalue of a value stored as a global or static variable (that's what I called `DestructiveMoveAssignment` in the call stack), like the assignment in the `ThreadFunc` below: ```c++ #include "json/json.h" #include <windows.h> constexpr auto nThreads = 2; constexpr auto nIterations = 0x1000; Json::Value gValue; DWORD ThreadFunc(void* aParam) { for (size_t i = 0; i < nIterations; ++i) { gValue[Json::String("")] = Json::Value("hello"); } return 0; } int main() { auto bResult = HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (!bResult) { return 1; } HANDLE threads[nThreads]{}; for (auto& thread : threads) { DWORD threadId = 0; thread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, &threadId); if (!thread) { return 1; } } WaitForMultipleObjects(nThreads, threads, TRUE, INFINITE); return 0; } ``` Such a move assignment is racy, as the example program illustrates; the example program forces the race condition and crashes with a double free heap corruption: ``` 00 00000038`b59ff3e0 00007fff`5989c213 ntdll!RtlReportFatalFailure+0x9 01 00000038`b59ff430 00007fff`598a52aa ntdll!RtlReportCriticalFailure+0x97 02 00000038`b59ff520 00007fff`598a558a ntdll!RtlpHeapHandleError+0x12 03 00000038`b59ff550 00007fff`598b1585 ntdll!RtlpHpHeapHandleError+0x7a 04 00000038`b59ff580 00007fff`597cc59c ntdll!RtlpLogHeapFailure+0x45 05 00000038`b59ff5b0 00007fff`597cb1e1 ntdll!RtlpFreeHeapInternal+0x84c 06 00000038`b59ff670 00007fff`56c637eb ntdll!RtlFreeHeap+0x51 07 00000038`b59ff6b0 00007ff7`49f34604 ucrtbase!_free_base+0x1b 08 00000038`b59ff6e0 00007ff7`49f354be MyJson!Json::releasePrefixedStringValue+0x14 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 182] 09 00000038`b59ff710 00007ff7`49f350d3 MyJson!Json::Value::releasePayload+0x4e [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 1018] 0a 00000038`b59ff760 00007ff7`49f315e1 MyJson!Json::Value::~Value+0x13 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 443] 0b 00000038`b59ff790 00007fff`58f726bd MyJson!ThreadFunc+0xb1 [C:\repos\MyJson\MyJson\MyJson.cpp @ 13] 0c 00000038`b59ff840 00007fff`597edfb8 KERNEL32!BaseThreadInitThunk+0x1d 0d 00000038`b59ff870 00000000`00000000 ntdll!RtlUserThreadStart+0x28 ``` I will try to contact the vendor to confirm if they do have code that looks like this and if they could add a mutex around it. Can anyone confirm that `PR_GetAddrInfoByName` can indeed potentially get called simultaneously by different threads? Thanks! (I haven't analyzed the other crashes for the moment.)
I have analyzed the most recurrent crash thanks to the DLL. The heap corruption is reported as a double free. I have identified that the last called functions in the stack belong to the statically-linked [JsonCpp](https://github.com/open-source-parsers/jsoncpp) library. I have also restored the Firefox part of the stack. The crash occurs in third-party code that gets called through `PR_GetAddrInfoByName`, they are probably hooking `getaddrinfo` from `ws2_32.dll` or something like this. The full call stack looks like this: ``` 00 000000cd`343ce370 00007ffb`951cf673 ntdll!RtlReportFatalFailure+0x9 01 000000cd`343ce3c0 00007ffb`951d83f2 ntdll!RtlReportCriticalFailure+0x97 02 000000cd`343ce4b0 00007ffb`951d86da ntdll!RtlpHeapHandleError+0x12 03 000000cd`343ce4e0 00007ffb`951de361 ntdll!RtlpHpHeapHandleError+0x7a 04 000000cd`343ce510 00007ffb`950f5bf0 ntdll!RtlpLogHeapFailure+0x45 05 000000cd`343ce540 00007ffb`950f47b1 ntdll!RtlpFreeHeapInternal+0x4e0 06 000000cd`343ce600 00007ffb`542850cc ntdll!RtlFreeHeap+0x51 wslbdhm64+0x350cc _free_base wslbdhm64+0x5eeb8 Json::Value::~Json::Value wslbdhm64+0x5ef84 Json::Value::"DestructiveMoveAssignment" wslbdhm64+0x657a6 DoStuff wslbdhm64+0x65e8b wslbdhm64+0x1de43 wslbdhm64+0x1d9d8 wslbdhm64+0x160db wslbdhm64+0x6829e wslbdhm64+0x64faf [ dynamic code ] 00 00000016`760dede0 00007ffb`49b4f32a nss3!PR_GetAddrInfoByName+0xc3 [/builds/worker/checkouts/gecko/nsprpub/pr/src/misc/prnetdb.c @ 2171] 01 (Inline Function) --------`-------- xul!mozilla::net::_GetAddrInfo_Portable+0x5f [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 241] 02 00000016`760dee60 00007ffb`4a2a284b xul!mozilla::net::GetAddrInfo+0x15a [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 374] 03 00000016`760df0a0 00007ffb`4a1e4471 xul!nsHostResolver::ThreadFunc+0x43b [/builds/worker/checkouts/gecko/netwerk/dns/nsHostResolver.cpp @ 1727] 04 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::applyImpl+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1147] 05 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::apply+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1153] 06 00000016`760df200 00007ffb`4a211f3e xul!mozilla::detail::RunnableMethodImpl<RefPtr<nsObserverService>,void (nsObserverService::*)(),1,0>::Run+0x21 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1203] 07 00000016`760df230 00007ffb`4b1c1aa8 xul!nsThreadPool::Run+0x29e [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp @ 311] 08 (Inline Function) --------`-------- xul!nsThread::ProcessNextEvent+0x17b0 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 1198] 09 00000016`760df3d0 00007ffb`4b1ad48f xul!NS_ProcessNextEvent+0x1808 [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp @ 466] 0a 00000016`760df760 00007ffb`4a3aba5f xul!mozilla::ipc::MessagePumpForNonMainThreads::Run+0xcf [/builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp @ 301] 0b (Inline Function) --------`-------- xul!MessageLoop::RunInternal+0x16 [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 381] 0c 00000016`760df800 00007ffb`499ef54e xul!MessageLoop::RunHandler+0x2f [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 375] 0d 00000016`760df850 00007ffb`4a20d757 xul!MessageLoop::Run+0x4e [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 357] 0e 00000016`760df8b0 00007ffb`63d244bc xul!nsThread::ThreadFunc+0xd7 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 385] 0f 00000016`760dfa60 00007ffb`63d39241 nss3!_PR_NativeRunThread+0x13c [/builds/worker/checkouts/gecko/nsprpub/pr/src/threads/combined/pruthr.c @ 421] 10 00000016`760dfae0 00007ffb`c2701bb2 nss3!pr_root+0x11 [/builds/worker/checkouts/gecko/nsprpub/pr/src/md/windows/w95thred.c @ 140] 11 00000016`760dfb10 00007ffb`c3657614 ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x42 12 00000016`760dfb40 00007ffb`9782e8a8 kernel32!BaseThreadInitThunk+0x14 13 (Inline Function) --------`-------- mozglue!mozilla::interceptor::FuncHook<mozilla::interceptor::WindowsDllInterceptor<mozilla::interceptor::VMSharingPolicyShared>,void (*)(int, void *, void *)>::operator()+0x15 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/nsWindowsDllInterceptor.h @ 150] 14 00000016`760dfb70 00007ffb`c4b426a1 mozglue!patched_BaseThreadInitThunk+0x28 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/WindowsDllBlocklist.cpp @ 587] 15 00000016`760dfbe0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 ``` From analyzing the code in the DLL, I believe the code is doing a move assignment to a subvalue of a `Json::Value` stored as a global or static variable (that's what I called `DestructiveMoveAssignment` in the call stack), like the assignment in the `ThreadFunc` below: ```c++ #include "json/json.h" #include <windows.h> constexpr auto nThreads = 2; constexpr auto nIterations = 0x1000; Json::Value gValue; DWORD ThreadFunc(void* aParam) { for (size_t i = 0; i < nIterations; ++i) { gValue[Json::String("")] = Json::Value("hello"); } return 0; } int main() { auto bResult = HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (!bResult) { return 1; } HANDLE threads[nThreads]{}; for (auto& thread : threads) { DWORD threadId = 0; thread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, &threadId); if (!thread) { return 1; } } WaitForMultipleObjects(nThreads, threads, TRUE, INFINITE); return 0; } ``` Such a move assignment is racy, as the example program illustrates; the example program forces the race condition and crashes with a double free heap corruption: ``` 00 00000038`b59ff3e0 00007fff`5989c213 ntdll!RtlReportFatalFailure+0x9 01 00000038`b59ff430 00007fff`598a52aa ntdll!RtlReportCriticalFailure+0x97 02 00000038`b59ff520 00007fff`598a558a ntdll!RtlpHeapHandleError+0x12 03 00000038`b59ff550 00007fff`598b1585 ntdll!RtlpHpHeapHandleError+0x7a 04 00000038`b59ff580 00007fff`597cc59c ntdll!RtlpLogHeapFailure+0x45 05 00000038`b59ff5b0 00007fff`597cb1e1 ntdll!RtlpFreeHeapInternal+0x84c 06 00000038`b59ff670 00007fff`56c637eb ntdll!RtlFreeHeap+0x51 07 00000038`b59ff6b0 00007ff7`49f34604 ucrtbase!_free_base+0x1b 08 00000038`b59ff6e0 00007ff7`49f354be MyJson!Json::releasePrefixedStringValue+0x14 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 182] 09 00000038`b59ff710 00007ff7`49f350d3 MyJson!Json::Value::releasePayload+0x4e [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 1018] 0a 00000038`b59ff760 00007ff7`49f315e1 MyJson!Json::Value::~Value+0x13 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 443] 0b 00000038`b59ff790 00007fff`58f726bd MyJson!ThreadFunc+0xb1 [C:\repos\MyJson\MyJson\MyJson.cpp @ 13] 0c 00000038`b59ff840 00007fff`597edfb8 KERNEL32!BaseThreadInitThunk+0x1d 0d 00000038`b59ff870 00000000`00000000 ntdll!RtlUserThreadStart+0x28 ``` I will try to contact the vendor to confirm if they do have code that looks like this and if they could add a mutex around it. Can anyone confirm that `PR_GetAddrInfoByName` can indeed potentially get called simultaneously by different threads? Thanks! (I haven't analyzed the other crashes for the moment.)
I have analyzed the most recurrent crash thanks to the DLL. The heap corruption is reported as a double free. I have identified that the last called functions in the stack belong to the statically-linked [JsonCpp](https://github.com/open-source-parsers/jsoncpp) library. I have also restored the Firefox part of the stack. The crash occurs in third-party code that gets called through `PR_GetAddrInfoByName`, they are probably hooking `getaddrinfo` from `ws2_32.dll` or something like this. The full call stack looks like this: ``` 00 000000cd`343ce370 00007ffb`951cf673 ntdll!RtlReportFatalFailure+0x9 01 000000cd`343ce3c0 00007ffb`951d83f2 ntdll!RtlReportCriticalFailure+0x97 02 000000cd`343ce4b0 00007ffb`951d86da ntdll!RtlpHeapHandleError+0x12 03 000000cd`343ce4e0 00007ffb`951de361 ntdll!RtlpHpHeapHandleError+0x7a 04 000000cd`343ce510 00007ffb`950f5bf0 ntdll!RtlpLogHeapFailure+0x45 05 000000cd`343ce540 00007ffb`950f47b1 ntdll!RtlpFreeHeapInternal+0x4e0 06 000000cd`343ce600 00007ffb`542850cc ntdll!RtlFreeHeap+0x51 wslbdhm64+0x350cc _free_base wslbdhm64+0x5eeb8 Json::Value::~Json::Value wslbdhm64+0x5ef84 Json::Value::"DestructiveMoveAssignment" wslbdhm64+0x657a6 DoStuff wslbdhm64+0x65e8b wslbdhm64+0x1de43 wslbdhm64+0x1d9d8 wslbdhm64+0x160db wslbdhm64+0x6829e wslbdhm64+0x64faf [ dynamic code ] 00 00000016`760dede0 00007ffb`49b4f32a nss3!PR_GetAddrInfoByName+0xc3 [/builds/worker/checkouts/gecko/nsprpub/pr/src/misc/prnetdb.c @ 2171] 01 (Inline Function) --------`-------- xul!mozilla::net::_GetAddrInfo_Portable+0x5f [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 241] 02 00000016`760dee60 00007ffb`4a2a284b xul!mozilla::net::GetAddrInfo+0x15a [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 374] 03 00000016`760df0a0 00007ffb`4a1e4471 xul!nsHostResolver::ThreadFunc+0x43b [/builds/worker/checkouts/gecko/netwerk/dns/nsHostResolver.cpp @ 1727] 04 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::applyImpl+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1147] 05 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::apply+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1153] 06 00000016`760df200 00007ffb`4a211f3e xul!mozilla::detail::RunnableMethodImpl<RefPtr<nsObserverService>,void (nsObserverService::*)(),1,0>::Run+0x21 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1203] 07 00000016`760df230 00007ffb`4b1c1aa8 xul!nsThreadPool::Run+0x29e [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp @ 311] 08 (Inline Function) --------`-------- xul!nsThread::ProcessNextEvent+0x17b0 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 1198] 09 00000016`760df3d0 00007ffb`4b1ad48f xul!NS_ProcessNextEvent+0x1808 [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp @ 466] 0a 00000016`760df760 00007ffb`4a3aba5f xul!mozilla::ipc::MessagePumpForNonMainThreads::Run+0xcf [/builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp @ 301] 0b (Inline Function) --------`-------- xul!MessageLoop::RunInternal+0x16 [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 381] 0c 00000016`760df800 00007ffb`499ef54e xul!MessageLoop::RunHandler+0x2f [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 375] 0d 00000016`760df850 00007ffb`4a20d757 xul!MessageLoop::Run+0x4e [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 357] 0e 00000016`760df8b0 00007ffb`63d244bc xul!nsThread::ThreadFunc+0xd7 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 385] 0f 00000016`760dfa60 00007ffb`63d39241 nss3!_PR_NativeRunThread+0x13c [/builds/worker/checkouts/gecko/nsprpub/pr/src/threads/combined/pruthr.c @ 421] 10 00000016`760dfae0 00007ffb`c2701bb2 nss3!pr_root+0x11 [/builds/worker/checkouts/gecko/nsprpub/pr/src/md/windows/w95thred.c @ 140] 11 00000016`760dfb10 00007ffb`c3657614 ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x42 12 00000016`760dfb40 00007ffb`9782e8a8 kernel32!BaseThreadInitThunk+0x14 13 (Inline Function) --------`-------- mozglue!mozilla::interceptor::FuncHook<mozilla::interceptor::WindowsDllInterceptor<mozilla::interceptor::VMSharingPolicyShared>,void (*)(int, void *, void *)>::operator()+0x15 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/nsWindowsDllInterceptor.h @ 150] 14 00000016`760dfb70 00007ffb`c4b426a1 mozglue!patched_BaseThreadInitThunk+0x28 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/WindowsDllBlocklist.cpp @ 587] 15 00000016`760dfbe0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 ``` From analyzing the code in the DLL, I believe the code is doing a move assignment to a subvalue of a `Json::Value` stored as a global or static variable (that's what I called `DestructiveMoveAssignment` in the call stack), like the assignment in the `ThreadFunc` below: ```c++ #include "json/json.h" #include <windows.h> constexpr auto nThreads = 2; constexpr auto nIterations = 0x1000; Json::Value gValue; DWORD ThreadFunc(void* aParam) { for (size_t i = 0; i < nIterations; ++i) { gValue[Json::String("")] = Json::Value("hello"); } return 0; } int main() { auto bResult = HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (!bResult) { return 1; } HANDLE threads[nThreads]{}; for (auto& thread : threads) { DWORD threadId = 0; thread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, &threadId); if (!thread) { return 1; } } WaitForMultipleObjects(nThreads, threads, TRUE, INFINITE); return 0; } ``` Such a move assignment is not thread-safe, as the example program illustrates; the example program forces the race condition and crashes with a double free heap corruption with a similar call stack: ``` 00 00000038`b59ff3e0 00007fff`5989c213 ntdll!RtlReportFatalFailure+0x9 01 00000038`b59ff430 00007fff`598a52aa ntdll!RtlReportCriticalFailure+0x97 02 00000038`b59ff520 00007fff`598a558a ntdll!RtlpHeapHandleError+0x12 03 00000038`b59ff550 00007fff`598b1585 ntdll!RtlpHpHeapHandleError+0x7a 04 00000038`b59ff580 00007fff`597cc59c ntdll!RtlpLogHeapFailure+0x45 05 00000038`b59ff5b0 00007fff`597cb1e1 ntdll!RtlpFreeHeapInternal+0x84c 06 00000038`b59ff670 00007fff`56c637eb ntdll!RtlFreeHeap+0x51 07 00000038`b59ff6b0 00007ff7`49f34604 ucrtbase!_free_base+0x1b 08 00000038`b59ff6e0 00007ff7`49f354be MyJson!Json::releasePrefixedStringValue+0x14 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 182] 09 00000038`b59ff710 00007ff7`49f350d3 MyJson!Json::Value::releasePayload+0x4e [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 1018] 0a 00000038`b59ff760 00007ff7`49f315e1 MyJson!Json::Value::~Value+0x13 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 443] 0b 00000038`b59ff790 00007fff`58f726bd MyJson!ThreadFunc+0xb1 [C:\repos\MyJson\MyJson\MyJson.cpp @ 13] 0c 00000038`b59ff840 00007fff`597edfb8 KERNEL32!BaseThreadInitThunk+0x1d 0d 00000038`b59ff870 00000000`00000000 ntdll!RtlUserThreadStart+0x28 ``` I will try to contact the vendor to confirm if they do have code that looks like this and if they could make their code thread-safe. Can anyone confirm that `PR_GetAddrInfoByName` can indeed potentially get called simultaneously by different threads? Thanks! (I haven't analyzed the other crashes for the moment.)
I have analyzed the most recurrent crash thanks to the DLL. The heap corruption is reported as a double free. I have identified that the last called functions in the stack belong to the statically-linked [JsonCpp](https://github.com/open-source-parsers/jsoncpp) library. I have also restored the Firefox part of the stack. The crash occurs in third-party code that gets called through `PR_GetAddrInfoByName`, they are probably hooking `getaddrinfo` from `ws2_32.dll` or something like this. The full call stack looks like this: ``` 00 000000cd`343ce370 00007ffb`951cf673 ntdll!RtlReportFatalFailure+0x9 01 000000cd`343ce3c0 00007ffb`951d83f2 ntdll!RtlReportCriticalFailure+0x97 02 000000cd`343ce4b0 00007ffb`951d86da ntdll!RtlpHeapHandleError+0x12 03 000000cd`343ce4e0 00007ffb`951de361 ntdll!RtlpHpHeapHandleError+0x7a 04 000000cd`343ce510 00007ffb`950f5bf0 ntdll!RtlpLogHeapFailure+0x45 05 000000cd`343ce540 00007ffb`950f47b1 ntdll!RtlpFreeHeapInternal+0x4e0 06 000000cd`343ce600 00007ffb`542850cc ntdll!RtlFreeHeap+0x51 wslbdhm64+0x350cc _free_base wslbdhm64+0x5eeb8 Json::Value::~Json::Value wslbdhm64+0x5ef84 Json::Value::"DestructiveMoveAssignment" wslbdhm64+0x657a6 DoStuff wslbdhm64+0x65e8b wslbdhm64+0x1de43 wslbdhm64+0x1d9d8 wslbdhm64+0x160db wslbdhm64+0x6829e wslbdhm64+0x64faf [ dynamic code ] 00 00000016`760dede0 00007ffb`49b4f32a nss3!PR_GetAddrInfoByName+0xc3 [/builds/worker/checkouts/gecko/nsprpub/pr/src/misc/prnetdb.c @ 2171] 01 (Inline Function) --------`-------- xul!mozilla::net::_GetAddrInfo_Portable+0x5f [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 241] 02 00000016`760dee60 00007ffb`4a2a284b xul!mozilla::net::GetAddrInfo+0x15a [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 374] 03 00000016`760df0a0 00007ffb`4a1e4471 xul!nsHostResolver::ThreadFunc+0x43b [/builds/worker/checkouts/gecko/netwerk/dns/nsHostResolver.cpp @ 1727] 04 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::applyImpl+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1147] 05 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::apply+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1153] 06 00000016`760df200 00007ffb`4a211f3e xul!mozilla::detail::RunnableMethodImpl<RefPtr<nsObserverService>,void (nsObserverService::*)(),1,0>::Run+0x21 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1203] 07 00000016`760df230 00007ffb`4b1c1aa8 xul!nsThreadPool::Run+0x29e [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp @ 311] 08 (Inline Function) --------`-------- xul!nsThread::ProcessNextEvent+0x17b0 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 1198] 09 00000016`760df3d0 00007ffb`4b1ad48f xul!NS_ProcessNextEvent+0x1808 [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp @ 466] 0a 00000016`760df760 00007ffb`4a3aba5f xul!mozilla::ipc::MessagePumpForNonMainThreads::Run+0xcf [/builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp @ 301] 0b (Inline Function) --------`-------- xul!MessageLoop::RunInternal+0x16 [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 381] 0c 00000016`760df800 00007ffb`499ef54e xul!MessageLoop::RunHandler+0x2f [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 375] 0d 00000016`760df850 00007ffb`4a20d757 xul!MessageLoop::Run+0x4e [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 357] 0e 00000016`760df8b0 00007ffb`63d244bc xul!nsThread::ThreadFunc+0xd7 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 385] 0f 00000016`760dfa60 00007ffb`63d39241 nss3!_PR_NativeRunThread+0x13c [/builds/worker/checkouts/gecko/nsprpub/pr/src/threads/combined/pruthr.c @ 421] 10 00000016`760dfae0 00007ffb`c2701bb2 nss3!pr_root+0x11 [/builds/worker/checkouts/gecko/nsprpub/pr/src/md/windows/w95thred.c @ 140] 11 00000016`760dfb10 00007ffb`c3657614 ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x42 12 00000016`760dfb40 00007ffb`9782e8a8 kernel32!BaseThreadInitThunk+0x14 13 (Inline Function) --------`-------- mozglue!mozilla::interceptor::FuncHook<mozilla::interceptor::WindowsDllInterceptor<mozilla::interceptor::VMSharingPolicyShared>,void (*)(int, void *, void *)>::operator()+0x15 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/nsWindowsDllInterceptor.h @ 150] 14 00000016`760dfb70 00007ffb`c4b426a1 mozglue!patched_BaseThreadInitThunk+0x28 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/WindowsDllBlocklist.cpp @ 587] 15 00000016`760dfbe0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 ``` From analyzing the code in the DLL, I believe the code is doing a move assignment to a subvalue of a `Json::Value` stored as a global or static variable (that's what I called `DestructiveMoveAssignment` in the call stack), like the assignment in the `ThreadFunc` below: ```c++ #include "json/json.h" #include <windows.h> constexpr auto nThreads = 2; constexpr auto nIterations = 0x1000; Json::Value gValue; DWORD ThreadFunc(void* aParam) { for (size_t i = 0; i < nIterations; ++i) { gValue[Json::String("")] = Json::Value("hello"); } return 0; } int main() { auto bResult = HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (!bResult) { return 1; } HANDLE threads[nThreads]{}; for (auto& thread : threads) { DWORD threadId = 0; thread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, &threadId); if (!thread) { return 1; } } WaitForMultipleObjects(nThreads, threads, TRUE, INFINITE); return 0; } ``` Such a move assignment is not thread-safe, as the example program illustrates; the example program forces the race condition and crashes with a double free heap corruption with a similar call stack: ``` 00 00000038`b59ff3e0 00007fff`5989c213 ntdll!RtlReportFatalFailure+0x9 01 00000038`b59ff430 00007fff`598a52aa ntdll!RtlReportCriticalFailure+0x97 02 00000038`b59ff520 00007fff`598a558a ntdll!RtlpHeapHandleError+0x12 03 00000038`b59ff550 00007fff`598b1585 ntdll!RtlpHpHeapHandleError+0x7a 04 00000038`b59ff580 00007fff`597cc59c ntdll!RtlpLogHeapFailure+0x45 05 00000038`b59ff5b0 00007fff`597cb1e1 ntdll!RtlpFreeHeapInternal+0x84c 06 00000038`b59ff670 00007fff`56c637eb ntdll!RtlFreeHeap+0x51 07 00000038`b59ff6b0 00007ff7`49f34604 ucrtbase!_free_base+0x1b 08 00000038`b59ff6e0 00007ff7`49f354be MyJson!Json::releasePrefixedStringValue+0x14 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 182] 09 00000038`b59ff710 00007ff7`49f350d3 MyJson!Json::Value::releasePayload+0x4e [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 1018] 0a 00000038`b59ff760 00007ff7`49f315e1 MyJson!Json::Value::~Value+0x13 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 443] 0b 00000038`b59ff790 00007fff`58f726bd MyJson!ThreadFunc+0xb1 [C:\repos\MyJson\MyJson\MyJson.cpp @ 13] 0c 00000038`b59ff840 00007fff`597edfb8 KERNEL32!BaseThreadInitThunk+0x1d 0d 00000038`b59ff870 00000000`00000000 ntdll!RtlUserThreadStart+0x28 ``` I will try to contact the vendor to confirm if they do have code that looks like this and if they could make their code thread-safe. Can anyone confirm that `PR_GetAddrInfoByName` can indeed potentially get called simultaneously by different threads? (Well, the call stack suggests that this is exactly what we are doing here -- using a specific thread each time we need to resolve a hostname.) Thanks! (I haven't analyzed the other crashes for the moment.)
I have analyzed the most recurrent crash thanks to the DLL. The heap corruption is reported as a double free. I have identified that the last called functions in the stack belong to the statically-linked [JsonCpp](https://github.com/open-source-parsers/jsoncpp) library. I have also restored the Firefox part of the stack. The crash occurs in third-party code that gets called through `PR_GetAddrInfoByName`, they are probably hooking `getaddrinfo` from `ws2_32.dll` or something like this. The full call stack looks like this: ``` 00 000000cd`343ce370 00007ffb`951cf673 ntdll!RtlReportFatalFailure+0x9 01 000000cd`343ce3c0 00007ffb`951d83f2 ntdll!RtlReportCriticalFailure+0x97 02 000000cd`343ce4b0 00007ffb`951d86da ntdll!RtlpHeapHandleError+0x12 03 000000cd`343ce4e0 00007ffb`951de361 ntdll!RtlpHpHeapHandleError+0x7a 04 000000cd`343ce510 00007ffb`950f5bf0 ntdll!RtlpLogHeapFailure+0x45 05 000000cd`343ce540 00007ffb`950f47b1 ntdll!RtlpFreeHeapInternal+0x4e0 06 000000cd`343ce600 00007ffb`542850cc ntdll!RtlFreeHeap+0x51 wslbdhm64+0x350cc _free_base wslbdhm64+0x5eeb8 Json::Value::~Json::Value wslbdhm64+0x5ef84 Json::Value::"DestructiveMoveAssignment" wslbdhm64+0x657a6 DoStuff wslbdhm64+0x65e8b wslbdhm64+0x1de43 wslbdhm64+0x1d9d8 wslbdhm64+0x160db wslbdhm64+0x6829e wslbdhm64+0x64faf [ dynamic code ] 00 00000016`760dede0 00007ffb`49b4f32a nss3!PR_GetAddrInfoByName+0xc3 [/builds/worker/checkouts/gecko/nsprpub/pr/src/misc/prnetdb.c @ 2171] 01 (Inline Function) --------`-------- xul!mozilla::net::_GetAddrInfo_Portable+0x5f [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 241] 02 00000016`760dee60 00007ffb`4a2a284b xul!mozilla::net::GetAddrInfo+0x15a [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 374] 03 00000016`760df0a0 00007ffb`4a1e4471 xul!nsHostResolver::ThreadFunc+0x43b [/builds/worker/checkouts/gecko/netwerk/dns/nsHostResolver.cpp @ 1727] 04 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::applyImpl+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1147] 05 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::apply+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1153] 06 00000016`760df200 00007ffb`4a211f3e xul!mozilla::detail::RunnableMethodImpl<RefPtr<nsObserverService>,void (nsObserverService::*)(),1,0>::Run+0x21 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1203] 07 00000016`760df230 00007ffb`4b1c1aa8 xul!nsThreadPool::Run+0x29e [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp @ 311] 08 (Inline Function) --------`-------- xul!nsThread::ProcessNextEvent+0x17b0 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 1198] 09 00000016`760df3d0 00007ffb`4b1ad48f xul!NS_ProcessNextEvent+0x1808 [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp @ 466] 0a 00000016`760df760 00007ffb`4a3aba5f xul!mozilla::ipc::MessagePumpForNonMainThreads::Run+0xcf [/builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp @ 301] 0b (Inline Function) --------`-------- xul!MessageLoop::RunInternal+0x16 [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 381] 0c 00000016`760df800 00007ffb`499ef54e xul!MessageLoop::RunHandler+0x2f [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 375] 0d 00000016`760df850 00007ffb`4a20d757 xul!MessageLoop::Run+0x4e [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 357] 0e 00000016`760df8b0 00007ffb`63d244bc xul!nsThread::ThreadFunc+0xd7 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 385] 0f 00000016`760dfa60 00007ffb`63d39241 nss3!_PR_NativeRunThread+0x13c [/builds/worker/checkouts/gecko/nsprpub/pr/src/threads/combined/pruthr.c @ 421] 10 00000016`760dfae0 00007ffb`c2701bb2 nss3!pr_root+0x11 [/builds/worker/checkouts/gecko/nsprpub/pr/src/md/windows/w95thred.c @ 140] 11 00000016`760dfb10 00007ffb`c3657614 ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x42 12 00000016`760dfb40 00007ffb`9782e8a8 kernel32!BaseThreadInitThunk+0x14 13 (Inline Function) --------`-------- mozglue!mozilla::interceptor::FuncHook<mozilla::interceptor::WindowsDllInterceptor<mozilla::interceptor::VMSharingPolicyShared>,void (*)(int, void *, void *)>::operator()+0x15 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/nsWindowsDllInterceptor.h @ 150] 14 00000016`760dfb70 00007ffb`c4b426a1 mozglue!patched_BaseThreadInitThunk+0x28 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/WindowsDllBlocklist.cpp @ 587] 15 00000016`760dfbe0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 ``` From analyzing the code in the DLL, I believe the code is doing a move assignment to a subvalue of a `Json::Value` stored as a global or static variable (that's what I called `DestructiveMoveAssignment` in the call stack), like the assignment in the `ThreadFunc` below: ```c++ #include "json/json.h" #include <windows.h> constexpr auto nThreads = 2; constexpr auto nIterations = 0x1000; Json::Value gValue; DWORD ThreadFunc(void* aParam) { for (size_t i = 0; i < nIterations; ++i) { gValue[Json::String("")] = Json::Value("hello"); } return 0; } int main() { auto bResult = HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (!bResult) { return 1; } HANDLE threads[nThreads]{}; for (auto& thread : threads) { DWORD threadId = 0; thread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, &threadId); if (!thread) { return 1; } } WaitForMultipleObjects(nThreads, threads, TRUE, INFINITE); return 0; } ``` Such a move assignment is not thread-safe, as the example program illustrates; the example program forces the race condition and crashes with a double free heap corruption with a similar call stack (the call to `releasePayload` is inlined but present in the original crash): ``` 00 00000038`b59ff3e0 00007fff`5989c213 ntdll!RtlReportFatalFailure+0x9 01 00000038`b59ff430 00007fff`598a52aa ntdll!RtlReportCriticalFailure+0x97 02 00000038`b59ff520 00007fff`598a558a ntdll!RtlpHeapHandleError+0x12 03 00000038`b59ff550 00007fff`598b1585 ntdll!RtlpHpHeapHandleError+0x7a 04 00000038`b59ff580 00007fff`597cc59c ntdll!RtlpLogHeapFailure+0x45 05 00000038`b59ff5b0 00007fff`597cb1e1 ntdll!RtlpFreeHeapInternal+0x84c 06 00000038`b59ff670 00007fff`56c637eb ntdll!RtlFreeHeap+0x51 07 00000038`b59ff6b0 00007ff7`49f34604 ucrtbase!_free_base+0x1b 08 00000038`b59ff6e0 00007ff7`49f354be MyJson!Json::releasePrefixedStringValue+0x14 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 182] 09 00000038`b59ff710 00007ff7`49f350d3 MyJson!Json::Value::releasePayload+0x4e [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 1018] 0a 00000038`b59ff760 00007ff7`49f315e1 MyJson!Json::Value::~Value+0x13 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 443] 0b 00000038`b59ff790 00007fff`58f726bd MyJson!ThreadFunc+0xb1 [C:\repos\MyJson\MyJson\MyJson.cpp @ 13] 0c 00000038`b59ff840 00007fff`597edfb8 KERNEL32!BaseThreadInitThunk+0x1d 0d 00000038`b59ff870 00000000`00000000 ntdll!RtlUserThreadStart+0x28 ``` I will try to contact the vendor to confirm if they do have code that looks like this and if they could make their code thread-safe. Can anyone confirm that `PR_GetAddrInfoByName` can indeed potentially get called simultaneously by different threads? (Well, the call stack suggests that this is exactly what we are doing here -- using a specific thread each time we need to resolve a hostname.) Thanks! (I haven't analyzed the other crashes for the moment.)
I have analyzed the most recurrent crash thanks to the DLL. The heap corruption is reported as a double free. I have identified that the last called functions in the stack belong to the statically-linked [JsonCpp](https://github.com/open-source-parsers/jsoncpp) library. I have also restored the Firefox part of the stack. The crash occurs in third-party code that gets called through `PR_GetAddrInfoByName`, they are probably hooking `getaddrinfo` from `ws2_32.dll` or something like this. The full call stack looks like this: ``` 00 000000cd`343ce370 00007ffb`951cf673 ntdll!RtlReportFatalFailure+0x9 01 000000cd`343ce3c0 00007ffb`951d83f2 ntdll!RtlReportCriticalFailure+0x97 02 000000cd`343ce4b0 00007ffb`951d86da ntdll!RtlpHeapHandleError+0x12 03 000000cd`343ce4e0 00007ffb`951de361 ntdll!RtlpHpHeapHandleError+0x7a 04 000000cd`343ce510 00007ffb`950f5bf0 ntdll!RtlpLogHeapFailure+0x45 05 000000cd`343ce540 00007ffb`950f47b1 ntdll!RtlpFreeHeapInternal+0x4e0 06 000000cd`343ce600 00007ffb`542850cc ntdll!RtlFreeHeap+0x51 wslbdhm64+0x350cc _free_base wslbdhm64+0x5eeb8 Json::Value::~Json::Value wslbdhm64+0x5ef84 Json::Value::"DestructiveMoveAssignment" wslbdhm64+0x657a6 DoStuff wslbdhm64+0x65e8b wslbdhm64+0x1de43 wslbdhm64+0x1d9d8 wslbdhm64+0x160db wslbdhm64+0x6829e wslbdhm64+0x64faf [ dynamic code ] 00 00000016`760dede0 00007ffb`49b4f32a nss3!PR_GetAddrInfoByName+0xc3 [/builds/worker/checkouts/gecko/nsprpub/pr/src/misc/prnetdb.c @ 2171] 01 (Inline Function) --------`-------- xul!mozilla::net::_GetAddrInfo_Portable+0x5f [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 241] 02 00000016`760dee60 00007ffb`4a2a284b xul!mozilla::net::GetAddrInfo+0x15a [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 374] 03 00000016`760df0a0 00007ffb`4a1e4471 xul!nsHostResolver::ThreadFunc+0x43b [/builds/worker/checkouts/gecko/netwerk/dns/nsHostResolver.cpp @ 1727] 04 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::applyImpl+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1147] 05 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::apply+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1153] 06 00000016`760df200 00007ffb`4a211f3e xul!mozilla::detail::RunnableMethodImpl<RefPtr<nsObserverService>,void (nsObserverService::*)(),1,0>::Run+0x21 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1203] 07 00000016`760df230 00007ffb`4b1c1aa8 xul!nsThreadPool::Run+0x29e [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp @ 311] 08 (Inline Function) --------`-------- xul!nsThread::ProcessNextEvent+0x17b0 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 1198] 09 00000016`760df3d0 00007ffb`4b1ad48f xul!NS_ProcessNextEvent+0x1808 [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp @ 466] 0a 00000016`760df760 00007ffb`4a3aba5f xul!mozilla::ipc::MessagePumpForNonMainThreads::Run+0xcf [/builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp @ 301] 0b (Inline Function) --------`-------- xul!MessageLoop::RunInternal+0x16 [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 381] 0c 00000016`760df800 00007ffb`499ef54e xul!MessageLoop::RunHandler+0x2f [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 375] 0d 00000016`760df850 00007ffb`4a20d757 xul!MessageLoop::Run+0x4e [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 357] 0e 00000016`760df8b0 00007ffb`63d244bc xul!nsThread::ThreadFunc+0xd7 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 385] 0f 00000016`760dfa60 00007ffb`63d39241 nss3!_PR_NativeRunThread+0x13c [/builds/worker/checkouts/gecko/nsprpub/pr/src/threads/combined/pruthr.c @ 421] 10 00000016`760dfae0 00007ffb`c2701bb2 nss3!pr_root+0x11 [/builds/worker/checkouts/gecko/nsprpub/pr/src/md/windows/w95thred.c @ 140] 11 00000016`760dfb10 00007ffb`c3657614 ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x42 12 00000016`760dfb40 00007ffb`9782e8a8 kernel32!BaseThreadInitThunk+0x14 13 (Inline Function) --------`-------- mozglue!mozilla::interceptor::FuncHook<mozilla::interceptor::WindowsDllInterceptor<mozilla::interceptor::VMSharingPolicyShared>,void (*)(int, void *, void *)>::operator()+0x15 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/nsWindowsDllInterceptor.h @ 150] 14 00000016`760dfb70 00007ffb`c4b426a1 mozglue!patched_BaseThreadInitThunk+0x28 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/WindowsDllBlocklist.cpp @ 587] 15 00000016`760dfbe0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 ``` From analyzing the code in the DLL, I believe the code is doing a move assignment to a subvalue of a `Json::Value` stored as a global or static variable (that's what I called `DestructiveMoveAssignment` in the call stack), like the assignment in the `ThreadFunc` below: ```c++ #include "json/json.h" #include <windows.h> constexpr auto nThreads = 2; constexpr auto nIterations = 0x1000; Json::Value gValue; DWORD ThreadFunc(void* aParam) { for (size_t i = 0; i < nIterations; ++i) { gValue[Json::String("")] = Json::Value("hello"); } return 0; } int main() { auto bResult = HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (!bResult) { return 1; } HANDLE threads[nThreads]{}; for (auto& thread : threads) { DWORD threadId = 0; thread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, &threadId); if (!thread) { return 1; } } WaitForMultipleObjects(nThreads, threads, TRUE, INFINITE); return 0; } ``` Such a move assignment is not thread-safe, as the example program illustrates; the example program forces the race condition and crashes with a double free heap corruption with a similar call stack (the call to `releasePayload` and `releasePrefixedStringValue` are inlined but present in the original crash): ``` 00 00000038`b59ff3e0 00007fff`5989c213 ntdll!RtlReportFatalFailure+0x9 01 00000038`b59ff430 00007fff`598a52aa ntdll!RtlReportCriticalFailure+0x97 02 00000038`b59ff520 00007fff`598a558a ntdll!RtlpHeapHandleError+0x12 03 00000038`b59ff550 00007fff`598b1585 ntdll!RtlpHpHeapHandleError+0x7a 04 00000038`b59ff580 00007fff`597cc59c ntdll!RtlpLogHeapFailure+0x45 05 00000038`b59ff5b0 00007fff`597cb1e1 ntdll!RtlpFreeHeapInternal+0x84c 06 00000038`b59ff670 00007fff`56c637eb ntdll!RtlFreeHeap+0x51 07 00000038`b59ff6b0 00007ff7`49f34604 ucrtbase!_free_base+0x1b 08 00000038`b59ff6e0 00007ff7`49f354be MyJson!Json::releasePrefixedStringValue+0x14 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 182] 09 00000038`b59ff710 00007ff7`49f350d3 MyJson!Json::Value::releasePayload+0x4e [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 1018] 0a 00000038`b59ff760 00007ff7`49f315e1 MyJson!Json::Value::~Value+0x13 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 443] 0b 00000038`b59ff790 00007fff`58f726bd MyJson!ThreadFunc+0xb1 [C:\repos\MyJson\MyJson\MyJson.cpp @ 13] 0c 00000038`b59ff840 00007fff`597edfb8 KERNEL32!BaseThreadInitThunk+0x1d 0d 00000038`b59ff870 00000000`00000000 ntdll!RtlUserThreadStart+0x28 ``` I will try to contact the vendor to confirm if they do have code that looks like this and if they could make their code thread-safe. Can anyone confirm that `PR_GetAddrInfoByName` can indeed potentially get called simultaneously by different threads? (Well, the call stack suggests that this is exactly what we are doing here -- using a specific thread each time we need to resolve a hostname.) Thanks! (I haven't analyzed the other crashes for the moment.)
I have analyzed the most recurrent crash thanks to the DLL. The heap corruption is reported as a double free. I have identified that the last called functions in the stack belong to the statically-linked [JsonCpp](https://github.com/open-source-parsers/jsoncpp) library. I have also restored the Firefox part of the stack. The crash occurs in third-party code that gets called through `PR_GetAddrInfoByName`, they are probably hooking `getaddrinfo` from `ws2_32.dll` or something like this. The full call stack looks like this: ``` 00 000000cd`343ce370 00007ffb`951cf673 ntdll!RtlReportFatalFailure+0x9 01 000000cd`343ce3c0 00007ffb`951d83f2 ntdll!RtlReportCriticalFailure+0x97 02 000000cd`343ce4b0 00007ffb`951d86da ntdll!RtlpHeapHandleError+0x12 03 000000cd`343ce4e0 00007ffb`951de361 ntdll!RtlpHpHeapHandleError+0x7a 04 000000cd`343ce510 00007ffb`950f5bf0 ntdll!RtlpLogHeapFailure+0x45 05 000000cd`343ce540 00007ffb`950f47b1 ntdll!RtlpFreeHeapInternal+0x4e0 06 000000cd`343ce600 00007ffb`542850cc ntdll!RtlFreeHeap+0x51 wslbdhm64+0x350cc _free_base wslbdhm64+0x5eeb8 Json::Value::~Value wslbdhm64+0x5ef84 Json::Value::"DestructiveMoveAssignment" wslbdhm64+0x657a6 DoStuff wslbdhm64+0x65e8b wslbdhm64+0x1de43 wslbdhm64+0x1d9d8 wslbdhm64+0x160db wslbdhm64+0x6829e wslbdhm64+0x64faf [ dynamic code ] 00 00000016`760dede0 00007ffb`49b4f32a nss3!PR_GetAddrInfoByName+0xc3 [/builds/worker/checkouts/gecko/nsprpub/pr/src/misc/prnetdb.c @ 2171] 01 (Inline Function) --------`-------- xul!mozilla::net::_GetAddrInfo_Portable+0x5f [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 241] 02 00000016`760dee60 00007ffb`4a2a284b xul!mozilla::net::GetAddrInfo+0x15a [/builds/worker/checkouts/gecko/netwerk/dns/GetAddrInfo.cpp @ 374] 03 00000016`760df0a0 00007ffb`4a1e4471 xul!nsHostResolver::ThreadFunc+0x43b [/builds/worker/checkouts/gecko/netwerk/dns/nsHostResolver.cpp @ 1727] 04 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::applyImpl+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1147] 05 (Inline Function) --------`-------- xul!mozilla::detail::RunnableMethodArguments<>::apply+0x9 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1153] 06 00000016`760df200 00007ffb`4a211f3e xul!mozilla::detail::RunnableMethodImpl<RefPtr<nsObserverService>,void (nsObserverService::*)(),1,0>::Run+0x21 [/builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h @ 1203] 07 00000016`760df230 00007ffb`4b1c1aa8 xul!nsThreadPool::Run+0x29e [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp @ 311] 08 (Inline Function) --------`-------- xul!nsThread::ProcessNextEvent+0x17b0 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 1198] 09 00000016`760df3d0 00007ffb`4b1ad48f xul!NS_ProcessNextEvent+0x1808 [/builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp @ 466] 0a 00000016`760df760 00007ffb`4a3aba5f xul!mozilla::ipc::MessagePumpForNonMainThreads::Run+0xcf [/builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp @ 301] 0b (Inline Function) --------`-------- xul!MessageLoop::RunInternal+0x16 [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 381] 0c 00000016`760df800 00007ffb`499ef54e xul!MessageLoop::RunHandler+0x2f [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 375] 0d 00000016`760df850 00007ffb`4a20d757 xul!MessageLoop::Run+0x4e [/builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc @ 357] 0e 00000016`760df8b0 00007ffb`63d244bc xul!nsThread::ThreadFunc+0xd7 [/builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp @ 385] 0f 00000016`760dfa60 00007ffb`63d39241 nss3!_PR_NativeRunThread+0x13c [/builds/worker/checkouts/gecko/nsprpub/pr/src/threads/combined/pruthr.c @ 421] 10 00000016`760dfae0 00007ffb`c2701bb2 nss3!pr_root+0x11 [/builds/worker/checkouts/gecko/nsprpub/pr/src/md/windows/w95thred.c @ 140] 11 00000016`760dfb10 00007ffb`c3657614 ucrtbase!thread_start<unsigned int (__cdecl*)(void *),1>+0x42 12 00000016`760dfb40 00007ffb`9782e8a8 kernel32!BaseThreadInitThunk+0x14 13 (Inline Function) --------`-------- mozglue!mozilla::interceptor::FuncHook<mozilla::interceptor::WindowsDllInterceptor<mozilla::interceptor::VMSharingPolicyShared>,void (*)(int, void *, void *)>::operator()+0x15 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/nsWindowsDllInterceptor.h @ 150] 14 00000016`760dfb70 00007ffb`c4b426a1 mozglue!patched_BaseThreadInitThunk+0x28 [/builds/worker/checkouts/gecko/toolkit/xre/dllservices/mozglue/WindowsDllBlocklist.cpp @ 587] 15 00000016`760dfbe0 00000000`00000000 ntdll!RtlUserThreadStart+0x21 ``` From analyzing the code in the DLL, I believe the code is doing a move assignment to a subvalue of a `Json::Value` stored as a global or static variable (that's what I called `DestructiveMoveAssignment` in the call stack), like the assignment in the `ThreadFunc` below: ```c++ #include "json/json.h" #include <windows.h> constexpr auto nThreads = 2; constexpr auto nIterations = 0x1000; Json::Value gValue; DWORD ThreadFunc(void* aParam) { for (size_t i = 0; i < nIterations; ++i) { gValue[Json::String("")] = Json::Value("hello"); } return 0; } int main() { auto bResult = HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); if (!bResult) { return 1; } HANDLE threads[nThreads]{}; for (auto& thread : threads) { DWORD threadId = 0; thread = CreateThread(nullptr, 0, ThreadFunc, nullptr, 0, &threadId); if (!thread) { return 1; } } WaitForMultipleObjects(nThreads, threads, TRUE, INFINITE); return 0; } ``` Such a move assignment is not thread-safe, as the example program illustrates; the example program forces the race condition and crashes with a double free heap corruption with a similar call stack (the call to `releasePayload` and `releasePrefixedStringValue` are inlined but present in the original crash): ``` 00 00000038`b59ff3e0 00007fff`5989c213 ntdll!RtlReportFatalFailure+0x9 01 00000038`b59ff430 00007fff`598a52aa ntdll!RtlReportCriticalFailure+0x97 02 00000038`b59ff520 00007fff`598a558a ntdll!RtlpHeapHandleError+0x12 03 00000038`b59ff550 00007fff`598b1585 ntdll!RtlpHpHeapHandleError+0x7a 04 00000038`b59ff580 00007fff`597cc59c ntdll!RtlpLogHeapFailure+0x45 05 00000038`b59ff5b0 00007fff`597cb1e1 ntdll!RtlpFreeHeapInternal+0x84c 06 00000038`b59ff670 00007fff`56c637eb ntdll!RtlFreeHeap+0x51 07 00000038`b59ff6b0 00007ff7`49f34604 ucrtbase!_free_base+0x1b 08 00000038`b59ff6e0 00007ff7`49f354be MyJson!Json::releasePrefixedStringValue+0x14 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 182] 09 00000038`b59ff710 00007ff7`49f350d3 MyJson!Json::Value::releasePayload+0x4e [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 1018] 0a 00000038`b59ff760 00007ff7`49f315e1 MyJson!Json::Value::~Value+0x13 [C:\repos\MyJson\packages\JsonCpp-source.1.0.2\build\native\src\json\json_value.cpp @ 443] 0b 00000038`b59ff790 00007fff`58f726bd MyJson!ThreadFunc+0xb1 [C:\repos\MyJson\MyJson\MyJson.cpp @ 13] 0c 00000038`b59ff840 00007fff`597edfb8 KERNEL32!BaseThreadInitThunk+0x1d 0d 00000038`b59ff870 00000000`00000000 ntdll!RtlUserThreadStart+0x28 ``` I will try to contact the vendor to confirm if they do have code that looks like this and if they could make their code thread-safe. Can anyone confirm that `PR_GetAddrInfoByName` can indeed potentially get called simultaneously by different threads? (Well, the call stack suggests that this is exactly what we are doing here -- using a specific thread each time we need to resolve a hostname.) Thanks! (I haven't analyzed the other crashes for the moment.)