You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
BINARY_OP_SUBSCR_LIST_INT / STORE_SUBSCR_LIST_INT silently depend on _GUARD_TOS_INT enforcing compactness #150587
_BINARY_OP_SUBSCR_LIST_INT and _STORE_SUBSCR_LIST_INT call _PyLong_CompactValue
on the subscript without verifying compactness themselves. They rely entirely on _GUARD_TOS_INT to have already rejected any non-compact int. This is an undocumented
invariant: if _GUARD_TOS_INT is ever relaxed, _PyLong_CompactValue will be called
on a wide int, reading ob_digit[0] as the index instead of the full value and
silently returning the wrong list element.
Details
The specializer gates BINARY_OP_SUBSCR_LIST_INT
(specialize.c:2391)
and STORE_SUBSCR_LIST_INT
(specialize.c:1575)
on _PyLong_IsNonNegativeCompact — only compact non-negative subscripts trigger
specialization. The runtime guard _GUARD_TOS_INT
(bytecodes.c:640)
currently checks _PyLong_CheckExactAndCompact, so the invariant holds today.
However, the opcode bodies
(_BINARY_OP_SUBSCR_LIST_INT, _STORE_SUBSCR_LIST_INT)
do not re-verify compactness before calling _PyLong_CompactValue
(longintrepr.h:130).
If _GUARD_TOS_INT were relaxed to accept wider ints (e.g. to PyLong_CheckExact),
a wide int subscript would pass the guard and reach _PyLong_CompactValue in an
invalid state. For a wide positive int n = ob_digit[0] + ob_digit[1] * 2^30 + ..., _PyLong_CompactValue reads only ob_digit[0], silently producing the wrong index.
On a debug build this fires assert(PyUnstable_Long_IsCompact(op)) inside _PyLong_CompactValue.
Fix
Add EXIT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub)) to _BINARY_OP_SUBSCR_LIST_INT and DEOPT_IF(!_PyLong_IsNonNegativeCompact((PyLongObject *)sub))
to _STORE_SUBSCR_LIST_INT, immediately before the _PyLong_CompactValue call.
Each opcode then enforces its own precondition rather than relying on the guard's
current strength.
Following up with a focused standalone PR in gh-153004. The earlier PR gh-150588 was closed because the fix was moved into gh-150425, but that broader performance PR has not landed, so this keeps the correctness fix separate.
Activity
pythongh-150587: Harden BINARY_OP_SUBSCR_LIST_INT/STORE_SUBSCR_LIST_I…
pythongh-150587: Add blurb
KRRT7 commentedon May 30, 2026
my work on #150425 is what triggers this bug, and is why I had opened this issue.
after a chat with @ZeroIntensity I'm moving the patch into 150425
KRRT7 commentedon May 30, 2026
I could've sworn I clicked on not planned....
pythongh-150587: Guard specialized list-int subscript indices
KRRT7 commentedon Jul 4, 2026
Following up with a focused standalone PR in gh-153004. The earlier PR gh-150588 was closed because the fix was moved into gh-150425, but that broader performance PR has not landed, so this keeps the correctness fix separate.
pythongh-150587: Use EXIT_IF for store subscript compact guard
pythongh-150587: Regenerate opcode metadata
pythongh-150587: Factor non-negative compact int subscript guards