UB 是否由于 allocaArray 自动清理?
UB due to allocaArray automatic cleanup or not?
我的代码中有这个功能似乎工作得很好:
-- type declaration just for reference, i don't have it in my actual code
retrieveVulkanArray :: Storable a => (Ptr Word32 -> Ptr a -> IO b) -> IO (Ptr a, Int)
retrieveVulkanArray' f =
alloca $ \arrCount -> do
f arrCount vkNullPtr
arrCount' <- fromIntegral <$> peek arrCount
allocaArray arrCount' $ \resArray -> do
f arrCount resArray
pure (resArray, arrCount')
(对于上下文,这是从 Vulkan API 获取 FFI 数组的辅助函数 API,例如 f 可能是 vkEnumeratePhysicalDevices)
当我检查我的代码时,我注意到它 returns resArray(从 allocaArray 的描述来看,它似乎只在内部 lambda 中有效)到它的调用者。在 C 中,这样的代码将是未定义的行为。我的直觉在这里是正确的还是还有其他事情发生?毕竟我还没有注意到任何崩溃:)
能用当然不能证明它是正确的,实际上这个功能确实是非常错误的。
alloca
,以及allocaArray
,都会分配一个Haskell,MutableByteArray#
将其转换为指针。对该指针进行操作,然后使用特殊的 touch#
函数确保该数组仍然存在。问题是一旦你失去对实际 MutableByteArray#
的引用,当你退出 alloca
时会发生这种情况,GC 将清理它并且指向该数组的 Ptr a
将不再有效。因此,如果您在 return 从 retrieveVulkanArray
之后继续读取或写入该指针 Ptr a
,那么您 reading/writing 进入了可以被其他东西使用的内存,并且现在是真实的段错误的危险以及随之而来的各种其他安全漏洞。
正确的方法是:
retrieveVulkanArray
:: Storable a => (Ptr Word32 -> Ptr a -> IO b) -> IO (ForeignPtr a, Int)
retrieveVulkanArray' f =
alloca $ \arrCount -> do
f arrCount vkNullPtr
arrCount' <- fromIntegral <$> peek arrCount
resArray <- mallocForeignPtrArray arrCount'
_ <- withForeignPtr resArray (f arrCount)
pure (resArray, arrCount')
ForeignPtr a
确保您可以在需要时对原始 Ptr a
进行操作,而不必担心它指向的内存会被释放,直到 ForeignPtr
不再使用。
我的代码中有这个功能似乎工作得很好:
-- type declaration just for reference, i don't have it in my actual code
retrieveVulkanArray :: Storable a => (Ptr Word32 -> Ptr a -> IO b) -> IO (Ptr a, Int)
retrieveVulkanArray' f =
alloca $ \arrCount -> do
f arrCount vkNullPtr
arrCount' <- fromIntegral <$> peek arrCount
allocaArray arrCount' $ \resArray -> do
f arrCount resArray
pure (resArray, arrCount')
(对于上下文,这是从 Vulkan API 获取 FFI 数组的辅助函数 API,例如 f 可能是 vkEnumeratePhysicalDevices)
当我检查我的代码时,我注意到它 returns resArray(从 allocaArray 的描述来看,它似乎只在内部 lambda 中有效)到它的调用者。在 C 中,这样的代码将是未定义的行为。我的直觉在这里是正确的还是还有其他事情发生?毕竟我还没有注意到任何崩溃:)
能用当然不能证明它是正确的,实际上这个功能确实是非常错误的。
alloca
,以及allocaArray
,都会分配一个Haskell,MutableByteArray#
将其转换为指针。对该指针进行操作,然后使用特殊的 touch#
函数确保该数组仍然存在。问题是一旦你失去对实际 MutableByteArray#
的引用,当你退出 alloca
时会发生这种情况,GC 将清理它并且指向该数组的 Ptr a
将不再有效。因此,如果您在 return 从 retrieveVulkanArray
之后继续读取或写入该指针 Ptr a
,那么您 reading/writing 进入了可以被其他东西使用的内存,并且现在是真实的段错误的危险以及随之而来的各种其他安全漏洞。
正确的方法是:
retrieveVulkanArray
:: Storable a => (Ptr Word32 -> Ptr a -> IO b) -> IO (ForeignPtr a, Int)
retrieveVulkanArray' f =
alloca $ \arrCount -> do
f arrCount vkNullPtr
arrCount' <- fromIntegral <$> peek arrCount
resArray <- mallocForeignPtrArray arrCount'
_ <- withForeignPtr resArray (f arrCount)
pure (resArray, arrCount')
ForeignPtr a
确保您可以在需要时对原始 Ptr a
进行操作,而不必担心它指向的内存会被释放,直到 ForeignPtr
不再使用。