坑三:如何避免Double Free
時間:2022-04-28 21:12:01 | 來源:行業(yè)動態(tài)
時間:2022-04-28 21:12:01 來源:行業(yè)動態(tài)
Standard Marshalling Service/Interop marshaller總是試圖釋放Unmanaged側代碼分配的內(nèi)存9,這會帶來Double Free的問題,如果碰到這種問題,程序就會直接崩潰。
引用資料中舉了以下例子:
BSTRMethodOne(BSTRb){returnb;}
如果這段代碼直接從Unmanaged側DLL中直接執(zhí)行,不會發(fā)生任何額外的內(nèi)存釋放;但是當你從Managed側調用這個方法時,b會被釋放兩次。
而更讓人抓狂的是,并沒有相應的信息提示究竟是哪個指針,哪個字段被Double Free了,你唯一能做的就是一點點加代碼來驗證自己猜測。所以,嚴格來說,并沒有一個萬無一失的方案來避免Double Free,你唯一能做的就是通過測試來驗證結果(有點盲擰魔方的味道了)。
有兩個基本的方法來解決Double Free的問題:
1、按照官方文檔建議,在Unmanaged側通過使用CoTaskMemAlloc來分配內(nèi)存,通過此種方法分配的內(nèi)存,除非顯式調用了CoTaskMemFree方法(在Unmanaged側或者Managed側均可以調用),Interop Marshaller會嚴格保證不去釋放該內(nèi)存。使用這種方法可以靈活的在任意一側分配內(nèi)存,并在合適的時候在另一側釋放內(nèi)存。
2、但上面這種方法貌似僅適用于Windows平臺,在macOS下沒有辦法使用(需要引用win32base.dll相關實現(xiàn))。在macOS下僅能通過在Mananged側調用Marshal.AllocCoTaskMem()方法分配內(nèi)存,并通過Marshal.FreeCoTaskMem()來在同一側進行釋放(按照此方法分配的內(nèi)存指針傳入Unmanaged側后,不要進行任何釋放即可)。另外有一個不太可靠的workaround是:在Unmanaged一側創(chuàng)建的內(nèi)存指針盡量通過IntPtr傳遞,并在可能的時候將對象中一些指針類型的屬性值置空,以避免Double Free的發(fā)生。