mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
gh-132124: improve safety nets for creating AF_UNIX socket files (GH-134085)
* ensure that we can create AF_UNIX socket files * emit a warning if system-wide temporary directory is used
This commit is contained in:
parent
e1f891414b
commit
1a07a01014
4 changed files with 87 additions and 5 deletions
|
@ -76,7 +76,7 @@ def arbitrary_address(family):
|
||||||
if family == 'AF_INET':
|
if family == 'AF_INET':
|
||||||
return ('localhost', 0)
|
return ('localhost', 0)
|
||||||
elif family == 'AF_UNIX':
|
elif family == 'AF_UNIX':
|
||||||
return tempfile.mktemp(prefix='listener-', dir=util.get_temp_dir())
|
return tempfile.mktemp(prefix='sock-', dir=util.get_temp_dir())
|
||||||
elif family == 'AF_PIPE':
|
elif family == 'AF_PIPE':
|
||||||
return tempfile.mktemp(prefix=r'\\.\pipe\pyc-%d-%d-' %
|
return tempfile.mktemp(prefix=r'\\.\pipe\pyc-%d-%d-' %
|
||||||
(os.getpid(), next(_mmap_counter)), dir="")
|
(os.getpid(), next(_mmap_counter)), dir="")
|
||||||
|
|
|
@ -19,7 +19,7 @@ from subprocess import _args_from_interpreter_flags # noqa: F401
|
||||||
from . import process
|
from . import process
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'sub_debug', 'debug', 'info', 'sub_warning', 'get_logger',
|
'sub_debug', 'debug', 'info', 'sub_warning', 'warn', 'get_logger',
|
||||||
'log_to_stderr', 'get_temp_dir', 'register_after_fork',
|
'log_to_stderr', 'get_temp_dir', 'register_after_fork',
|
||||||
'is_exiting', 'Finalize', 'ForkAwareThreadLock', 'ForkAwareLocal',
|
'is_exiting', 'Finalize', 'ForkAwareThreadLock', 'ForkAwareLocal',
|
||||||
'close_all_fds_except', 'SUBDEBUG', 'SUBWARNING',
|
'close_all_fds_except', 'SUBDEBUG', 'SUBWARNING',
|
||||||
|
@ -34,6 +34,7 @@ SUBDEBUG = 5
|
||||||
DEBUG = 10
|
DEBUG = 10
|
||||||
INFO = 20
|
INFO = 20
|
||||||
SUBWARNING = 25
|
SUBWARNING = 25
|
||||||
|
WARNING = 30
|
||||||
|
|
||||||
LOGGER_NAME = 'multiprocessing'
|
LOGGER_NAME = 'multiprocessing'
|
||||||
DEFAULT_LOGGING_FORMAT = '[%(levelname)s/%(processName)s] %(message)s'
|
DEFAULT_LOGGING_FORMAT = '[%(levelname)s/%(processName)s] %(message)s'
|
||||||
|
@ -53,6 +54,10 @@ def info(msg, *args):
|
||||||
if _logger:
|
if _logger:
|
||||||
_logger.log(INFO, msg, *args, stacklevel=2)
|
_logger.log(INFO, msg, *args, stacklevel=2)
|
||||||
|
|
||||||
|
def warn(msg, *args):
|
||||||
|
if _logger:
|
||||||
|
_logger.log(WARNING, msg, *args, stacklevel=2)
|
||||||
|
|
||||||
def sub_warning(msg, *args):
|
def sub_warning(msg, *args):
|
||||||
if _logger:
|
if _logger:
|
||||||
_logger.log(SUBWARNING, msg, *args, stacklevel=2)
|
_logger.log(SUBWARNING, msg, *args, stacklevel=2)
|
||||||
|
@ -121,6 +126,21 @@ abstract_sockets_supported = _platform_supports_abstract_sockets()
|
||||||
# Function returning a temp directory which will be removed on exit
|
# Function returning a temp directory which will be removed on exit
|
||||||
#
|
#
|
||||||
|
|
||||||
|
# Maximum length of a socket file path is usually between 92 and 108 [1],
|
||||||
|
# but Linux is known to use a size of 108 [2]. BSD-based systems usually
|
||||||
|
# use a size of 104 or 108 and Windows does not create AF_UNIX sockets.
|
||||||
|
#
|
||||||
|
# [1]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_un.h.html
|
||||||
|
# [2]: https://man7.org/linux/man-pages/man7/unix.7.html.
|
||||||
|
|
||||||
|
if sys.platform == 'linux':
|
||||||
|
_SUN_PATH_MAX = 108
|
||||||
|
elif sys.platform.startswith(('openbsd', 'freebsd')):
|
||||||
|
_SUN_PATH_MAX = 104
|
||||||
|
else:
|
||||||
|
# On Windows platforms, we do not create AF_UNIX sockets.
|
||||||
|
_SUN_PATH_MAX = None if os.name == 'nt' else 92
|
||||||
|
|
||||||
def _remove_temp_dir(rmtree, tempdir):
|
def _remove_temp_dir(rmtree, tempdir):
|
||||||
rmtree(tempdir)
|
rmtree(tempdir)
|
||||||
|
|
||||||
|
@ -130,12 +150,67 @@ def _remove_temp_dir(rmtree, tempdir):
|
||||||
if current_process is not None:
|
if current_process is not None:
|
||||||
current_process._config['tempdir'] = None
|
current_process._config['tempdir'] = None
|
||||||
|
|
||||||
|
def _get_base_temp_dir(tempfile):
|
||||||
|
"""Get a temporary directory where socket files will be created.
|
||||||
|
|
||||||
|
To prevent additional imports, pass a pre-imported 'tempfile' module.
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
#
|
||||||
|
# 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 length of socket filename will be:
|
||||||
|
#
|
||||||
|
# len(base_tempdir + '/pymp-XXXXXXXX' + '/sock-XXXXXXXX')
|
||||||
|
sun_path_len = len(base_tempdir) + 14 + 14
|
||||||
|
if sun_path_len <= _SUN_PATH_MAX:
|
||||||
|
return base_tempdir
|
||||||
|
# Fallback to the default system-wide temporary directory.
|
||||||
|
# This ignores user-defined environment variables.
|
||||||
|
#
|
||||||
|
# On POSIX systems, /tmp MUST be writable by any application [1].
|
||||||
|
# We however emit a warning if this is not the case to prevent
|
||||||
|
# obscure errors later in the execution.
|
||||||
|
#
|
||||||
|
# On some legacy systems, /var/tmp and /usr/tmp can be present
|
||||||
|
# and will be used instead.
|
||||||
|
#
|
||||||
|
# [1]: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s18.html
|
||||||
|
dirlist = ['/tmp', '/var/tmp', '/usr/tmp']
|
||||||
|
try:
|
||||||
|
base_system_tempdir = tempfile._get_default_tempdir(dirlist)
|
||||||
|
except FileNotFoundError:
|
||||||
|
warn("Process-wide temporary directory %s will not be usable for "
|
||||||
|
"creating socket files and no usable system-wide temporary "
|
||||||
|
"directory was found in %s", base_tempdir, dirlist)
|
||||||
|
# At this point, the system-wide temporary directory is not usable
|
||||||
|
# but we may assume that the user-defined one is, even if we will
|
||||||
|
# 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
|
||||||
|
return base_system_tempdir
|
||||||
|
|
||||||
def get_temp_dir():
|
def get_temp_dir():
|
||||||
# get name of a temp directory which will be automatically cleaned up
|
# get name of a temp directory which will be automatically cleaned up
|
||||||
tempdir = process.current_process()._config.get('tempdir')
|
tempdir = process.current_process()._config.get('tempdir')
|
||||||
if tempdir is None:
|
if tempdir is None:
|
||||||
import shutil, tempfile
|
import shutil, tempfile
|
||||||
tempdir = tempfile.mkdtemp(prefix='pymp-')
|
base_tempdir = _get_base_temp_dir(tempfile)
|
||||||
|
tempdir = tempfile.mkdtemp(prefix='pymp-', dir=base_tempdir)
|
||||||
info('created temp directory %s', tempdir)
|
info('created temp directory %s', tempdir)
|
||||||
# keep a strong reference to shutil.rmtree(), since the finalizer
|
# keep a strong reference to shutil.rmtree(), since the finalizer
|
||||||
# can be called late during Python shutdown
|
# can be called late during Python shutdown
|
||||||
|
|
|
@ -180,7 +180,7 @@ def _candidate_tempdir_list():
|
||||||
|
|
||||||
return dirlist
|
return dirlist
|
||||||
|
|
||||||
def _get_default_tempdir():
|
def _get_default_tempdir(dirlist=None):
|
||||||
"""Calculate the default directory to use for temporary files.
|
"""Calculate the default directory to use for temporary files.
|
||||||
This routine should be called exactly once.
|
This routine should be called exactly once.
|
||||||
|
|
||||||
|
@ -190,7 +190,8 @@ def _get_default_tempdir():
|
||||||
service, the name of the test file must be randomized."""
|
service, the name of the test file must be randomized."""
|
||||||
|
|
||||||
namer = _RandomNameSequence()
|
namer = _RandomNameSequence()
|
||||||
dirlist = _candidate_tempdir_list()
|
if dirlist is None:
|
||||||
|
dirlist = _candidate_tempdir_list()
|
||||||
|
|
||||||
for dir in dirlist:
|
for dir in dirlist:
|
||||||
if dir != _os.curdir:
|
if dir != _os.curdir:
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
On POSIX-compliant systems, :func:`!multiprocessing.util.get_temp_dir` now
|
||||||
|
ignores :envvar:`TMPDIR` (and similar environment variables) if the path
|
||||||
|
length of ``AF_UNIX`` socket files exceeds the platform-specific maximum
|
||||||
|
length when using the :ref:`forkserver
|
||||||
|
<multiprocessing-start-method-forkserver>` start method. Patch by Bénédikt
|
||||||
|
Tran.
|
Loading…
Add table
Add a link
Reference in a new issue