Skip to content

WIP: System.Environment: thread safe withArgs

This is a tentative fix for #18261

withArgs args action is not thread safe and its behavior with multiples threads is not reproducible, among other things:

  • call of getArgs in action is not guarantee to return args if another withArgs in another thread was executed.
  • call of getArgs after the withArgs block is not guaranteed to see the initially restored environment.
  • It can crash the program because of access of a freed memory area. Or worse, maybe this could be exploited and lead to a security issue.

This merge request attempts at fixing the crash, and also document the thread unsafe behavior.

In order to fix the possible crash, the program arguments stored in "C side" are only read once and never wrote. In Haskell side, it is stored in an IORef [String].

Discussion points

  • It use unsafePerformIO, is that acceptable?
  • It will stores a bunch of String for the complete lifetime of a program. This may have a (kinda negligible, but existing) impact on the GC. Should we look for a more compact representation (ByteString?) or some kind of lazy initialization (e.g. we can store Nothing in the IORef if the arguments are unchanged from what's stored in "C side")?

Tests

I've "tested" with the following program:

import Control.Monad
import System.Environment
import Control.Concurrent (threadDelay, forkIO)

prog = forkIO $ forever $ do
  withArgs ["hello", "goodbye"] $ do
    print "chicken"

main = do
  replicateM 10 prog
  threadDelay 10_000_000

Without this MR, the program crashs (when run with more than 1 thread), with, it does not crash anymore

Merge request reports

Loading