mutated × shared — the answer changes between runs
Lost Update (Read-Modify-Write Race)
Incrementing shared state is three steps — read, add, write — and without a lock two actors overwrite each other's result.
01the recipe
In the wild
compound ofThreading & MutexesCWE-362 Race Condition++ / -- & Integer OverflowcompoundCWE-567 Unsynchronized Access
example.py
# SMELL: read-modify-write on shared state with no lock.
# (threading-mutexes x increment / decrement)
balance = 0
def deposit():
global balance
balance = balance + 1 # read, +1, write -- not atomic
# 1000 threads each deposit() -> balance is often < 1000:
# updates interleave and clobber one another.
# RIGHT: serialize the critical section.
lock = Lock()
def deposit():
global balance
with lock:
balance += 1Two threads read the same value, both add one, both write back -- one increment vanishes. The final count differs every run.
// observed
race: balance = 987 (varies every run) right: balance = 1000 (always)
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.