Fix #6: bound formula evaluation to stop untrusted-profile DoS
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) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yT89n4zR4qbrySoSiEyZs
This commit is contained in:
@@ -63,6 +63,24 @@ def test_formula_is_sandboxed():
|
||||
print(" formula evaluator rejects code/unknowns: OK")
|
||||
|
||||
|
||||
def test_formula_dos_bounded():
|
||||
import time
|
||||
# giant-integer / resource-exhaustion payloads must be blocked fast, not
|
||||
# allowed to freeze the polling thread
|
||||
for bad in ("9**9**9", "1<<10**9", "2**5000", "10**9**9", "A<<100000000",
|
||||
"A+" * 300 + "A"):
|
||||
t = time.time()
|
||||
try:
|
||||
fn = compile_formula(bad, "AB")
|
||||
fn({"A": 250, "B": 250})
|
||||
raise AssertionError(f"formula not bounded: {bad}")
|
||||
except FormulaError:
|
||||
assert time.time() - t < 0.5, f"{bad} was slow to reject"
|
||||
# legit bit-field / scaling formulas still work
|
||||
assert compile_formula("(A<<8)|B", "AB")({"A": 1, "B": 2}) == 258
|
||||
print(" formula DoS payloads bounded (<0.5s), legit bit ops intact: OK")
|
||||
|
||||
|
||||
def test_registry_decoders_match_truck_bytes():
|
||||
reg = PidRegistry(load_default())
|
||||
cases = {
|
||||
@@ -138,6 +156,7 @@ def test_record_replay_roundtrip(tmp_path=None):
|
||||
|
||||
if __name__ == "__main__":
|
||||
for fn in [test_profiles_load_and_validate, test_formula_is_sandboxed,
|
||||
test_formula_dos_bounded,
|
||||
test_registry_decoders_match_truck_bytes, test_crank_ramp_and_peak,
|
||||
test_derived_boost_channel, test_dead_pid_parks_and_revives,
|
||||
test_record_replay_roundtrip]:
|
||||
|
||||
Reference in New Issue
Block a user