Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
gh-84559: multiprocessing detect of forkserver cannot work due to mis…
…sing hmac-sha256

Default to the spawn start method in that scenario.
  • Loading branch information
gpshead committed Dec 2, 2024
commit 7a5ab6c0972074960b82e463e589b0cdd61db482
9 changes: 6 additions & 3 deletions Lib/multiprocessing/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from . import util

from . import AuthenticationError, BufferTooShort
from . import context
from .context import reduction
_ForkingPickler = reduction.ForkingPickler

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


def deliver_challenge(connection, authkey: bytes, digest_name='sha256'):
def deliver_challenge(connection, authkey: bytes, digest_name: str = ''):
if not isinstance(authkey, bytes):
raise ValueError(
"Authkey must be bytes, not {0!s}".format(type(authkey)))
if not digest_name:
digest_name = context._DIGEST_FOR_CONNECTION_HMAC
assert MESSAGE_LENGTH > _MD5ONLY_MESSAGE_LENGTH, "protocol constraint"
message = os.urandom(MESSAGE_LENGTH)
message = b'{%s}%s' % (digest_name.encode('ascii'), message)
Expand Down
24 changes: 23 additions & 1 deletion Lib/multiprocessing/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ class TimeoutError(ProcessError):
class AuthenticationError(ProcessError):
pass

# The default digest for multiprocessing.connection to use for auth.
# We configure it here so that it can be tested when choosing a
# default context without a circular import.
# Must be the str of a value seen in connection._ALLOWED_DIGESTS.
_DIGEST_FOR_CONNECTION_HMAC = 'sha256'

#
# Base type for contexts. Bound methods of an instance of this type are included in __all__ of __init__.py
#
Expand Down Expand Up @@ -313,6 +319,21 @@ class ForkServerContext(BaseContext):
def _check_available(self):
if not reduction.HAVE_SEND_HANDLE:
raise ValueError('forkserver start method not available')
if not _test_if_connection_can_work():
raise ValueError(f'forkserver start method not available due to missing hmac-{_DIGEST_FOR_CONNECTION_HMAC}')

def _test_if_connection_can_work() -> bool:
# Authenticated connections required for forkserver using hmac.
# If the algorithm is unavailable (poor FIPS mode config?) at
# import time, we cannot default to forkserver. If a user
# changes the _DIGEST_FOR_CONNECTION_HMAC to one that works in
# their strange config, the forkserver context will still work.
import hmac
try:
hmac.new(b'test-key'*8, b'', _DIGEST_FOR_CONNECTION_HMAC)
except ValueError:
return False
return True

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