Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC

Commits:

5 changed files:

Changes:

  • changelog.d/fix-nonmoving-weak-evacuation-race-23160
    1
    +section: rts
    
    2
    +synopsis: Fix crash in the nonmoving GC caused by racing weak pointer evacuation
    
    3
    +issues: #23160
    
    4
    +mrs: !16522
    
    5
    +description:
    
    6
    +  When two threads compete for evacuating the same ``Weak#`` object, the
    
    7
    +  losing thread would produces an object that is never put on a weak list.
    
    8
    +  This resulted in pointers in the object getting stale, and the nonmoving
    
    9
    +  GC crashing trying to follow them. This is fixed by immediately marking
    
    10
    +  the losing weak object as dead.

  • rts/sm/Evac.c
    ... ... @@ -265,6 +265,17 @@ copy_tag(StgClosure **p, const StgInfoTable *info,
    265 265
                   LDVW(to) = 0;
    
    266 266
                 }
    
    267 267
     #endif
    
    268
    +            // The weak we just created is dead and doesn't appear on any
    
    269
    +            // weak pointer list. This means that its value, finalizer and link
    
    270
    +            // pointers will not be updated. However the nonmoving collector
    
    271
    +            // can still see it (update remembered set, mutable list), so we must
    
    272
    +            // tombstone it to avoid following stale pointers. See #23160.
    
    273
    +
    
    274
    +            // The copy is still thread-private here so a plain store
    
    275
    +            // suffices.
    
    276
    +            if (INFO_PTR_TO_STRUCT(info)->type == WEAK) {
    
    277
    +                ((StgClosure *)to)->header.info = &stg_DEAD_WEAK_info;
    
    278
    +            }
    
    268 279
                 return evacuate(p); // does the failed_to_evac stuff
    
    269 280
             } else {
    
    270 281
                 // This doesn't need to have RELEASE ordering since we are guaranteed
    

  • testsuite/tests/rts/T23160.hs
    1
    +{-# LANGUAGE UnboxedTuples #-}
    
    2
    +{-# LANGUAGE MagicHash #-}
    
    3
    +
    
    4
    +-- Regression test for multiple threads competing for evacuating a Weak#
    
    5
    +-- Based on the reproducer by Ben Gamari in #23160
    
    6
    +
    
    7
    +
    
    8
    +import GHC.Exts
    
    9
    +import GHC.Weak
    
    10
    +import GHC.IO (IO(..))
    
    11
    +import Control.Concurrent
    
    12
    +import Control.Exception
    
    13
    +import Control.Monad
    
    14
    +import System.Mem
    
    15
    +import System.Mem.Weak
    
    16
    +
    
    17
    +data MBA = MBA (MutableByteArray# RealWorld)
    
    18
    +
    
    19
    +newPinnedMBA :: Int -> IO MBA
    
    20
    +newPinnedMBA (I# n) = IO $ \s ->
    
    21
    +  case newPinnedByteArray# n s of
    
    22
    +    (# s', mba #) -> (# s', MBA mba #)
    
    23
    +
    
    24
    +mkMBAWeak :: MBA -> a -> IO (Weak a)
    
    25
    +mkMBAWeak (MBA mba) v = IO $
    
    26
    +    \s -> case mkWeakNoFinalizer# mba v s of
    
    27
    +            (# s', w #) -> (# s', Weak w #)
    
    28
    +
    
    29
    +forced :: String -> IO Int
    
    30
    +forced a = evaluate (length a)
    
    31
    +
    
    32
    +worker :: IO ()
    
    33
    +worker = forever $ do
    
    34
    +    xs <- mapM f [0..200000::Int]
    
    35
    +    mapM_ (\w -> deRefWeak w >>= mapM_ forced) (take 1000 xs)
    
    36
    +  where
    
    37
    +    f n = do
    
    38
    +      mba <- newPinnedMBA 10
    
    39
    +      v <- forced (show n)
    
    40
    +      mkMBAWeak mba (show (n + v))
    
    41
    +
    
    42
    +main :: IO ()
    
    43
    +main = do
    
    44
    +  forkIO $ forever $ do
    
    45
    +    threadDelay (100*1000)
    
    46
    +    performMajorGC
    
    47
    +
    
    48
    +  replicateM_ 10 (forkIO worker)
    
    49
    +  threadDelay (25*1000*1000)
    
    50
    +  putStrLn "OK"

  • testsuite/tests/rts/T23160.stdout
    1
    +OK

  • testsuite/tests/rts/all.T
    ... ... @@ -767,3 +767,12 @@ test('T27526',
    767 767
           extra_hc_opts('-fno-prof-auto -fprof-late -rtsopts'),
    
    768 768
           extra_run_opts('+RTS -kc2k -kb256 -A64k -AL8k -RTS')],
    
    769 769
          compile_and_run, [''])
    
    770
    +
    
    771
    +test('T23160',
    
    772
    +     [ req_target_smp
    
    773
    +     , extra_ways(['nonmoving_thr_sanity'])
    
    774
    +     , only_ways(['nonmoving_thr_sanity'])
    
    775
    +     , extra_run_opts('+RTS -A1M -N4 -RTS')
    
    776
    +     , unless(slow(), skip)
    
    777
    +     ],
    
    778
    +     compile_and_run, [''])