Skip to content

`withForeignPtr` is unsafe

Similarly to #14346 (closed), the following program triggers a segfault:

import Control.Concurrent
import Control.Monad
import Data.Word
import Foreign.ForeignPtr
import Foreign.Storable
import System.Mem

main :: IO ()
main = do
    fp <- mallocForeignPtrBytes 40000
    _ <- forkIO $ do
       -- withForeignPtr is supposed to keep `p` alive but it wasn't the case
       withForeignPtr fp $ \p -> do
         forever $ do
           performGC
           -- We need to perform at least two operations here on the pointer.
           -- By doing so, the loop worker takes an Addr# as a parameter.
           -- Otherwise `byteArrayContents` is used here (instead of in the
           -- caller) and the ByteArray used by the ForeignPtr implementation is
           -- kept alive even with `touch#` removed.
           poke p (0xDEADBEEF :: Word32)
           poke p (0x12345678 :: Word32)
           --touchForeignPtr fp
           yield
    threadDelay 1000

withForeignPtr doesn't keep p alive because touch# becomes unreachable due to the use of forever.

Making withForeignPtr NOINLINE fixes the bug.

Edited by Ömer Sinan Ağacan
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information