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:
-
Download Z3 from https://github.com/Z3Prover/z3/releases/download/z3-4.8.14/z3-4.8.14-x64-win.zip and extract it.
-
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.)
-
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.
-
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
-