【问题标题】:UB due to allocaArray automatic cleanup or not?UB是否由于allocaArray自动清理?
【发布时间】:2020-05-09 09:09:13
【问题描述】:

我的代码中有这个功能,它似乎工作得很好:

-- 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 中,这样的代码将是未定义的行为。我的直觉在这里是正确的还是有更多的事情发生?毕竟我还没有注意到任何崩溃:)

【问题讨论】:

  • 我认为这是 UB。您正在返回一个指向内存 (resArray) 的指针。您可以使用非“alloca”分配函数并让调用者在正确的时间释放它。 (或者也许是一个外部指针)
  • 我是这么认为的。我想没有访问冲突可以解释为 GC 直到一段时间后才真正释放内存?
  • 很难说。内存在被释放时可能不会立即返回给操作系统,因此它保持可用但可以被其他分配器重用。这也发生在许多 C 实现中:如果你 free() 一个指针并立即访问它,它可能不会导致访问冲突,因为内存并不总是返回给操作系统,特别是如果它是一个小内存区域。
  • 您应该始终在您的代码中添加函数类型签名,以便其他人阅读您的代码,并在您将来阅读自己的代码时为您自己着想。 -- type declaration just for reference, i don't have it in my actual code

标签: haskell memory-management monads haskell-ffi


【解决方案1】:

它确实有效并不能证明它是正确的,事实上这个功能确实是非常错误的。

alloca,以及allocaArray,将分配一个Haskell MutableByteArray# 将其转换为一个指针。对该指针进行操作,然后使用特殊的touch# 函数确保该数组仍然存在。问题是,一旦您失去对实际MutableByteArray# 的引用,这就是您退出alloca 时发生的情况,GC 将清理它并且指向该数组的Ptr a 将不再有效。因此,如果您在从 retrieveVulkanArray 返回该指针 Ptr a 后继续读取或写入该指针,则您正在读取/写入可由其他东西使用的内存,并且现在处于段错误和各种其他安全性的真正危险中随之而来的漏洞。

正确的方法是:

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

【讨论】:

  • 谢谢,由于某种原因,我之前没有注意到ForeignPtr 输入。以# 结尾的函数是什么?
  • 不客气。以MagicHash 结尾的函数和类型被称为ghc 原始函数,例如touch#downloads.haskell.org/~ghc/8.10.1/docs/html/libraries/… 这些是核心部分,用于实现base 的其余部分以及Haskell 中的大部分功能
猜你喜欢
  • 2017-05-18
  • 2019-05-25
  • 2012-05-12
  • 1970-01-01
  • 2021-10-07
  • 2012-10-16
  • 2012-12-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多