https://github.com/python/cpython/commit/6f1e0c4f01a6f3605d3ec9afa2d38bd65cdb0eb9
commit: 6f1e0c4f01a6f3605d3ec9afa2d38bd65cdb0eb9
branch: 3.13
author: Miss Islington (bot) <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-02-24T17:20:53Z
summary:

[3.13] gh-137335: Fix unlikely name conflicts for named pipes in 
multiprocessing and asyncio on Windows (GH-137389) (GH-145171)

Since os.stat() raises an OSError for existing named pipe "\\.\pipe\...",
os.path.exists() always returns False for it, and tempfile.mktemp() can
return a name that matches an existing named pipe.

So, tempfile.mktemp() cannot be used to generate unique names for named
pipes. Instead, CreateNamedPipe() should be called in a loop with
different names until it completes successfully.
(cherry picked from commit d6a71f4690c702892644b1fbae90ae9ef733a8ab)

Co-authored-by: Serhiy Storchaka <[email protected]>

files:
A Misc/NEWS.d/next/Library/2025-08-04-23-20-43.gh-issue-137335.IIjDJN.rst
M Lib/asyncio/windows_utils.py
M Lib/multiprocessing/connection.py

diff --git a/Lib/asyncio/windows_utils.py b/Lib/asyncio/windows_utils.py
index ef277fac3e291c..acd49441131b04 100644
--- a/Lib/asyncio/windows_utils.py
+++ b/Lib/asyncio/windows_utils.py
@@ -10,7 +10,6 @@
 import msvcrt
 import os
 import subprocess
-import tempfile
 import warnings
 
 
@@ -24,6 +23,7 @@
 PIPE = subprocess.PIPE
 STDOUT = subprocess.STDOUT
 _mmap_counter = itertools.count()
+_MAX_PIPE_ATTEMPTS = 20
 
 
 # Replacement for os.pipe() using handles instead of fds
@@ -31,10 +31,6 @@
 
 def pipe(*, duplex=False, overlapped=(True, True), bufsize=BUFSIZE):
     """Like os.pipe() but with overlapped support and using handles not fds."""
-    address = tempfile.mktemp(
-        prefix=r'\\.\pipe\python-pipe-{:d}-{:d}-'.format(
-            os.getpid(), next(_mmap_counter)))
-
     if duplex:
         openmode = _winapi.PIPE_ACCESS_DUPLEX
         access = _winapi.GENERIC_READ | _winapi.GENERIC_WRITE
@@ -56,9 +52,20 @@ def pipe(*, duplex=False, overlapped=(True, True), 
bufsize=BUFSIZE):
 
     h1 = h2 = None
     try:
-        h1 = _winapi.CreateNamedPipe(
-            address, openmode, _winapi.PIPE_WAIT,
-            1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, _winapi.NULL)
+        for attempts in itertools.count():
+            address = r'\\.\pipe\python-pipe-{:d}-{:d}-{}'.format(
+                os.getpid(), next(_mmap_counter), os.urandom(8).hex())
+            try:
+                h1 = _winapi.CreateNamedPipe(
+                    address, openmode, _winapi.PIPE_WAIT,
+                    1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER, 
_winapi.NULL)
+                break
+            except OSError as e:
+                if attempts >= _MAX_PIPE_ATTEMPTS:
+                    raise
+                if e.winerror not in (_winapi.ERROR_PIPE_BUSY,
+                                      _winapi.ERROR_ACCESS_DENIED):
+                    raise
 
         h2 = _winapi.CreateFile(
             address, access, 0, _winapi.NULL, _winapi.OPEN_EXISTING,
diff --git a/Lib/multiprocessing/connection.py 
b/Lib/multiprocessing/connection.py
index abd88adf76e700..efb9ea95ab4696 100644
--- a/Lib/multiprocessing/connection.py
+++ b/Lib/multiprocessing/connection.py
@@ -44,6 +44,7 @@
 CONNECTION_TIMEOUT = 20.
 
 _mmap_counter = itertools.count()
+_MAX_PIPE_ATTEMPTS = 100
 
 default_family = 'AF_INET'
 families = ['AF_INET']
@@ -76,8 +77,8 @@ def arbitrary_address(family):
     elif family == 'AF_UNIX':
         return tempfile.mktemp(prefix='sock-', dir=util.get_temp_dir())
     elif family == 'AF_PIPE':
-        return tempfile.mktemp(prefix=r'\\.\pipe\pyc-%d-%d-' %
-                               (os.getpid(), next(_mmap_counter)), dir="")
+        return (r'\\.\pipe\pyc-%d-%d-%s' %
+                (os.getpid(), next(_mmap_counter), os.urandom(8).hex()))
     else:
         raise ValueError('unrecognized family')
 
@@ -455,17 +456,29 @@ class Listener(object):
     def __init__(self, address=None, family=None, backlog=1, authkey=None):
         family = family or (address and address_type(address)) \
                  or default_family
-        address = address or arbitrary_address(family)
-
         _validate_family(family)
+        if authkey is not None and not isinstance(authkey, bytes):
+            raise TypeError('authkey should be a byte string')
+
         if family == 'AF_PIPE':
-            self._listener = PipeListener(address, backlog)
+            if address:
+                self._listener = PipeListener(address, backlog)
+            else:
+                for attempts in itertools.count():
+                    address = arbitrary_address(family)
+                    try:
+                        self._listener = PipeListener(address, backlog)
+                        break
+                    except OSError as e:
+                        if attempts >= _MAX_PIPE_ATTEMPTS:
+                            raise
+                        if e.winerror not in (_winapi.ERROR_PIPE_BUSY,
+                                              _winapi.ERROR_ACCESS_DENIED):
+                            raise
         else:
+            address = address or arbitrary_address(family)
             self._listener = SocketListener(address, family, backlog)
 
-        if authkey is not None and not isinstance(authkey, bytes):
-            raise TypeError('authkey should be a byte string')
-
         self._authkey = authkey
 
     def accept(self):
@@ -553,7 +566,6 @@ def Pipe(duplex=True):
         '''
         Returns pair of connection objects at either end of a pipe
         '''
-        address = arbitrary_address('AF_PIPE')
         if duplex:
             openmode = _winapi.PIPE_ACCESS_DUPLEX
             access = _winapi.GENERIC_READ | _winapi.GENERIC_WRITE
@@ -563,15 +575,25 @@ def Pipe(duplex=True):
             access = _winapi.GENERIC_WRITE
             obsize, ibsize = 0, BUFSIZE
 
-        h1 = _winapi.CreateNamedPipe(
-            address, openmode | _winapi.FILE_FLAG_OVERLAPPED |
-            _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE,
-            _winapi.PIPE_TYPE_MESSAGE | _winapi.PIPE_READMODE_MESSAGE |
-            _winapi.PIPE_WAIT,
-            1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER,
-            # default security descriptor: the handle cannot be inherited
-            _winapi.NULL
-            )
+        for attempts in itertools.count():
+            address = arbitrary_address('AF_PIPE')
+            try:
+                h1 = _winapi.CreateNamedPipe(
+                    address, openmode | _winapi.FILE_FLAG_OVERLAPPED |
+                    _winapi.FILE_FLAG_FIRST_PIPE_INSTANCE,
+                    _winapi.PIPE_TYPE_MESSAGE | _winapi.PIPE_READMODE_MESSAGE |
+                    _winapi.PIPE_WAIT,
+                    1, obsize, ibsize, _winapi.NMPWAIT_WAIT_FOREVER,
+                    # default security descriptor: the handle cannot be 
inherited
+                    _winapi.NULL
+                    )
+                break
+            except OSError as e:
+                if attempts >= _MAX_PIPE_ATTEMPTS:
+                    raise
+                if e.winerror not in (_winapi.ERROR_PIPE_BUSY,
+                                      _winapi.ERROR_ACCESS_DENIED):
+                    raise
         h2 = _winapi.CreateFile(
             address, access, 0, _winapi.NULL, _winapi.OPEN_EXISTING,
             _winapi.FILE_FLAG_OVERLAPPED, _winapi.NULL
diff --git 
a/Misc/NEWS.d/next/Library/2025-08-04-23-20-43.gh-issue-137335.IIjDJN.rst 
b/Misc/NEWS.d/next/Library/2025-08-04-23-20-43.gh-issue-137335.IIjDJN.rst
new file mode 100644
index 00000000000000..2311ace10e411d
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-08-04-23-20-43.gh-issue-137335.IIjDJN.rst
@@ -0,0 +1,2 @@
+Get rid of any possibility of a name conflict for named pipes in
+:mod:`multiprocessing` and :mod:`asyncio` on Windows, no matter how small.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to