Skip to content

Infinite loop when running interactive process with native WinIO manager

I recently tried running a build of Cryptol with the new native Windows IO manager enabled, but I ran into an infinite loop that is not present when using the POSIX-based IO manager. To reproduce the infinite loop, do the following on a Windows machine:

  1. Download Z3 from https://github.com/Z3Prover/z3/releases/download/z3-4.8.14/z3-4.8.14-x64-win.zip and extract it.

  2. Compile the following minimized example:

    module Main (main) where
    
    import System.IO (Handle, hClose, hGetLine, hFlush, hPutStrLn)
    import System.Process (CreateProcess(..), ProcessHandle, StdStream(..), createProcess, proc, waitForProcess)
    
    main :: IO ()
    main = do
      (in_h, out_h, err_h, ph) <- startProcess "z3" ["-smt2", "-in"]
    
      let doCmd cmd = do
            hPutStrLn in_h cmd
            hFlush in_h
            line <- hGetLine out_h
            putStrLn line
      doCmd "(set-option :print-success true)"
      doCmd "(set-option :produce-models true)"
      doCmd "(set-option :pp.decimal true)"
      doCmd "(set-option :global-declarations true)"
      doCmd "(get-info :error-behavior)"
      doCmd "(exit)"
    
      hClose in_h
      hClose out_h
      hClose err_h
      print =<< waitForProcess ph
    
    -- | Start a process connected to this one via pipes.
    startProcess ::
      FilePath {-^ Path to executable -} ->
      [String] {-^ Command-line arguments -} ->
      IO (Handle, Handle, Handle, ProcessHandle)
      {-^ process stdin, process stdout, process stderr, process handle -}
    startProcess path args =
      do let create_proc
                = (proc path args)
                  { std_in  = CreatePipe
                  , std_out = CreatePipe
                  , std_err = CreatePipe
                  , create_group = False
                  , cwd = Nothing
                  , delegate_ctlc = True
                  }
         createProcess create_proc >>= \x -> case x of
           (Just in_h, Just out_h, Just err_h, ph) -> return (in_h, out_h, err_h, ph)
           _ -> fail $ unlines $
                   [ "startProcess"
                   , "Failed to exec: " ++ show path
                   , "With the following arguments:"
                   ] ++ args

    With:

    $ ghc-9.2 Main.hs -rtsopts

    (Note that GHC 9.0 is also sufficient to trigger the bug.)

  3. First, run the program with the POSIX-based IO manager:

    $ $env:Path = "$env:USERPROFILE\Software\z3-4.8.14-x64-win\bin;" + $env:Path 
    $ ./Main.exe +RTS --io-manager=posix -RTS
    success
    success
    success
    success
    (:error-behavior continued-execution)
    success
    ExitSuccess

    This is the expected behavior of the program.

  4. Finally, run the program with the native WinIO manager:

    $ ./Main.exe +RTS --io-manager=native -RTS
    success
    # infinite loop

    After printing out a single success, the program will infinitely loop. If you Ctrl-C at this point, you'll get some interesting errors depending on where in the program you interrupt. I've seen the following happen:

    • Main.exe: schedule: re-entered unsafely.
         Perhaps a 'foreign import unsafe' should be 'safe'?
    • Main.exe: internal error: TSO object (0000000000a33c71) entered!
          (GHC version 9.2.2 for x86_64_unknown_mingw32)
          Please report this as a GHC bug:  https://www.haskell.org/ghc/reportabug
    • Access violation in generated code when reading 0xffffffffffffffff
      
       Attempting to reconstruct a stack trace...
      
         Frame        Code address
       * 0x214db30    0x1902cf0 C:\Users\winferno\Documents\Hacking\Haskell\Main.exe+0x1502cf0
       * 0x214db38    0x22fedd0
       * 0x214db40    0x7fffe3596098 C:\Windows\System32\USER32.dll+0x96098
       * 0x214db48    0x22fe450
       * 0x214db50    0x22fedd0
       * 0x214db58    0x6f0064006e0069
       * 0x214db60    0x73005c00730077
       * 0x214db68    0x65007400730079
    • Segmentation fault
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information