Skip to content

Commit 78a874a

Browse files
committed
pythongh-111246: Don't remove stolen Unix socket address (python#111246)
We only want to clean up *our* socket, so try to determine if we still own this address or if something else has replaced it.
1 parent 25be81d commit 78a874a

File tree

3 files changed

+31
-3
lines changed

3 files changed

+31
-3
lines changed

Doc/library/asyncio-eventloop.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -803,7 +803,8 @@ Creating network servers
803803
.. versionchanged:: 3.13
804804

805805
The Unix socket will automatically be removed from the filesystem
806-
when the server is closed.
806+
when the server is closed, unless the socket has been replaced
807+
after the server has been created.
807808

808809

809810
.. coroutinemethod:: loop.connect_accepted_socket(protocol_factory, \

Lib/asyncio/unix_events.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop):
6464
def __init__(self, selector=None):
6565
super().__init__(selector)
6666
self._signal_handlers = {}
67+
self._unix_server_sockets = {}
6768

6869
def close(self):
6970
super().close()
@@ -340,6 +341,14 @@ async def create_unix_server(
340341
raise ValueError(
341342
f'A UNIX Domain Stream Socket was expected, got {sock!r}')
342343

344+
path = sock.getsockname()
345+
# Check for abstract socket. `str` and `bytes` paths are supported.
346+
if path[0] not in (0, '\x00'):
347+
try:
348+
self._unix_server_sockets[sock] = os.stat(path).st_ino
349+
except FileNotFoundError:
350+
pass
351+
343352
sock.setblocking(False)
344353
server = base_events.Server(self, [sock], protocol_factory,
345354
ssl, backlog, ssl_handshake_timeout,
@@ -472,9 +481,12 @@ def _stop_serving(self, sock):
472481

473482
super()._stop_serving(sock)
474483

475-
if path is not None:
484+
if path is not None and sock in self._unix_server_sockets:
485+
prev_ino = self._unix_server_sockets[sock]
486+
del self._unix_server_sockets[sock]
476487
try:
477-
os.unlink(path)
488+
if os.stat(path).st_ino == prev_ino:
489+
os.unlink(path)
478490
except FileNotFoundError:
479491
pass
480492
except OSError as err:

Lib/test/test_asyncio/test_server.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,21 @@ async def serve(*args):
190190

191191
srv.close()
192192

193+
@socket_helper.skip_unless_bind_unix_socket
194+
async def test_unix_server_cleanup_replaced(self):
195+
with test_utils.unix_socket_path() as addr:
196+
async def serve(*args):
197+
pass
198+
199+
srv = await asyncio.start_unix_server(serve, addr)
200+
201+
os.unlink(addr)
202+
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
203+
sock.bind(addr)
204+
205+
srv.close()
206+
self.assertTrue(os.path.exists(addr))
207+
193208

194209
@unittest.skipUnless(hasattr(asyncio, 'ProactorEventLoop'), 'Windows only')
195210
class ProactorStartServerTests(BaseStartServer, unittest.TestCase):

0 commit comments

Comments
 (0)