mirror of
https://github.com/python/cpython.git
synced 2026-04-14 07:41:00 +00:00
[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 d6a71f4690)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
f1534307e8
commit
6f1e0c4f01
3 changed files with 58 additions and 27 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 family == 'AF_PIPE':
|
||||
self._listener = PipeListener(address, backlog)
|
||||
else:
|
||||
self._listener = SocketListener(address, family, backlog)
|
||||
|
||||
if authkey is not None and not isinstance(authkey, bytes):
|
||||
raise TypeError('authkey should be a byte string')
|
||||
|
||||
if family == 'AF_PIPE':
|
||||
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)
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue