This library's implementation of accept can leak file descriptors if the thread calling accept receives an asynchronous (throwTo) exception during the execution of accept. The current version, with CPP removed to show only the codepath for Linux, looks like this:
accept :: SocketAddress sa => Socket -> IO (Socket, sa)
accept listing_sock = withNewSocketAddress $ \new_sa sz ->
withFdSocket listing_sock $ \listing_fd -> do
new_sock <- callAccept listing_fd new_sa sz >>= mkSocket
new_addr <- peekSocketAddress new_sa
return (new_sock, new_addr)
where
callAccept fd sa sz = with (fromIntegral sz) $ \ ptr_len -> do
throwSocketErrorWaitRead listing_sock "Network.Socket.accept"
(c_accept4 fd sa ptr_len (sockNonBlock .|. sockCloexec))
After callAccept but before mkSocket, an asynchronous exception could be received. One simple solution is to mask exceptions:
new_sock <- mask_ (callAccept listing_fd new_sa sz >>= mkSocket)
Now, even if an exception is delivered, the finalizer associated with the socket should close it. Note: I am assuming that mkSocket is not interruptible, and if this assumption is wrong, none of this will work. It is defined as
mkSocket :: CInt -> IO Socket
mkSocket fd = do
ref <- newIORef fd
let s = Socket ref fd
void $ mkWeakIORef ref $ close s
return s
And the documentation in Control.Exception makes it clear that newIORef is not interruptible. I believe that mkWeakIORef is also not interruptible.
This library's implementation of
acceptcan leak file descriptors if the thread callingacceptreceives an asynchronous (throwTo) exception during the execution ofaccept. The current version, with CPP removed to show only the codepath for Linux, looks like this:After
callAcceptbut beforemkSocket, an asynchronous exception could be received. One simple solution is to mask exceptions:Now, even if an exception is delivered, the finalizer associated with the socket should close it. Note: I am assuming that
mkSocketis not interruptible, and if this assumption is wrong, none of this will work. It is defined asAnd the documentation in
Control.Exceptionmakes it clear thatnewIORefis not interruptible. I believe thatmkWeakIORefis also not interruptible.