Description
BuildBackendHookCaller.subprocess_runner() context manager mutates self._subprocess_runner on the instance without any synchronization:
@contextmanager
def subprocess_runner(self, runner):
prev = self._subprocess_runner
self._subprocess_runner = runner
try:
yield
finally:
self._subprocess_runner = prev
If two threads share the same BuildBackendHookCaller instance and one of them uses subprocess_runner() to temporarily swap the runner, the other thread sees the swapped runner and may invoke hooks with the wrong runner. The restore in finally also races with concurrent reads of self._subprocess_runner in _call_hook.
Impact
This prevents consumers (e.g. pypa/build) from safely sharing a BuildBackendHookCaller (or a wrapping ProjectBuilder) across threads when subprocess_runner() is used.
Discovered during a thread-safety audit of pypa/build for pypa/build#377.
Description
BuildBackendHookCaller.subprocess_runner()context manager mutatesself._subprocess_runneron the instance without any synchronization:If two threads share the same
BuildBackendHookCallerinstance and one of them usessubprocess_runner()to temporarily swap the runner, the other thread sees the swapped runner and may invoke hooks with the wrong runner. The restore infinallyalso races with concurrent reads ofself._subprocess_runnerin_call_hook.Impact
This prevents consumers (e.g.
pypa/build) from safely sharing aBuildBackendHookCaller(or a wrappingProjectBuilder) across threads whensubprocess_runner()is used.Discovered during a thread-safety audit of
pypa/buildfor pypa/build#377.