fix(code-runtime-python): bind BaseException into every settlement-path except clause

The rebindable-BaseException vector the bot flagged existed in every except
clause of the settlement path, not just the _run outer catch: safe_model_traceback
(three guards) and the post-done flush swallow resolved the module-global
BaseException at runtime, so a __main__.BaseException rebind plus a throwing
__str__ could let a render-time exception escape and lose the done frame. Bind
BaseException into a _run local (at the top) and a closure cell in
_make_failure_reporter, and change every such except clause to catch the local
— immune to a one-line rebind.
This commit is contained in:
Chinesezjc
2026-08-31 14:34:09 +08:00
committed by Tianyi Cui
parent d5945546c7
commit 69dc17c906
@@ -727,6 +727,15 @@ def _clamped(which: int, soft: int, hard: int) -> tuple[int, int]:
async def _run(channel: ProtocolChannel) -> None:
# The exception class every `except` clause in this function catches is bound
# into a LOCAL at the very top, before any model code runs. This bootstrap IS
# `__main__`, so `__main__.BaseException = RuntimeError` would otherwise
# rebind the module global the `except BaseException` clauses resolve at
# runtime — and a program exception that no longer matches would escape `_run`
# with no done frame, misreporting the run as a `worker-exit`. A frame local
# is not reachable by `__main__._X = ...`, so the catch is immune.
_BaseException = BaseException
# 1. Boot handshake.
boot = channel.read_frame()
if boot is None or boot.get("type") != "boot":
@@ -786,7 +795,7 @@ async def _run(channel: ProtocolChannel) -> None:
"lower the budget or raise the inherited address-space limit"
% (_budget_key, effective_soft)
)
except BaseException as exc: # noqa: BLE001 -- report every failure to host
except _BaseException as exc: # noqa: BLE001 -- report every failure to host; `_BaseException` is a pre-program local
channel.send_sync(
{
"type": "done",
@@ -998,15 +1007,6 @@ async def _run(channel: ProtocolChannel) -> None:
_os_write_local = _os_write
_memoryview_local = _memoryview
_fallback_frame_local = _FALLBACK_DONE_FRAME
# The exception class the outer try/except catches is bound into a LOCAL
# here, before the program runs. This bootstrap is `__main__`, so
# `__main__.BaseException = RuntimeError` would otherwise rebind the module
# global `BaseException` the `except BaseException` clause resolves at
# runtime, and a subsequent `ValueError` would then not match the clause —
# escaping `_run` with no `done` frame and misreporting the run as a
# `worker-exit`. Binding the class into a local makes the catch immune to a
# one-line rebind.
_BaseException = BaseException
def send_done(payload: dict[str, Any] | str) -> None:
try:
@@ -1106,7 +1106,7 @@ async def _run(channel: ProtocolChannel) -> None:
for _flush in (flush_out, flush_err):
try:
_flush()
except BaseException: # noqa: BLE001 -- swallow ONLY the log tail; `done` must reach the host
except _BaseException: # noqa: BLE001 -- swallow ONLY the log tail; `done` must reach the host; `_BaseException` is a pre-program local
pass
reply_task.cancel()
send_done(done)
@@ -2159,11 +2159,18 @@ def _make_failure_reporter() -> Any:
cap_message = _cap_message
model_traceback = _model_traceback
unrenderable = _UNRENDERABLE_DIAGNOSTIC
# The exception class the guards below catch is bound into a closure cell
# here, at import time, before model code runs. `safe_model_traceback` runs
# AFTER the program (which is `__main__`) may have rebound the module global
# `BaseException`, so `except BaseException` would resolve the rebound class
# and a render-time throw could escape — losing the done frame. A closure cell
# is not reachable by `__main__._X = ...`, so the catch is immune.
_BaseException = BaseException
def safe_model_traceback(exc: BaseException, max_bytes: int) -> str:
try:
return cap_message(model_traceback(exc, max_bytes), max_bytes)
except BaseException: # noqa: BLE001 -- a throw here would cost the done frame
except _BaseException: # noqa: BLE001 -- a throw here would cost the done frame
pass
try:
raw_name = type(exc).__name__
@@ -2176,14 +2183,14 @@ def _make_failure_reporter() -> Any:
# code-unit prefix, which bounds the bytes at 4x, and the following
# `cap_message` still applies the exact byte cap.
name = raw_name[:_MAX_FALLBACK_NAME_CHARS] if type(raw_name) is str else "<unknown>"
except BaseException: # noqa: BLE001 -- a raising __name__ must not cost the done frame
except _BaseException: # noqa: BLE001 -- a raising __name__ must not cost the done frame
name = "<unknown>"
# Wrapped for the same reason: `cap_message` encodes, and its allocation
# is the only step left that can still fail. The fixed literal needs no
# budget, so it can always be delivered.
try:
return cap_message(f"{name}: {unrenderable}", max_bytes)
except BaseException: # noqa: BLE001 -- the done frame outranks the diagnostic's detail
except _BaseException: # noqa: BLE001 -- the done frame outranks the diagnostic's detail
return unrenderable
return safe_model_traceback