shared × unstructured — bad data written permanently, and it spreads
NaN Poisons a Shared Aggregate
A single divide-by-zero feeds NaN into a shared running total; NaN is absorbing, so every later reader gets garbage forever.
01the recipe
In the wild
compound ofDivide by ZeroCWE-369 Divide by ZeroThreading & MutexescompoundCWE-682 Incorrect Calculation
example.py
# SMELL: a /0 produces NaN that lands in a shared accumulator.
# (divide-by-zero x threading / mutexes)
import math
stats = {"sum": 0.0}
def record(latency, n):
with lock:
stats["sum"] += latency / n # n == 0 -> NaN; NaN + x == NaN
# one bad sample (n == 0) makes stats["sum"] NaN forever; every reader of the
# shared aggregate now gets NaN -- the dashboard is permanently wrong.
# RIGHT: reject the non-finite value before it touches shared state.
def record(latency, n):
if n == 0: return
v = latency / n
if not math.isfinite(v): return
with lock:
stats["sum"] += vNaN is absorbing: once a single divide-by-zero feeds NaN into the shared running total, every later add stays NaN and every reader downstream sees garbage. The lock makes the write thread-safe but cannot un-poison the value -- the corruption is permanent and shared. Validate the numeric domain before it crosses into shared state.
// observed
poisoned: one n==0 sample -> shared sum is NaN for all readers, forever right: non-finite values rejected; the shared aggregate stays valid
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.