Deferred from PR #131 (Python bindings code review, 2026-07-02).
Problem
The native binding walks the incoming Python object graph via depythonize with no recursion-depth bound:
crates/mds-python/src/lib.rs — extract_vars (vars), parse_modules (modules), and CompileResult.__new__ (unpickle).
A pathologically deep structure (buildable iteratively, then handed to compile(..., vars=…) / compile_virtual(modules=…) / pickle.loads) could overflow the Rust stack → an uncatchable process abort. This also sidesteps the crate's "never panic across the FFI boundary" guarantee, because extract_vars/parse_modules run before run_catching.
Severity is low in practice: input is in-process, same-trust caller (not network-facing) — this is defense-in-depth. Reviewer confidence ~70%, filed as a Suggestion (not a blocker) in the #131 review.
Why deferred (not fixed in #131)
pyo3/pythonize 0.29 expose no depth/recursion-limit knob. A correct fix needs either a custom recursion-limited Deserializer wrapping pythonize::Depythonizer, or a hand-rolled pre-walk replicating its full type dispatch — genuine surgery with a real risk of silent incompleteness (miss one container type → false confidence).
Scope — cross-binding
The same latent gap exists in mds-napi (its depythonize equivalent). The correct fix is a coordinated bound applied at both native-binding boundaries so parity is preserved — not a unilateral change in one crate.
Acceptance criteria
Target: v0.4.0.
Deferred from PR #131 (Python bindings code review, 2026-07-02).
Problem
The native binding walks the incoming Python object graph via
depythonizewith no recursion-depth bound:crates/mds-python/src/lib.rs—extract_vars(vars),parse_modules(modules), andCompileResult.__new__(unpickle).A pathologically deep structure (buildable iteratively, then handed to
compile(..., vars=…)/compile_virtual(modules=…)/pickle.loads) could overflow the Rust stack → an uncatchable process abort. This also sidesteps the crate's "never panic across the FFI boundary" guarantee, becauseextract_vars/parse_modulesrun beforerun_catching.Severity is low in practice: input is in-process, same-trust caller (not network-facing) — this is defense-in-depth. Reviewer confidence ~70%, filed as a Suggestion (not a blocker) in the #131 review.
Why deferred (not fixed in #131)
pyo3/pythonize0.29 expose no depth/recursion-limit knob. A correct fix needs either a custom recursion-limitedDeserializerwrappingpythonize::Depythonizer, or a hand-rolled pre-walk replicating its full type dispatch — genuine surgery with a real risk of silent incompleteness (miss one container type → false confidence).Scope — cross-binding
The same latent gap exists in mds-napi (its
depythonizeequivalent). The correct fix is a coordinated bound applied at both native-binding boundaries so parity is preserved — not a unilateral change in one crate.Acceptance criteria
mds::invalid_options(ormds::resource_limit) error instead of aborting on deeply-nested input.vars/modulespayload asserting the clean error.Target: v0.4.0.