Skip to content

About

Repro: Windows sockets are inheritable in haskell/network, so a child process keeps a closed connection open

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

7 Commits

Folders and files

Repository files navigation

Windows: sockets are inheritable, so a child process keeps a closed connection open

Running it

On Windows, with GHC and cabal (e.g. from ghcup):

cabal run --project-file=cabal.project.original repro    # haskell/network master
cabal run --project-file=cabal.project.fixed repro       # master plus the fix

Add -- +RTS --io-manager=native to either to use the WinIO I/O manager; the results are the same.

Each project file builds network from a commit on codedownio/network that has its generated configure script committed, so no autoconf is needed. Running configure still needs an sh, which ghcup's msys2 or Git for Windows (C:\Program Files\Git\bin on the PATH) provides.

Results (Windows 11, GHC 9.12.4)

original:

  listener handle flags: 1
  client handle flags: 1
  conn handle flags: 1
no child: peer saw the close
child with std_out = Inherit: peer saw the close
child with std_out = CreatePipe: peer did NOT see the close within 3s (connection still open)

fixed: every handle reports flags 0, and the peer sees the close in all three cases.

What it shows

socket and accept create inheritable handles on Windows, whereas on Unix accept sets FD_CLOEXEC (accept4 with SOCK_CLOEXEC). createProcess passes bInheritHandles=TRUE whenever a standard stream is redirected, even with close_fds = True (see haskell/process#235), so every socket open at that moment is inherited by the child. As on Unix, the connection only closes when the last handle to it is closed, so closing the socket in the parent doesn't close it: the peer never sees EOF until the child exits.

Main.hs accepts a connection, spawns a child that sleeps, closes the accepted socket, and checks whether the client sees the close. The fix clears HANDLE_FLAG_INHERIT right after socket and accept.

How we hit it: a warp server spawning long-lived wsl.exe children. Every keep-alive connection open at spawn time was inherited; when warp closed an idle one, the client reused it and its next request hung forever.

About

Repro: Windows sockets are inheritable in haskell/network, so a child process keeps a closed connection open

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages