UB由于allocaArray自动清理还是没有?

问题描述 投票:1回答:1

在我的代码中,我有这个函数,似乎工作得很好。

-- 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数组的辅助函数,例如f可能是 vkEnumeratePhysicalDevices(物理设备))

当我回顾我的代码时,我注意到它返回resArray(从 allocaArray 的描述来看,似乎只有在内部 lambda 中才有效)给它的调用者。在C语言中,这样的代码是未定义的行为。我的直觉是正确的吗,还是有更多的事情发生?毕竟我还没有注意到任何崩溃:)

haskell memory-management monads haskell-ffi
1个回答
2
投票

能用当然不能证明它是正确的,事实上这个函数确实是非常错误的。

alloca以及 allocaArray,将分配一个Haskell MutableByteArray# 将其转换为一个指针。对该指针进行操作,然后用一个特殊的 touch# 函数。问题是,一旦你失去了对实际的 MutableByteArray#这就是当你退出 alloca,GC将清理它和 Ptr a 指向该数组的指针将不再有效。所以,如果你继续向那个指针读或写东西 Ptr a 退回后 retrieveVulkanArray 你正在向内存中读入可以被其他东西使用的内容,并且现在有发生segfault的真正危险,以及随之而来的各种其他安全漏洞。

正确的方式应该是。

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 已不再使用。

© www.soinside.com 2019 - 2024. All rights reserved.