記 dotnet 8.0.4 修復的 WPF 的觸摸模塊安全問題

本文記錄 dotnet 8.0.4 版本修復的 WPF 的觸摸模塊安全問題,此問題影響所有的 .NET 版本,修復方法是更新 SDK 和運行時

宣佈安全漏洞地址: https://github.com/dotnet/wpf/issues/9003

安全漏洞宣佈地址: https://github.com/dotnet/announcements/issues/303

漏洞代號: CVE-2024-21409

核心更改: https://github.com/dotnet/wpf/commit/c15b5c68cd74ae28bc99af539d05880658c45024

影響模塊: 觸摸模塊

開發者側的修復方法: 升級 .NET SDK 或運行時版本,攜帶此更新的版本分別如下

  • .NET 6 : 6.0.29
  • .NET 7 : 7.0.18
  • .NET 8 : 8.0.4

微軟系統更新 Microsoft Update 將會自動推送以上版本的 .NET Core 更新,以及相應的 .NET Framework 質量更新

修復的原因和修復的方法請參閱核心請參閱核心更改裏面的註釋,註釋內容如下

    // The CComObject<CPimcManager> destructor is the only function which calls into this
    // FinalRelease code.
    //
    // In all successful usage of CPimcManager: 1) Managed WPF code uses CoCreateInstance
    // to acquire an IPimcManager2 interface to a brand-new CPimcManager instance (created by
    // the ATL CComCreator<T>::CreateInstance machinery), meaning FinalConstruct by-definition
    // completes successfully, meaning "m_managerLock" is therefore guaranteed to be locked;
    // 2) Managed WPF code then runs through its full end-to-end usage of the CPimcManager
    // object (generally managed by the code in PenThreadWorker.cs); 3) When/if the managed WPF
    // code determines that the CPimcManager object is no longer needed, it sends a
    // RELEASE_MANAGER_EXT message (see UnsafeNativeMethods.ReleaseManagerExternalLock()) which
    // unlocks "m_managerLock"; 4) Now that it is unlocked, the CComObject<CPimcManager> object
    // can be destroyed when/if its refcount drops to zero, and this FinalRelease function will
    // run at that time.
    //
    // So in all successful usage cases, it is guaranteed that "m_managerLock" is already
    // unlocked when this code runs (because if it was still locked, the lock itself would have
    // prevented the refcount from reaching zero, and would have prevented this function from
    // ever running).
    //
    // That said, in unsuccessful usage cases, the ATL CComCreator<T>::CreateInstance machinery
    // can fail, meaning it will destroy the brand-new CPimcManager instance before returning
    // an error back to the CreateInstance caller.  Destroying the brand-new instance triggers
    // the CComObject<CPimcManager> destructor and therefore calls into this function during
    // the CComCreator<T>::CreateInstance operation itself.
    //
    // The final step in CComCreator<T>::CreateInstance is a QI which queries the newly-created
    // object for whatever interface has been requested by the caller.  This operation is the
    // main way that CComCreator<T>::CreateInstance can fail.  For example, this QI is
    // guaranteed to fail whenever the CoCreateInstance caller targets the CPimcManager CLSID
    // but passes in a "random" IID that has nothing to do with IPimcManager2 or anything else
    // that CPimcManager implements.
    //
    // (In CPimcManager construction, outside of pathological cases (e.g., where a small heap
    // allocation in OS code fails due to out-of-memory), there are no other known ways that
    // the CComCreator<T>::CreateInstance sequence can fail; so the QI failure is the only
    // failure mode that is known to be of general interest.)
    //
    // The QI failure can only occur after the preceding FinalConstruct call has completed
    // successfully (since any FinalConstruct failure would have caused
    // CComCreator<T>::CreateInstance to abort without ever trying the QI); since
    // CPimcManager::FinalConstruct always locks the "m_managerLock", this implies that the
    // "m_managerLock" is guaranteed to be locked when this code runs (which is exactly
    // opposite to what happens in all successful usage cases as discussed above).
    //
    // In this case, it is crucial to unlock "m_managerLock" before allowing this CPimcManager
    // object to be destroyed.  Without the unlock, this CPimcManager object would be destroyed
    // while the associated CStdIdentity in the OS code still holds a reference to it; during
    // any future apartment unload, the OS code would release this reference, and the release
    // would be a use-after-free at that point.
    //
    // Note that the crucial unlock causes overactive ATL debug asserts to fire if a chk build
    // of this DLL is used; specifically:
    //
    //    - The code in the CComObject<CPimcManager> destructor always stomps the refcount to
    //      0xc0000001 (i.e., "-(LONG_MAX/2)"), meaning this CPimcManager object's refcount is
    //      always 0xc0000001 when this code runs; unlocking "m_managerLock" will cause the refcount
    //      to drop by one (because, as discussed above, the crucial operation which prevents
    //      use-after-free problems will release the associated CStdIdentity's reference to this
    //      CPimcManager object, and in this way releases the reference that was added when
    //      "managerLock" was locked during FinalConstruct); as a result, unlocking "m_managerLock"
    //      will move this CPimcManager object's refcount through a "0xc0000001 -> 0xc0000000"
    //      transition.
    //
    //    - Both of the CComObjectRootEx<T>::InternalRelease specializations contain debug asserts
    //      which will fire whenever the refcount drops below 0xc0000001, so this transition always
    //      triggers a debug assert when using a chk build of this DLL.
    //
    //    - That said, all evidence strongly suggests that this is just an overactive assert in
    //      the ATL code (probably just indicating that it is rare for FinalConstruct to add
    //      "self-references" like it does for CPimcManager (since these self-references generally
    //      prevent the server object from being destroyed unless a manual action like the
    //      RELEASE_MANAGER_EXT message is taken later on), meaning it is rare to have a situation
    //      where FinalRelease needs to release self-references that were acquired in
    //      FinalConstruct, meaning this is a rare enough case that the ATL authors either didn't
    //      test it or didn't think it was common enough to warrant adjusting the assert).
    //
    // Since this change is being made in servicing, attempt to change behavior as little as
    // possible in the "successful usage" cases where "m_managerLock" is already unlocked,
    // while still ensuring that FinalRelease will always run the crucial unlock in all
    // "unsuccessful usage" cases.

以下是 new Bing 對上面註釋的解釋

這段代碼註釋討論的是一個關於 CPimcManager 類的析構函數(destructor)的問題。讓我來解釋一下:

  1. 首先,我們有一個 CPimcManager 類,它的析構函數(destructor)被稱爲 FinalRelease
  2. 在成功使用 CPimcManager 的情況下,以下步驟發生:
    • 託管的 WPF 代碼使用 CoCreateInstance 來獲取一個指向全新 CPimcManager 實例的 IPimcManager2 接口(由 ATL CComCreator<T>::CreateInstance 機制創建)。
    • 這意味着 FinalConstruct 已經成功完成,也就是說,“m_managerLock”已經被鎖定。
    • 然後,託管的 WPF 代碼通過發送 RELEASE_MANAGER_EXT 消息(參見 UnsafeNativeMethods.ReleaseManagerExternalLock())來解鎖 “m_managerLock”,表示不再需要 CPimcManager 對象。
    • 現在,“m_managerLock”已經解鎖,CComObject<CPimcManager> 對象可以在其引用計數降至零時被銷燬,此時 FinalRelease 函數將運行。
  3. 因此,在所有成功的使用情況下,當此代碼運行時,“m_managerLock”已經解鎖(因爲如果它仍然被鎖定,鎖本身將阻止引用計數達到零,從而阻止此函數運行)。
  4. 但是,在不成功的使用情況下,ATL CComCreator<T>::CreateInstance 機制可能會失敗,這意味着它將在將錯誤返回給 CreateInstance 調用者之前銷燬全新的 CPimcManager 實例。
  5. 銷燬全新實例會觸發 CComObject<CPimcManager> 析構函數,因此在 CComCreator<T>::CreateInstance操作本身期間會調用此函數。
  6. CComCreator<T>::CreateInstance 的最後一步是查詢新創建的對象,以獲取已重新定義的任何接口。

總之,這段註釋詳細描述了 CPimcManager 類的析構函數在不同使用情況下的行爲和保證。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章