Skip to content

Commit d0cd049

Browse files
picnixzlkollar
authored andcommitted
pythongh-132124: improve safety nets for creating AF_UNIX socket files (pythonGH-134085)
* ensure that we can create AF_UNIX socket files * emit a warning if system-wide temporary directory is used
1 parent dbd0da3 commit d0cd049

File tree

4 files changed

+87
-5
lines changed

4 files changed

+87
-5
lines changed

Lib/multiprocessing/connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def arbitrary_address(family):
7676
if family == 'AF_INET':
7777
return ('localhost', 0)
7878
elif family == 'AF_UNIX':
79-
return tempfile.mktemp(prefix='listener-', dir=util.get_temp_dir())
79+
return tempfile.mktemp(prefix='sock-', dir=util.get_temp_dir())
8080
elif family == 'AF_PIPE':
8181
return tempfile.mktemp(prefix=r'\\.\pipe\pyc-%d-%d-' %
8282
(os.getpid(), next(_mmap_counter)), dir="")

Lib/multiprocessing/util.py

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from . import process
2020

2121
__all__ = [
22-
'sub_debug', 'debug', 'info', 'sub_warning', 'get_logger',
22+
'sub_debug', 'debug', 'info', 'sub_warning', 'warn', 'get_logger',
2323
'log_to_stderr', 'get_temp_dir', 'register_after_fork',
2424
'is_exiting', 'Finalize', 'ForkAwareThreadLock', 'ForkAwareLocal',
2525
'close_all_fds_except', 'SUBDEBUG', 'SUBWARNING',
@@ -34,6 +34,7 @@
3434
DEBUG = 10
3535
INFO = 20
3636
SUBWARNING = 25
37+
WARNING = 30
3738

3839
LOGGER_NAME = 'multiprocessing'
3940
DEFAULT_LOGGING_FORMAT = '[%(levelname)s/%(processName)s] %(message)s'
@@ -53,6 +54,10 @@ def info(msg, *args):
5354
if _logger:
5455
_logger.log(INFO, msg, *args, stacklevel=2)
5556

57+
def warn(msg, *args):
58+
if _logger:
59+
_logger.log(WARNING, msg, *args, stacklevel=2)
60+
5661
def sub_warning(msg, *args):
5762
if _logger:
5863
_logger.log(SUBWARNING, msg, *args, stacklevel=2)
@@ -121,6 +126,21 @@ def is_abstract_socket_namespace(address):
121126
# Function returning a temp directory which will be removed on exit
122127
#
123128

129+
# Maximum length of a socket file path is usually between 92 and 108 [1],
130+
# but Linux is known to use a size of 108 [2]. BSD-based systems usually
131+
# use a size of 104 or 108 and Windows does not create AF_UNIX sockets.
132+
#
133+
# [1]: https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/sys_un.h.html
134+
# [2]: https://man7.org/linux/man-pages/man7/unix.7.html.
135+
136+
if sys.platform == 'linux':
137+
_SUN_PATH_MAX = 108
138+
elif sys.platform.startswith(('openbsd', 'freebsd')):
139+
_SUN_PATH_MAX = 104
140+
else:
141+
# On Windows platforms, we do not create AF_UNIX sockets.
142+
_SUN_PATH_MAX = None if os.name == 'nt' else 92
143+
124144
def _remove_temp_dir(rmtree, tempdir):
125145
rmtree(tempdir)
126146

@@ -130,12 +150,67 @@ def _remove_temp_dir(rmtree, tempdir):
130150
if current_process is not None:
131151
current_process._config['tempdir'] = None
132152

153+
def _get_base_temp_dir(tempfile):
154+
"""Get a temporary directory where socket files will be created.
155+
156+
To prevent additional imports, pass a pre-imported 'tempfile' module.
157+
"""
158+
if os.name == 'nt':
159+
return None
160+
# Most of the time, the default temporary directory is /tmp. Thus,
161+
# listener sockets files "$TMPDIR/pymp-XXXXXXXX/sock-XXXXXXXX" do
162+
# not have a path length exceeding SUN_PATH_MAX.
163+
#
164+
# If users specify their own temporary directory, we may be unable
165+
# to create those files. Therefore, we fall back to the system-wide
166+
# temporary directory /tmp, assumed to exist on POSIX systems.
167+
#
168+
# See https://github.com/python/cpython/issues/132124.
169+
base_tempdir = tempfile.gettempdir()
170+
# Files created in a temporary directory are suffixed by a string
171+
# generated by tempfile._RandomNameSequence, which, by design,
172+
# is 8 characters long.
173+
#
174+
# Thus, the length of socket filename will be:
175+
#
176+
# len(base_tempdir + '/pymp-XXXXXXXX' + '/sock-XXXXXXXX')
177+
sun_path_len = len(base_tempdir) + 14 + 14
178+
if sun_path_len <= _SUN_PATH_MAX:
179+
return base_tempdir
180+
# Fallback to the default system-wide temporary directory.
181+
# This ignores user-defined environment variables.
182+
#
183+
# On POSIX systems, /tmp MUST be writable by any application [1].
184+
# We however emit a warning if this is not the case to prevent
185+
# obscure errors later in the execution.
186+
#
187+
# On some legacy systems, /var/tmp and /usr/tmp can be present
188+
# and will be used instead.
189+
#
190+
# [1]: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/ch03s18.html
191+
dirlist = ['/tmp', '/var/tmp', '/usr/tmp']
192+
try:
193+
base_system_tempdir = tempfile._get_default_tempdir(dirlist)
194+
except FileNotFoundError:
195+
warn("Process-wide temporary directory %s will not be usable for "
196+
"creating socket files and no usable system-wide temporary "
197+
"directory was found in %s", base_tempdir, dirlist)
198+
# At this point, the system-wide temporary directory is not usable
199+
# but we may assume that the user-defined one is, even if we will
200+
# not be able to write socket files out there.
201+
return base_tempdir
202+
warn("Ignoring user-defined temporary directory: %s", base_tempdir)
203+
# at most max(map(len, dirlist)) + 14 + 14 = 36 characters
204+
assert len(base_system_tempdir) + 14 + 14 <= _SUN_PATH_MAX
205+
return base_system_tempdir
206+
133207
def get_temp_dir():
134208
# get name of a temp directory which will be automatically cleaned up
135209
tempdir = process.current_process()._config.get('tempdir')
136210
if tempdir is None:
137211
import shutil, tempfile
138-
tempdir = tempfile.mkdtemp(prefix='pymp-')
212+
base_tempdir = _get_base_temp_dir(tempfile)
213+
tempdir = tempfile.mkdtemp(prefix='pymp-', dir=base_tempdir)
139214
info('created temp directory %s', tempdir)
140215
# keep a strong reference to shutil.rmtree(), since the finalizer
141216
# can be called late during Python shutdown

Lib/tempfile.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ def _candidate_tempdir_list():
180180

181181
return dirlist
182182

183-
def _get_default_tempdir():
183+
def _get_default_tempdir(dirlist=None):
184184
"""Calculate the default directory to use for temporary files.
185185
This routine should be called exactly once.
186186
@@ -190,7 +190,8 @@ def _get_default_tempdir():
190190
service, the name of the test file must be randomized."""
191191

192192
namer = _RandomNameSequence()
193-
dirlist = _candidate_tempdir_list()
193+
if dirlist is None:
194+
dirlist = _candidate_tempdir_list()
194195

195196
for dir in dirlist:
196197
if dir != _os.curdir:
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
On POSIX-compliant systems, :func:`!multiprocessing.util.get_temp_dir` now
2+
ignores :envvar:`TMPDIR` (and similar environment variables) if the path
3+
length of ``AF_UNIX`` socket files exceeds the platform-specific maximum
4+
length when using the :ref:`forkserver
5+
<multiprocessing-start-method-forkserver>` start method. Patch by Bénédikt
6+
Tran.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy