Skip to content

C finalizer of a finalized ForeignPtr may be called again when RTS shuts down

The C finalizer of a ForeignPtr that was explicitly finalized (finalizeForeignPtr) is sometimes called again when the RTS shuts down.

Expected behaviour:

  • The test script (./test.sh) below never stops

Observed behaviour:

  • After a while, the test script stops with an error message indicating double free or memory corruption
$ ./test.sh
[1 of 1] Compiling Main             ( finalizer_hs.hs, finalizer_hs.o )
Linking finalizer_hs ...
*** Error in `./finalizer_hs': double free or corruption (!prev): 0x00000000010b85e0 ***
./test.sh: line 3: 20482 Aborted                 (core dumped) ./finalizer_hs +RTS -N
178

File finalizer_hs.hs

{-# LANGUAGE ForeignFunctionInterface #-}

import Foreign (Ptr, FunPtr, malloc)
import Foreign.ForeignPtr (newForeignPtr, finalizeForeignPtr)
import Control.Monad (forM, forM_)

foreign import ccall "stdlib.h &free" p_free :: FunPtr (Ptr a -> IO ())

main :: IO ()
main = do
    fps <- forM [1 .. 10000] $ \_ -> do
        p <- malloc
        newForeignPtr p_free (p :: Ptr Int)
    forM_ fps finalizeForeignPtr

File test.sh:

ghc finalizer_hs.hs -threaded
CNT=0
while ./finalizer_hs +RTS -N; do CNT=$((CNT+1)); done
echo $CNT

Note: running several instances of ./test.sh concurrently triggers the problem faster

Issue detected on following tested setups:

  • Linux 32bit GHC 7.10.2
  • Linux 64bit GHC 7.8.3
  • Linux 64bit GHC 7.8.4
Trac metadata
Trac field Value
Version 7.8.3
Type Bug
TypeOfFailure OtherFailure
Priority normal
Resolution Unresolved
Component Runtime System
Test case
Differential revisions
BlockedBy
Related
Blocking
CC simonmar
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information