| Issue |
179976
|
| Summary |
sanitizer_unwind_win.cpp: seems that stack walking cannot handle all types of functions
|
| Labels |
new issue
|
| Assignees |
|
| Reporter |
b068931
|
I use ASan that comes with MSVC, so it might be modified, but it seems that source code here has the same problem.
On Windows, ASan injects its own SEH filter for uncaught exceptions to dump information about them. It is helpful, but its stack trace generation logic does not cover all edge cases on that OS.
File [sanitizer_unwind_win.cpp](https://github.com/llvm/llvm-project/blob/25b4609b55135f1fc0e6deffebc3a2f948785d4a/compiler-rt/lib/sanitizer_common/sanitizer_unwind_win.cpp#L93C1-L98C4) uses StackWalk64 to get the list of PCs for frames in stack. StackWalk64 abstracts away most of the complexity, but still requires some functions that will give it additional information as to how unwind the stack properly. One of those is PFUNCTION_TABLE_ACCESS_ROUTINE64.
Current implementation uses `SymFunctionTableAccess64`, which can handle entries registered in DbgHelp. DbgHelp, however, only knows about functions which have program database files generated by the compiler. RUNTIME_FUNCTION entries with their UNWIND_INFO structures registered using RtlAddFunctionTable won't be covered by it. Thus, exceptions can propagate through these functions and can end up in ASan's filter, but it ends up generating garbage.
In my case, it ends up with something like this ("frames" one through five report shadow space and pushed registers):
```
#0 0x022e018a01dd (<unknown module>)
#1 0x000000000003 (<unknown module>)
#2 0x000000000000 (<unknown module>)
#3 0x124464ca1928 (<unknown module>)
#4 0x124864f98d7f (<unknown module>)
#5 0x02438e7ffcad (<unknown module>)
```
While it should have been more like this:
```
[0] 0x220FF9201DE [DYNAMIC_FUNCTION]+0x13A
[1] 0x7FFB783AF6BD MYDLL.dll!std::invoke<void (MYCLASS::*)(unsigned long long),MYCLASS *,unsigned short>+0x2D
[2] 0x7FFB783AF337 MYDLL.dll!std::thread::_Invoke<std::tuple<void (MYCLASS::*)(unsigned long long),MYCLASS *,unsigned short>,0,1,2>+0x67
...
```
I use code like this to handle dynamic functions registered with Rtl*:
```
// Custom function table access that falls back to RtlLookupFunctionEntry.
[[nodiscard]] PVOID CALLBACK CustomFunctionTableAccess64(HANDLE hProcess, DWORD64 dwAddrBase) {
// First try DbgHelp's function.
if (pfnSymFunctionTableAccess64 && g_bSymbolsInitialized) {
if (PVOID pResult = pfnSymFunctionTableAccess64(hProcess, dwAddrBase)) {
return pResult;
}
}
// Fall back to RtlLookupFunctionEntry for dynamic code.
// Function registered with RtlAddFunctionTable is not necessarily registered with DbgHelp,
// so this is required to cover some edge cases.
#if defined(_M_AMD64) || defined(_M_ARM64)
DWORD64 dw64ImageBase = 0;
return RtlLookupFunctionEntry(dwAddrBase, &dw64ImageBase, nullptr);
#else
return nullptr;
#endif
}
```
For PGET_MODULE_BASE_ROUTINE64 I ended up with a very similar routine. This gives enough information to the stack walking procedure to traverse a dynamic frame without PDB. I have no idea whether code like this is good enough for LLVM, but in my case it does work, without RtlLookupFunctionEntry I end up with incomplete frame. I also created a gist with a full code sample [here](https://gist.github.com/b068931/b76b88bf858426707e2b6008ca00567c).
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs