mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	[3.13] gh-132124: improve safety nets for creating AF_UNIX socket files (GH-134085) (#134448)
* 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
(cherry picked from commit 1a07a01014)
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
* rename warn -> _warn for the 3.13.x backport
* Update Misc/NEWS.d/next/Library/2025-05-16-12-40-37.gh-issue-132124.T_5Odx.rst
---------
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
			
			
This commit is contained in:
		
							parent
							
								
									235d380c4c
								
							
						
					
					
						commit
						932221bc01
					
				
					 4 changed files with 84 additions and 4 deletions
				
			
		| 
						 | 
					@ -74,7 +74,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="")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,6 +34,7 @@
 | 
				
			||||||
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 @@ def is_abstract_socket_namespace(address):
 | 
				
			||||||
# 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,4 @@
 | 
				
			||||||
 | 
					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 *forkserver* start method. Patch by Bénédikt Tran.
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue