Skip to content

Commit 7a5ab6c

Browse files
committed
gh-84559: multiprocessing detect of forkserver cannot work due to missing hmac-sha256
Default to the spawn start method in that scenario.
1 parent bd46651 commit 7a5ab6c

File tree

3 files changed

+33
-4
lines changed

3 files changed

+33
-4
lines changed

Lib/multiprocessing/connection.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from . import util
2424

2525
from . import AuthenticationError, BufferTooShort
26+
from . import context
2627
from .context import reduction
2728
_ForkingPickler = reduction.ForkingPickler
2829

@@ -899,9 +900,9 @@ def _create_response(authkey, message):
899900
return hmac.new(authkey, message, 'md5').digest()
900901
except ValueError:
901902
# HMAC-MD5 is not available (FIPS mode?), fall back to
902-
# HMAC-SHA2-256 modern protocol. The legacy server probably
903+
# our modern HMAC-SHA* protocol. The legacy server probably
903904
# doesn't support it and will reject us anyways. :shrug:
904-
digest_name = 'sha256'
905+
digest_name = context._DIGEST_FOR_CONNECTION_HMAC
905906
# Modern protocol, indicate the digest used in the reply.
906907
response = hmac.new(authkey, message, digest_name).digest()
907908
return b'{%s}%s' % (digest_name.encode('ascii'), response)
@@ -932,10 +933,12 @@ def _verify_challenge(authkey, message, response):
932933
raise AuthenticationError('digest received was wrong')
933934

934935

935-
def deliver_challenge(connection, authkey: bytes, digest_name='sha256'):
936+
def deliver_challenge(connection, authkey: bytes, digest_name: str = ''):
936937
if not isinstance(authkey, bytes):
937938
raise ValueError(
938939
"Authkey must be bytes, not {0!s}".format(type(authkey)))
940+
if not digest_name:
941+
digest_name = context._DIGEST_FOR_CONNECTION_HMAC
939942
assert MESSAGE_LENGTH > _MD5ONLY_MESSAGE_LENGTH, "protocol constraint"
940943
message = os.urandom(MESSAGE_LENGTH)
941944
message = b'{%s}%s' % (digest_name.encode('ascii'), message)

Lib/multiprocessing/context.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ class TimeoutError(ProcessError):
2323
class AuthenticationError(ProcessError):
2424
pass
2525

26+
# The default digest for multiprocessing.connection to use for auth.
27+
# We configure it here so that it can be tested when choosing a
28+
# default context without a circular import.
29+
# Must be the str of a value seen in connection._ALLOWED_DIGESTS.
30+
_DIGEST_FOR_CONNECTION_HMAC = 'sha256'
31+
2632
#
2733
# Base type for contexts. Bound methods of an instance of this type are included in __all__ of __init__.py
2834
#
@@ -313,6 +319,21 @@ class ForkServerContext(BaseContext):
313319
def _check_available(self):
314320
if not reduction.HAVE_SEND_HANDLE:
315321
raise ValueError('forkserver start method not available')
322+
if not _test_if_connection_can_work():
323+
raise ValueError(f'forkserver start method not available due to missing hmac-{_DIGEST_FOR_CONNECTION_HMAC}')
324+
325+
def _test_if_connection_can_work() -> bool:
326+
# Authenticated connections required for forkserver using hmac.
327+
# If the algorithm is unavailable (poor FIPS mode config?) at
328+
# import time, we cannot default to forkserver. If a user
329+
# changes the _DIGEST_FOR_CONNECTION_HMAC to one that works in
330+
# their strange config, the forkserver context will still work.
331+
import hmac
332+
try:
333+
hmac.new(b'test-key'*8, b'', _DIGEST_FOR_CONNECTION_HMAC)
334+
except ValueError:
335+
return False
336+
return True
316337

317338
_concrete_contexts = {
318339
'fork': ForkContext(),
@@ -322,7 +343,8 @@ def _check_available(self):
322343
# bpo-33725: running arbitrary code after fork() is no longer reliable
323344
# on macOS since macOS 10.14 (Mojave). Use spawn by default instead.
324345
# gh-84559: We changed everyones default to a thread safeish one in 3.14.
325-
if reduction.HAVE_SEND_HANDLE and sys.platform != 'darwin':
346+
if (reduction.HAVE_SEND_HANDLE and sys.platform != 'darwin' and
347+
_test_if_connection_can_work()):
326348
_default_context = DefaultContext(_concrete_contexts['forkserver'])
327349
else:
328350
_default_context = DefaultContext(_concrete_contexts['spawn'])
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
On hosts without the hmac-sha256 algorithm available (rare),
2+
:mod:`multiprocessing` will default to the ``"spawn"`` start method instead
3+
of ``"forkserver"`` which requires the algorithm for control socket
4+
authentication.

0 commit comments

Comments
 (0)