Skip to content

Specialized STORE_SUBSCR_DICT allows silent mutation of frozendict after adaptive specialization #145122

Description

@abdoulrasheed

Bug report

Bug description:

Commit 3e2f5c1 broadened _GUARD_NOS_DICT from PyDict_CheckExact() to PyAnyDict_CheckExact() for read operations (BINARY_OP_SUBSCR_DICT, CONTAINS_OP_DICT). However, the same guard is also used by STORE_SUBSCR_DICT, which still calls _PyDict_SetItem_Take2() directly; bypassing frozendict's immutability checks entirely.

When a function containing d[key] = value is first specialized with dict, subsequent calls with frozendict pass the widened guard and mutate the frozendict at the C level. In debug builds this is an assertion crash; in release builds it's silent mutation with a stale cached hash.

defstore(d, key, val):
d[key] =val# specializeforiinrange(100):
store({}, 'k', i)
# now frozendict passes the widened guard and is mutatedfd=frozendict({'a': 1})
h_before=hash(fd)
store(fd, 'b', 2) # should raise TypeErrorprint(fd) # frozendict({'a': 1, 'b': 2}), mutatedprint(hash(fd) ==h_before) # True, stale hash

Before specialization, store(frozendict(...), 'b', 2) correctly raises TypeError: 'frozendict' object does not support item assignment.

After specialization, it silently mutates the frozendict and leaves the cached hash stale.

CPython versions tested on:

CPython main branch

Operating systems tested on:

macOS

Linked PRs

Metadata

Metadata

Assignees

Labels

3.15pre-release feature fixes, bugs and security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)release-blockertype-bugAn unexpected behavior, bug, or error

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions