Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
yukawa opened this issue Dec 22, 2023 · 1 comment

Comments

@yukawa
Copy link
Collaborator

yukawa commented Dec 22, 2023

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

68fea8a

2.29.5288.100 and later versions are considered to be affected.

Environment

  • OS: Windows 11 22H2

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.

Logging::InitLogStream(
FileUtil::JoinPath(SystemUtil::GetLoggingDirectory(), kLogFileName));

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 6c920a9.

coooooooozy pushed a commit to coooooooozy/mozc that referenced this issue Dec 29, 2023
With google/cctz#242, absl::LocalTimeZone() now calls
RoInitialize(RO_INIT_MULTITHREADED), which may conflict with CUAS' COM
apartment handling when called from Mozc's TIP DLLs.

Note that in the current code ClockImpl::timezone_ is always overridden
with absl::FixedTimeZone on ChromeOS and Windows, which means that
calling absl::LocalTimeZone() on such platforms.

Closes google#856.

PiperOrigin-RevId: 593601170
yukawa added a commit to yukawa/mozc that referenced this issue Oct 14, 2024
This commit affects only mozc_tip{32,64}.dll build with debug mode.
There must be no behavior change in release build.

Currently 'Mozc_tsf_ui.log' is created only when NDEBUG is defined.
With this commit we stop creating it even when NDEBUG is defined.
This addresses a crash issue in debug builds discussed in google#1077.

See google#856 about why absl::LocalTimeZone cannot be used in Windows right
now.

Note that this commit may also help us diagnose google#1076, where
  Windows.Storage.OneCore.dll
looks to be intercepting certain Win32 file I/O API calls in
AppContainer processes then trigger RoInitialize as needed.
Creating 'Mozc_tsf_ui.log' only in debug builds can make our debugging
more complicated.
yukawa added a commit to yukawa/mozc that referenced this issue Oct 14, 2024
This commit affects only mozc_tip{32,64}.dll build with debug mode.
There must be no behavior change in release build.

Currently 'Mozc_tsf_ui.log' is created only when NDEBUG is defined.
With this commit we stop creating it even when NDEBUG is defined.
This addresses a crash issue in debug builds discussed in google#1077.

See google#856 about why absl::LocalTimeZone cannot be used in Windows right
now.

Note that this commit may also help us diagnose google#1076, where
  Windows.Storage.OneCore.dll
looks to be intercepting certain Win32 file I/O API calls in
AppContainer processes then trigger RoInitialize as needed.
Creating 'Mozc_tsf_ui.log' only in debug builds can make our debugging
more complicated.
yukawa added a commit to yukawa/mozc that referenced this issue Oct 14, 2024
This attemps to avoid the deadlock issue discussed in google#1076.

From what I can observe on Windows 11 23H2 (22631.4317), it looks to be
dangerous for us to call File I/O Win32 APIs such as CreateFile from
AppContainer processes unless we are confident that the target file/dir
is accessible to the AppContainer process. When such an access is not
allowed at the ACL level, the debugger shows that

  Windows.Storage.OneCore.dll

tries to forward it to a broker process (e.g. via BrokeredCreateFile2).
The issue is that Windows.Storage.OneCore.dll is a WinRT component,
which means that just calling CreateFile() results in dyanamically
invoking RoInitialize() internally, which can confuse TSF runtime as we
have already seen in google#856.

To summarize, the safest option is to ensure that the target file/dir is
always accessible to AppContainer so that

  Windows.Storage.OneCore.dll

will not be involved if the file/dir is opened with CreateFile() from
MozcTip{32,64}.dll.

If this commit is not sufficient to address google#1076, we then need to take
care of other File I/O APIs such as CreateDirectoryW() and
GetFileAttributes().
yukawa added a commit to yukawa/mozc that referenced this issue Oct 14, 2024
This attempts to avoid the deadlock issue discussed in google#1076.

From what I can observe on Windows 11 23H2 (22631.4317), it looks to be
dangerous for us to call File I/O Win32 APIs such as CreateFile from
AppContainer processes unless we are confident that the target file/dir
is accessible to the AppContainer process. When such an access is not
allowed at the ACL level, the debugger shows that

  Windows.Storage.OneCore.dll

tries to forward it to a broker process (e.g. via BrokeredCreateFile2).
The issue is that Windows.Storage.OneCore.dll is a WinRT component,
which means that just calling CreateFile() results in dynamically
invoking RoInitialize() internally, which can confuse TSF runtime as we
have already seen in google#856.

To summarize, the safest option is to ensure that the target file/dir is
always accessible to AppContainer so that

  Windows.Storage.OneCore.dll

will not be involved if the file/dir is opened with CreateFile() from
MozcTip{32,64}.dll.

If this commit is not sufficient to address google#1076, we then need to take
care of other File I/O APIs such as CreateDirectoryW() and
GetFileAttributes().
hiroyuki-komatsu pushed a commit that referenced this issue Oct 15, 2024
This commit affects only mozc_tip{32,64}.dll build with debug mode.
There must be no behavior change in release build.

Currently 'Mozc_tsf_ui.log' is created only when NDEBUG is defined.
With this commit we stop creating it even when NDEBUG is defined.
This addresses a crash issue in debug builds discussed in #1077.

See #856 about why absl::LocalTimeZone cannot be used in Windows right
now.

Note that this commit may also help us diagnose #1076, where
  Windows.Storage.OneCore.dll
looks to be intercepting certain Win32 file I/O API calls in
AppContainer processes then trigger RoInitialize as needed.
Creating 'Mozc_tsf_ui.log' only in debug builds can make our debugging
more complicated.

PiperOrigin-RevId: 686025719
@malkia
Copy link

malkia commented Nov 25, 2024

I havent' used mozc, but was looking for other projects on Windows using absl's LocalTimeZone. The current implementaiton (on Windows 10 +) is slow - abseil/abseil-cpp#1760 (comment) - Not sure how this can be tackled better, just putting here if someone else has run into this. In my case it's gRPC for Windows.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants