The AST sandbox whitelisted ** and << with no magnitude bound, so a hostile
profile formula (9**9**9, 1<<10**9) computed a multi-hundred-MB integer on the
scheduler thread -> CPU pin + OOM. The scheduler except clause never catches a
runaway/OOM (not a raised exception), and a derived PID with empty deps fires
every tick on connect.
- _apply() guards each BinOp: shift amount <= 256, exponent <= 64, and any int
result bit_length > 512 raises FormulaError (caught by the scheduler -> sample
dropped, thread survives).
- compile-time caps: expr length <= 500, AST depth <= 60; parse also catches
RecursionError.
- test_formula_dos_bounded: giant-int payloads rejected in <0.5s; legit bit ops
and scaling still work.
Closes#6
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_016yT89n4zR4qbrySoSiEyZs