google / mozc

Mozc - a Japanese Input Method Editor designed for multi-platform
Other
2.32k stars 330 forks source link

Applications that do not explicitly call `CoInitialize` may crash when loading or enabling Mozc in Windows #856

Closed yukawa closed 6 months ago

yukawa commented 6 months ago

Description

When an application that does not explicitly call CoInitialize try to load Mozc's dll (or the user tries to enable Mozc), the application may crash due to system's unexpectedly unloading Mozc's dll.

Steps to reproduce

Steps to reproduce the behavior:

  1. Install Mozc
  2. Build and launch the following app.
  3. Select Mozc
#include <windows.h>

int WINAPI wWinMain(HINSTANCE instance_handle, HINSTANCE prev_instance_handle,
                    PWSTR command_line, int command_show) {
  const wchar_t window_class_name[] = L"Test Window Class";
  {
    const WNDCLASS wc = {
        .lpfnWndProc = [](HWND window_handle, UINT message,
                          auto... params) -> LRESULT {
          if (message == WM_DESTROY) {
            ::PostQuitMessage(0);
            return 0;
          }
          return ::DefWindowProc(window_handle, message, params...);
        },
        .hInstance = instance_handle,
        .hbrBackground = (HBRUSH)(COLOR_WINDOW + 1),
        .lpszClassName = window_class_name,
    };
    ::RegisterClass(&wc);
  }

  const HWND window_handle = ::CreateWindowEx(
      0, window_class_name, L"Window Title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
      CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, nullptr, nullptr,
      instance_handle, nullptr);

  if (window_handle == nullptr) {
    return 0;
  }

  ::ShowWindow(window_handle, command_show);

  while (true) {
    MSG message = {};
    if (::GetMessage(&message, nullptr, 0, 0) <= 0) {
      break;
    }
    ::TranslateMessage(&message);
    ::DispatchMessage(&message);
  }

  return 0;
}

Expected behavior

The test app does not crash at the step 3.

Actual behavior

The test app crashes at the step 3.

Version or commit-id

68fea8a05c189a7ff166b36b07eac434569f4cd2

2.29.5288.100 and later versions are considered to be affected.

Environment

Additional context

What's actually happening

  1. When an app creates a Win32 window without initializing COM, CUAS (Cicero Unaware Application Support) automatically calls CoInitialize with also installing CoRegisterInitializeSpy, because TSF is built on top of COM.
  2. When Mozc's TIP is activated, calling mozc::Logging::InitLogStream eventually triggers absl::LocalTimeZone, which now calls RoInitialize(RO_INIT_MULTITHREADED).
  3. CUAS detects that RoInitialize(RO_INIT_MULTITHREADED) gets called through CoRegisterInitializeSpy and tries to clean up its default COM apartment by explicitly calling CoUninitialize.
  4. Calling CoUninitialize forcefully unloads Mozc's TIP DLLs (even if Mozc's DllCanUnloadNow returned S_FALSE), while TipTextServiceImpl::ActivateEx is still in the call stack.

Here is the callstack when CUAS internally calls CoInitialize at the step 1.

combase.dll!CoInitializeEx(void * pMalloc, unsigned long flags) Line 3728   C++
imm32.dll!CtfImmCoInitialize()  Unknown
imm32.dll!CanCreateThreadMgr()  Unknown
imm32.dll!CtfImmTIMActivate()   Unknown
imm32.dll!ImmSetActiveContext() Unknown
user32.dll!FocusSetIMCContext() Unknown
user32.dll!ImeSystemHandler()   Unknown
user32.dll!ImeWndProcWorker()   Unknown
user32.dll!ImeWndProcW(struct HWND__ *,unsigned int,unsigned __int64,__int64)   Unknown
user32.dll!UserCallWinProcCheckWow()    Unknown
user32.dll!DispatchClientMessage()  Unknown
user32.dll!__fnDWORD()  Unknown
ntdll.dll!KiUserCallbackDispatcherContinue()    Unknown
win32u.dll!NtUserMessageCall()  Unknown
user32.dll!RealDefWindowProcWorker()    Unknown
user32.dll!DefWindowProcW() Unknown
SimpleWindow.exe!`wWinMain'::`3'::<lambda_1>::operator()<unsigned __int64,__int64>(HWND__ * window_handle, unsigned int message, unsigned __int64 <params_0>, __int64 <params_1>) Line 15   C++
SimpleWindow.exe!`wWinMain'::`3'::<lambda_1>::<lambda_invoker_cdecl><unsigned __int64,__int64>(HWND__ * window_handle, unsigned int message, unsigned __int64 <params_0>, __int64 <params_1>) Line 15   C++
user32.dll!UserCallWinProcCheckWow()    Unknown
user32.dll!DispatchClientMessage()  Unknown
user32.dll!__fnDWORD()  Unknown
ntdll.dll!KiUserCallbackDispatcherContinue()    Unknown
win32u.dll!NtUserShowWindow()   Unknown
SimpleWindow.exe!wWinMain(HINSTANCE__ * instance_handle, HINSTANCE__ * prev_instance_handle, wchar_t * command_line, int command_show) Line 34  C++
SimpleWindow.exe!invoke_main() Line 123 C++
SimpleWindow.exe!__scrt_common_main_seh() Line 288  C++
SimpleWindow.exe!__scrt_common_main() Line 331  C++
SimpleWindow.exe!wWinMainCRTStartup(void * __formal) Line 17    C++
kernel32.dll!BaseThreadInitThunk()  Unknown
ntdll.dll!RtlUserThreadStart()  Unknown

Here is the relevant code at the step 2. https://github.com/google/mozc/blob/68fea8a05c189a7ff166b36b07eac434569f4cd2/src/win32/tip/tip_text_service.cc#L512-L513

Here is the call stack at the step 3.

mozc_tip64.dll!`anonymous namespace'::OnDllProcessDetach(HINSTANCE__ * instance, bool process_shutdown) Line 94 C++
mozc_tip64.dll!DllMain(HINSTANCE__ * instance, unsigned long reason, void * reserved) Line 167  C++
mozc_tip64.dll!dllmain_dispatch(HINSTANCE__ * const instance, const unsigned long reason, void * const reserved) Line 281   C++
mozc_tip64.dll!_DllMainCRTStartup(HINSTANCE__ * const instance, const unsigned long reason, void * const reserved) Line 335 C++
ntdll.dll!LdrpCallInitRoutine() Unknown
ntdll.dll!LdrpProcessDetachNode()   Unknown
ntdll.dll!LdrpUnloadNode()  Unknown
ntdll.dll!LdrpDecrementModuleLoadCountEx()  Unknown
ntdll.dll!LdrUnloadDll()    Unknown
KernelBase.dll!FreeLibrary()    Unknown
combase.dll!CClassCache::CDllPathEntry::CFinishObject::Finish() Line 2781   C++
combase.dll!CClassCache::CFinishComposite::Finish() Line 2895   C++
combase.dll!CClassCache::CleanUpDllsForProcess() Line 5849  C++
[Inline Frame] combase.dll!CCCleanUpDllsForProcess() Line 7580  C++
combase.dll!ProcessUninitialize() Line 2175 C++
combase.dll!DecrementProcessInitializeCount() Line 983  C++
combase.dll!wCoUninitialize(COleTls & Tls, int fHostThread) Line 4051   C++
combase.dll!CoUninitialize() Line 3881  C++
imm32.dll!CtfImmCoUninitialize()    Unknown
imm32.dll!ISPY_PreInitialize()  Unknown
[Inline Frame] combase.dll!NotifyInitializeSpies(int) Line 3021 C++
combase.dll!_CoInitializeEx(unsigned long flags) Line 3585  C++
combase.dll!RoInitialize(RO_INIT_TYPE initType) Line 357    C++
mozc_tip64.dll!absl::lts_20230802::time_internal::cctz::local_time_zone() Line 301  C++
mozc_tip64.dll!absl::lts_20230802::LocalTimeZone() Line 1262    C++
mozc_tip64.dll!mozc::`anonymous namespace'::ClockImpl::ClockImpl() Line 49  C++
mozc_tip64.dll!mozc::SingletonMockable<mozc::ClockInterface,mozc::`anonymous namespace'::ClockImpl>::Get() Line 113 C++
mozc_tip64.dll!mozc::Clock::GetAbslTime() Line 74   C++
mozc_tip64.dll!mozc::Logging::GetLogMessageHeader() Line 111    C++
mozc_tip64.dll!mozc::Logging::InitLogStream(const std::string & log_file_path) Line 310 C++
mozc_tip64.dll!mozc::win32::tsf::`anonymous namespace'::TipTextServiceImpl::ActivateEx(ITfThreadMgr * thread_mgr, unsigned long client_id, unsigned long flags) Line 512    C++
msctf.dll!CTip::Activate(struct ITfThreadMgr *,unsigned long)   Unknown
msctf.dll!CThreadInputMgr::_ActivateTip(class CTip *)   Unknown
msctf.dll!CThreadInputMgr::ActivateInputProfile()   Unknown
msctf.dll!CThreadInputMgr::OnActiveProfileChange()  Unknown
msctf.dll!CInputProfileManager::ActivateProfile(class CInputLanguage *,struct CInputProfile *)  Unknown
msctf.dll!CInputProfileManager::OnCiceroEvent() Unknown
msctf.dll!WinEventProc(struct HWINEVENTHOOK__ *,unsigned long,struct HWND__ *,long,long,unsigned long,unsigned long)    Unknown
user32.dll!__ClientCallWinEventProc()   Unknown
ntdll.dll!KiUserCallbackDispatcherContinue()    Unknown
win32u.dll!NtUserActivateKeyboardLayout()   Unknown
msctf.dll!CInputProfileManager::ActivateHKL(struct HKL__ *) Unknown
msctf.dll!CInputProfileManager::ActivateProfile(class CInputLanguage *,struct CInputProfile *)  Unknown
msctf.dll!CInputProfileManager::ActivateProfile()   Unknown
msctf.dll!_GetMsgHook(struct SYSTHREAD *,unsigned __int64,__int64)  Unknown
msctf.dll!TF_Notify()   Unknown
user32.dll!CtfHookProcWorker(int,unsigned __int64,__int64,unsigned __int64) Unknown
user32.dll!CallHookWithSEH(struct _GENERICHOOKHEADER *,void *,unsigned long *,unsigned __int64) Unknown
user32.dll!__fnHkINLPMSG()  Unknown
ntdll.dll!KiUserCallbackDispatcherContinue()    Unknown
win32u.dll!NtUserGetMessage()   Unknown
user32.dll!GetMessageW()    Unknown
SimpleWindow.exe!wWinMain(HINSTANCE__ * instance_handle, HINSTANCE__ * prev_instance_handle, wchar_t * command_line, int command_show) Line 36  C++
SimpleWindow.exe!invoke_main() Line 123 C++
SimpleWindow.exe!__scrt_common_main_seh() Line 288  C++
SimpleWindow.exe!__scrt_common_main() Line 331  C++
SimpleWindow.exe!wWinMainCRTStartup(void * __formal) Line 17    C++
kernel32.dll!BaseThreadInitThunk()  Unknown
ntdll.dll!RtlUserThreadStart()  Unknown

The code in question originates from google/cctz#242, and Mozc started using it when updating abseil-cpp to 20230802.1 LTS for #841 with 6c920a9c2476059607d4c5c3fe3e8f241025850a.