Skip to content

gh-151892: Fix crash unpickling interpreters.call() result#151893

Open
tonghuaroot wants to merge 2 commits into
python:mainfrom
tonghuaroot:fix-crossinterp-pyxi-exit-stale-exc
Open

gh-151892: Fix crash unpickling interpreters.call() result#151893
tonghuaroot wants to merge 2 commits into
python:mainfrom
tonghuaroot:fix-crossinterp-pyxi-exit-stale-exc

Conversation

@tonghuaroot

@tonghuaroot tonghuaroot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

When Interpreter.call() returns a value, it is preserved across the cross-interpreter session and reconstructed (unpickled) in the calling interpreter inside _PyXI_Exit() (Python/crossinterp.c). If reconstruction raises, the live exception was left set while the function applied its _PyXI_ERR_PRESERVE_FAILURE override, so the subsequent assert(!_PyErr_Occurred(tstate)) aborted on debug builds (and the stale exception was clobbered on release builds).

The reconstruction-failure branch now consumes the live exception and propagates it as the failure, mirroring how the active-session _pop_preserved path already routes errors through capture_session_error. call() now raises concurrent.interpreters.NotShareableError (with the original exception reachable as the cause) instead of crashing; a NotShareableError raised during reconstruction is propagated the same way.

A regression test is added in Lib/test/test_interpreters/test_api.py covering caller-side result-unpickling failures (a plain exception and a NotShareableError); it aborts without the fix and passes with it. test_interpreters / test__interpreters pass and the new test is refleak-clean under -R 3:3.

The affected machinery (_PyXI_Preserve / _finish_preserved, xi_error_*) postdates the 3.14.0b1 freeze, so this targets main only — no backport is needed.

A separate, distinct crossinterp NULL dereference on the queue/channel receive path is tracked in #151862.

When call() result reconstruction in the calling interpreter raised (e.g.
unpickling failed), the live exception was left set while _PyXI_Exit()
applied the preserve-failure override, tripping an assertion.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant