坑三:如何避免Double Free
时间:2022-04-28 21:12:01 | 来源:行业动态
时间:2022-04-28 21:12:01 来源:行业动态
Standard Marshalling Service/Interop marshaller总是试图释放Unmanaged侧代码分配的内存9,这会带来Double Free的问题,如果碰到这种问题,程序就会直接崩溃。
引用资料中举了以下例子:
BSTRMethodOne(BSTRb){returnb;}
如果这段代码直接从Unmanaged侧DLL中直接执行,不会发生任何额外的内存释放;但是当你从Managed侧调用这个方法时,b会被释放两次。
而更让人抓狂的是,并没有相应的信息提示究竟是哪个指针,哪个字段被Double Free了,你唯一能做的就是一点点加代码来验证自己猜测。所以,严格来说,并没有一个万无一失的方案来避免Double Free,你唯一能做的就是通过测试来验证结果(有点盲拧魔方的味道了)。
有两个基本的方法来解决Double Free的问题:
1、按照官方文档建议,在Unmanaged侧通过使用CoTaskMemAlloc来分配内存,通过此种方法分配的内存,除非显式调用了CoTaskMemFree方法(在Unmanaged侧或者Managed侧均可以调用),Interop Marshaller会严格保证不去释放该内存。使用这种方法可以灵活的在任意一侧分配内存,并在合适的时候在另一侧释放内存。
2、但上面这种方法貌似仅适用于Windows平台,在macOS下没有办法使用(需要引用win32base.dll相关实现)。在macOS下仅能通过在Mananged侧调用Marshal.AllocCoTaskMem()方法分配内存,并通过Marshal.FreeCoTaskMem()来在同一侧进行释放(按照此方法分配的内存指针传入Unmanaged侧后,不要进行任何释放即可)。另外有一个不太可靠的workaround是:在Unmanaged一侧创建的内存指针尽量通过IntPtr传递,并在可能的时候将对象中一些指针类型的属性值置空,以避免Double Free的发生。