When loop.create_connection(protocol_factory, sock=sock) is cancelled (e.g. by a connect timeout), the passed-in Python socket object is never detached. Its __del__ later closes whatever fd the OS recycled into that slot, silently corrupting an unrelated transport.
Standard asyncio is not affected — its transport directly owns the Python socket and properly closes it on the error path, setting fileno() to -1.
Root cause
In loop.pyx (the sock is not None branch of create_connection):
# uvloop/loop.pyx ~line 2060waiter=self._new_future()
tr=TCPTransport.new(self, protocol, None, waiter, context)
try:
tr._open(sock.fileno()) # libuv takes over the fdtr._init_protocol() # _transports[fd] = trawaitwaiter# ← cancellation pointexceptBaseException:
tr._close() # libuv closes fd, but sock doesn't knowraisetr._attach_fileobj(sock) # only reached on success — calls sock.detach() later via _close()
On the success path, _attach_fileobj saves a reference to sock. When the transport is later closed, _close() calls sock.detach(), which sets the socket's internal fd to -1 so GC won't double-close it.
On the error/cancellation path, _attach_fileobj is never called → sock.detach() never happens → the Python socket still believes it owns the (now-recycled) fd number → sock.__del__() closes an unrelated fd.
uvloop: sock.fileno() = 13 ← should be -1
asyncio: sock.fileno() = -1 ← correct
Impact
Under high concurrency with force_close=True (aiohttp) and intermittent connection timeouts, the GC'd socket closes a live transport's fd. This manifests as:
RuntimeError: File descriptor 1768 is used by transport <TCPTransport closed=False reading=True 0x...>
followed by Assertion failed: ok (src/mailbox.cpp:72) → SIGABRT, crashing the process.
Activity
Detach socket on create_connection/create_unix_connection cancellation
Detach socket on create_connection cancellation to prevent fd double-…
uvloop 0.23.0
fix(relay): bump uvloop to 0.23.0 to fix a double-closed file descriptor
fix(relay): bump uvloop to 0.23.0 to fix a double-closed file descrip…
fix(api): bump uvloop to 0.23.0 to stop cross-connection response mis…