Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions Lib/multiprocessing/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,12 @@ def arbitrary_address(family):
if family == 'AF_INET':
return ('localhost', 0)
elif family == 'AF_UNIX':
# NOTE: util.get_temp_dir() is a 0o700 per-process directory. A
# mktemp-style ToC vs ToU concern is not important; bind() surfaces
# the extremely unlikely collision as EADDRINUSE.
# NOTE: util.get_temp_dir() is a 0o700 per-process directory.
# A mktemp-style ToC vs ToU concern is not important as bind()
# surfaces the extremely unlikely collision as EADDRINUSE.
suffix = os.urandom(util._TMPSOCK_SUFFIXLEN // 2).hex()
return os.path.join(util.get_temp_dir(),
f'sock-{os.urandom(6).hex()}')
f"{util._TMPSOCK_PREFIX}{suffix}")
elif family == 'AF_PIPE':
return (r'\\.\pipe\pyc-%d-%d-%s' %
(os.getpid(), next(_mmap_counter), os.urandom(8).hex()))
Expand Down
52 changes: 38 additions & 14 deletions Lib/multiprocessing/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,38 @@ def is_abstract_socket_namespace(address):
# On Windows platforms, we do not create AF_UNIX sockets.
_SUN_PATH_MAX = None if os.name == 'nt' else 92

# The temporary socket file path (without NULL terminator) is
#
# TEMPDIR/{_TMPPYMP_PREFIX}{S1}/{_TMPSOCK_PREFIX}{S2}
#
# where
#
# - S1 is a random suffix of length 8 generated by get_temp_dir().
# This is used to generate the temporary sub-directory that will
# contains the socket file. Update _TMPPYMP_SUFFIXLEN accordingly
# if the random suffix length chosen by tempfile.mkdtemp() changes.
#
# - S2 is a random suffix of length 2N generated by os.urandom(N).hex()
# in multiprocessing.connection.arbitrary_address(). The value of N
# is not fixed and may vary across Python versions as to minimize
# collisions and path lengths.
#
_TMPPYMP_PREFIX = "pymp-"
_TMPPYMP_PREFIXLEN = len(_TMPPYMP_PREFIX)
_TMPPYMP_SUFFIXLEN = 8
_TMPSOCK_PREFIX = "sock-"
_TMPSOCK_PREFIXLEN = len(_TMPSOCK_PREFIX)
_TMPSOCK_SUFFIXLEN = 12

# Length of the socket filepath from the chosen temporary directory,
# including a leading path separator and the path separator between
# the temporary subdirectory and the socket filename.
_SUN_PATH_LEN_RESERVED = (
len(os.path.sep) + _TMPPYMP_PREFIXLEN + _TMPPYMP_SUFFIXLEN +
len(os.path.sep) + _TMPSOCK_PREFIXLEN + _TMPSOCK_SUFFIXLEN
)


def _remove_temp_dir(rmtree, tempdir):
rmtree(tempdir)

Expand All @@ -159,24 +191,16 @@ def _get_base_temp_dir(tempfile):
"""
if os.name == 'nt':
return None
# Most of the time, the default temporary directory is /tmp. Thus,
# listener sockets files "$TMPDIR/pymp-XXXXXXXX/sock-XXXXXXXX" do
# not have a path length exceeding SUN_PATH_MAX.
# Most of the time, the default temporary directory is /tmp. As such,
# the path length of listener sockets files does not exceed SUN_PATH_MAX.
#
# If users specify their own temporary directory, we may be unable
# to create those files. Therefore, we fall back to the system-wide
# temporary directory /tmp, assumed to exist on POSIX systems.
#
# See https://github.com/python/cpython/issues/132124.
base_tempdir = tempfile.gettempdir()
# Files created in a temporary directory are suffixed by a string
# generated by tempfile._RandomNameSequence, which, by design,
# is 8 characters long.
#
# Thus, the socket file path length (without NULL terminator) will be:
#
# len(base_tempdir + '/pymp-XXXXXXXX' + '/sock-XXXXXXXX')
sun_path_len = len(base_tempdir) + 14 + 14
sun_path_len = len(base_tempdir) + _SUN_PATH_LEN_RESERVED
# Strict inequality to account for the NULL terminator.
# See https://github.com/python/cpython/issues/140734.
if sun_path_len < _SUN_PATH_MAX:
Expand Down Expand Up @@ -204,8 +228,8 @@ def _get_base_temp_dir(tempfile):
# not be able to write socket files out there.
return base_tempdir
warn("Ignoring user-defined temporary directory: %s", base_tempdir)
# at most max(map(len, dirlist)) + 14 + 14 = 36 characters
assert len(base_system_tempdir) + 14 + 14 < _SUN_PATH_MAX
# The following assertion must be satisfied with the chosen constants.
assert len(base_system_tempdir) + _SUN_PATH_LEN_RESERVED < _SUN_PATH_MAX
return base_system_tempdir

def get_temp_dir():
Expand All @@ -214,7 +238,7 @@ def get_temp_dir():
if tempdir is None:
import shutil, tempfile
base_tempdir = _get_base_temp_dir(tempfile)
tempdir = tempfile.mkdtemp(prefix='pymp-', dir=base_tempdir)
tempdir = tempfile.mkdtemp(prefix=_TMPPYMP_PREFIX, dir=base_tempdir)
info('created temp directory %s', tempdir)
# keep a strong reference to shutil.rmtree(), since the finalizer
# can be called late during Python shutdown
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:mod:`multiprocessing`: fix creation of temporary socket files following
:gh:`137335` (`PR#148578 <https://github.com/python/cpython/pull/148578>`__
and corresponding backports). Patch by Bénédikt Tran.
Loading