sentry 在加载模块时闪退
这是一个很久之前的问题了,今天记录一下,以便遇到同样问题的同学能够看到此文章
崩溃环境:
目前仅收到 windows 7 的部分用户反馈,在程序启动时发生闪退
问题分析:
查看用户提供的日志,可以看见崩溃发生在 sentry 内部

堆栈显示,在加载 sentry_get_modules_list 时程序出现闪退,已知 sentry_get_modules_list 是 sentry 源码里可找到的函数
p.s. sentry_capture_event 是 sentry 内部上传日志所调用的函数,sentry_unwind_stack_from_ucontext 没有在源码中找到,所以姑且跳过这个函数
我们猜测是 sentry 在加载模块时出错了,于是我们便尝试将加载模块的部分代码先注释,再看会不会闪退
sentry_value_t
sentry_get_modules_list(void)
{
sentry__mutex_lock(&g_mutex);
if (!g_initialized) {
// load_modules(); // 这块先注释掉
g_initialized = true;
}
sentry_value_t modules = g_modules;
sentry_value_incref(modules);
sentry__mutex_unlock(&g_mutex);
return modules;
}
发现问题确实是出现 load_modules 里面,继续看 load_modules 内部干了啥,
static void
load_modules(void)
{
HANDLE snapshot
= CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId());
MODULEENTRY32W module = { 0 };
module.dwSize = sizeof(MODULEENTRY32W);
g_modules = sentry_value_new_list();
if (Module32FirstW(snapshot, &module)) {
do {
HMODULE handle = LoadLibraryExW(
module.szExePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
MEMORY_BASIC_INFORMATION vmem_info = { 0 };
if (handle
&& sizeof(vmem_info)
== VirtualQuery(
module.modBaseAddr, &vmem_info, sizeof(vmem_info))
&& vmem_info.State == MEM_COMMIT) {
sentry_value_t rv = sentry_value_new_object();
sentry_value_set_by_key(
rv, "type", sentry_value_new_string("pe"));
sentry_value_set_by_key(rv, "image_addr",
sentry__value_new_addr((uint64_t)module.modBaseAddr));
sentry_value_set_by_key(rv, "image_size",
sentry_value_new_int32((int32_t)module.modBaseSize));
sentry_value_set_by_key(rv, "code_file",
sentry__value_new_string_from_wstr(module.szExePath));
extract_pdb_info((uintptr_t)module.modBaseAddr, rv);
sentry_value_append(g_modules, rv);
}
FreeLibrary(handle);
} while (Module32NextW(snapshot, &module));
}
结合堆栈显示的 dll 信息,
(No symbol) [nvd3d9wrap.dll 0x000024DD]
问题可能在 sentry 调用 LoadLibraryExW 加载 nvd3d9wrap.dll 时崩溃了,我们尝试在遍历 dll 时跳过它,并在测试机器上测试,发现仍然闪退
那问题出在哪里呢?
google 了一下,看是否有类似案例,查到了一个 bug 反馈
- https://bugzilla.mozilla.org/show_bug.cgi?id=1607574
关键部分:

sentry 加载 detoured.dll 时调到了映射地址,在该地址上继续加载 nvd3d9wrap.dll,从而发生了崩溃
对此,我们尝试跳过 detoured.dll 的加载,程序没闪退
解决方案:
1. 只对 win7 之前的 windows 机器(包括 win7)做判断
2. 如果机器加载到 detoured.dll,则跳过该 dll 的加载
代码:
#define STATUS_SUCCESS (0x00000000) typedef NTSTATUS (WINAPI *RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); bool GetRealOSVersion(RTL_OSVERSIONINFOW *provi) { HMODULE hMod = GetModuleHandleW(L"ntdll.dll"); if (hMod) { RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)GetProcAddress(hMod, "RtlGetVersion"); if (fxPtr) { if (STATUS_SUCCESS == fxPtr(provi)) { return true; } } } return false; } bool IsWin8OrGreater() { bool is_win7_or_greater = true; bool is_win8_or_greater = false; OSVERSIONINFOEX ver = {0}; ver.dwOSVersionInfoSize = sizeof(ver); if (VerifyVersionInfo(&ver, VER_MAJORVERSION | VER_MINORVERSION | VER_PRODUCT_TYPE, NULL)) { is_win7_or_greater = (ver.dwMajorVersion > 6) || ((ver.dwMajorVersion == 6) && (ver.dwMinorVersion >= 1)); is_win8_or_greater = (ver.dwMajorVersion > 6) || ((ver.dwMajorVersion == 6) && (ver.dwMinorVersion > 1)); } if (is_win7_or_greater) { RTL_OSVERSIONINFOW rovi = {0}; rovi.dwOSVersionInfoSize = sizeof(rovi); if (GetRealOSVersion(&rovi)) { is_win8_or_greater = (rovi.dwMajorVersion > 6) || ((rovi.dwMajorVersion == 6) && (rovi.dwMinorVersion > 1)); } } return is_win8_or_greater; } static void load_modules(void) { HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()); MODULEENTRY32W module = {0}; module.dwSize = sizeof(MODULEENTRY32W); g_modules = sentry_value_new_list(); /* 部分 win7 电脑在加载 nvd3d9wrap.dll 会出现闪退,故跳过该部分代码 */ /* https://bugzilla.mozilla.org/show_bug.cgi?id=1607574 */ bool is_win8_or_later = IsWin8OrGreater(); if (Module32FirstW(snapshot, &module)) { do { if (!is_win8_or_later) { wchar_t *wstr_copy; if (_wcslwr_s(wstr_copy = _wcsdup(module.szModule), wcslen(module.szModule) + 1) == 0) { if (wcscmp(wstr_copy, L"detoured.dll") == 0) { free(wstr_copy); continue; } free(wstr_copy); } } HMODULE handle = LoadLibraryExW( module.szExePath, NULL, LOAD_LIBRARY_AS_DATAFILE); MEMORY_BASIC_INFORMATION vmem_info = { 0 }; ...

浙公网安备 33010602011771号