-
-
Notifications
You must be signed in to change notification settings - Fork 34.1k
Description
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.
def store(d, key, val):
d[key] = val
# specialize
for i in range(100):
store({}, 'k', i)
# now frozendict passes the widened guard and is mutated
fd = frozendict({'a': 1})
h_before = hash(fd)
store(fd, 'b', 2) # should raise TypeError
print(fd) # frozendict({'a': 1, 'b': 2}), mutated
print(hash(fd) == h_before) # True, stale hashBefore 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
Projects
Status