sentry 在加载模块时闪退

这是一个很久之前的问题了,今天记录一下,以便遇到同样问题的同学能够看到此文章

崩溃环境:

目前仅收到 windows 7 的部分用户反馈,在程序启动时发生闪退

问题分析:

查看用户提供的日志,可以看见崩溃发生在 sentry 内部

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 在加载模块时闪退

 

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 };
...

 

原文链接: https://www.cnblogs.com/strive-sun/p/16976052.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    sentry 在加载模块时闪退

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/404389

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月25日 下午4:33
下一篇 2023年4月25日 下午4:33

相关推荐