Bug description:
The specialized list/int subscript and store paths can be entered after a code object was specialized with a compact non-negative int index, then later called with a non-compact exact int index.
At specialization time, BINARY_OP_SUBSCR_LIST_INT and STORE_SUBSCR_LIST_INT require _PyLong_IsNonNegativeCompact(sub), but the runtime macro only guards with _GUARD_TOS_INT. The specialized operation then calls _PyLong_CompactValue() without rechecking compactness.
On debug builds this can trip the _PyLong_CompactValue() assertion. On release builds, a non-compact value whose low digit is zero, such as 1 << 64, can be treated as index 0 rather than falling back to generic list indexing and raising IndexError.
Minimal CPython-internal reproducer:
import dis
import _testinternalcapi
def get(index):
a = [1, 2, 3]
return a[index]
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
assert get(0) == 1
assert "BINARY_OP_SUBSCR_LIST_INT" in {
instruction.opname for instruction in dis.get_instructions(get, adaptive=True)
}
get(1 << 64)
The same invariant issue applies to STORE_SUBSCR_LIST_INT after warming with index 0 and then storing with 1 << 64.
CPython versions tested on:
CPython main branch
Operating systems tested on:
macOS
Linked PRs
Bug description:
The specialized list/int subscript and store paths can be entered after a code object was specialized with a compact non-negative int index, then later called with a non-compact exact int index.
At specialization time,
BINARY_OP_SUBSCR_LIST_INTandSTORE_SUBSCR_LIST_INTrequire_PyLong_IsNonNegativeCompact(sub), but the runtime macro only guards with_GUARD_TOS_INT. The specialized operation then calls_PyLong_CompactValue()without rechecking compactness.On debug builds this can trip the
_PyLong_CompactValue()assertion. On release builds, a non-compact value whose low digit is zero, such as1 << 64, can be treated as index0rather than falling back to generic list indexing and raisingIndexError.Minimal CPython-internal reproducer:
The same invariant issue applies to
STORE_SUBSCR_LIST_INTafter warming with index0and then storing with1 << 64.CPython versions tested on:
CPython main branch
Operating systems tested on:
macOS
Linked PRs