mutated × shared — the answer changes between runs
Concurrent Modification During Iteration
One actor mutates a shared collection while another walks it — the loop sometimes throws, sometimes silently skips.
01the recipe
In the wild
compound offor-Loop Control FlowThreading & MutexesCWE-820 Missing SynchronizationcompoundCWE-362 Race ConditionCWE-567 Unsynchronized Access
example.py
# SMELL: iterate a shared collection while another thread mutates it.
# (for-loop control flow x threading & mutexes)
for sid, s in sessions.items(): # another thread runs sessions.pop(...)
if s.expired:
reap(sid) # -> RuntimeError, or entries silently skipped
# RIGHT: iterate a snapshot; mutate the original under a lock.
for sid, s in list(sessions.items()): # stable copy to walk
if s.expired:
with lock:
sessions.pop(sid, None)Reading and writing one collection from two actors with no lock makes the iterator's behavior depend on timing: sometimes it raises, sometimes it skips or double-visits entries. The failure mode changes run to run.
// observed
race: RuntimeError: dictionary changed size during iteration (sometimes) right: snapshot iterated; mutation serialized, no surprise
02weakness catalog
Mapped weaknesses (CWE)
On its own, this defect is catalogued by MITRE as one or more of these weaknesses. The exploitable vulnerability usually appears only when it chains or combines with another.