Skip to content

BINARY_OP_SUBSCR_LIST_INT / STORE_SUBSCR_LIST_INT silently depend on _GUARD_TOS_INT enforcing compactness #150587

Description

@KRRT7

Summary

_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.

Linked PRs

Activity

  1. added 2 commits that reference this issue on May 29, 2026
  2. added
    type-bugAn unexpected behavior, bug, or error
    interpreter-core(Objects, Python, Grammar, and Parser dirs)
    on May 29, 2026
  3. KRRT7 commented on May 30, 2026

    @KRRT7
    Author

    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

  4. KRRT7 commented on May 30, 2026

    @KRRT7
    Author

    I could've sworn I clicked on not planned....

  5. added a commit that references this issue on Jul 4, 2026
  6. KRRT7 commented on Jul 4, 2026

    @KRRT7
    Author

    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.

  7. added 3 commits that reference this issue on Jul 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions