BLOCKED: Monty 0.0.21 upgrade silently disables PythonLimits::max_memory - #2298
BLOCKED: Monty 0.0.21 upgrade silently disables PythonLimits::max_memory#2298chaliy wants to merge 1 commit into
Conversation
Dependabot split this upgrade into two PRs (#2296 monty-types, #2297 monty). Either one alone leaves two `monty-types` versions in the graph, so its result types stop unifying and ~22 CI checks go red. Bumping both together reduces that to two real API breaks: * `ResourceLimits::new()` -> `default()`, and `max_recursion_depth` now takes `usize` rather than `Option<usize>`. * `ResourceTracker` changed from a host-implementable trait into a concrete struct and `LimitedTracker` was removed, so the `BudgetTracker` wrapper that charged the shared `ExecutionBudget` from inside the VM's own allocation/statement checkpoints no longer has anything to hook into, and 0.0.21 ships no replacement hook. Python now drives the shared budget the same way the TypeScript builtin always has: an up-front charge (input bytes, code size, and a reserve proportional to the VM memory ceiling) plus per-round-trip charging in the start/resume loop. Containment is unchanged, because it was never the tracker's job — `max_duration` is clamped to the caller's remaining execution deadline before the VM starts, and Monty's own tracker still enforces that duration plus `max_memory` and the recursion ceiling synchronously. Only the finer-grained work-unit accounting for a script that never re-enters the host loop is lost; the up-front reserve approximates it. monty 0.0.21 tracks the published jiter 0.16.0, which is already on pyo3 0.29 — the exact condition the `[patch.crates-io]` jiter git pin documented for its own removal — so that pin is dropped. Adds a regression test for the charging path that now carries the load, so removing host-loop charging fails loudly rather than silently.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
bashkit | 74bf892 | Commit Preview URL Branch Preview URL |
Aug 14 2026, 09:22 AM |
|
Closing this rather than leaving it open, because it is not actionable until the upstream memory-enforcement question is settled, and I need the branch for other work. Nothing is lost. The full analysis stays in the description above, and the blocker is recorded on the two dependabot PRs it affects — #2296 and #2297 — which remain open as the tracking items for this upgrade. The port itself is preserved at commit Recap of why this must not merge: Monty 0.0.21 silently turns Generated by Claude Code |
What I found
Upgrading to Monty 0.0.21 silently turns
PythonLimits::max_memoryinto a no-op for every embedder of thebashkitlibrary. Memory bombs that are contained today run to completion.Evidence —
nested_list_bomballocates 1,000,000 list items under a 2 MB limit:Exit code 0. Five tests fail on exactly this axis:
Why it happens
0.0.21 reworked resource tracking in two ways that compound:
ResourceTrackertrait became a concrete struct, andLimitedTrackerwas deleted.on_grow/on_free— the per-allocation VM heap-growth hooks — no longer exist at all. Bashkit'sBudgetTrackerused exactly those to meter the VM.max_memoryis now evaluated as:Those statics are only populated by
monty-allocinstalled as the process#[global_allocator]and armed viaset_limit. Unarmed, the defaults areLIVE_MEMORY = 0andBASELINE_MEMORY = usize::MAX, sosaturating_subpinsusedat 0 — andused > limitis never true. The limit is not merely loose; it can never fire.Verified nothing arms it, and that
monty-allocis not even in the dependency tree:Why I can't just fix it here
Enforcement now requires being the process's global allocator.
bashkitis a library: a#[global_allocator]in a library is imposed on every downstream binary and collides with any allocator the embedder chose. There is no host-side hook left to reimplement the metering against —on_grow/on_freeare gone, andResourceTracker::check_allocationis only useful to whoever performs the allocation, which is the VM, not us.So this is genuinely blocked upstream, not a porting problem. Options, none of which I want to pick unilaterally:
max_memoryenforced, revisit when upstream restores a host hook.max_memoryas unenforced-in-library, and armmonty-alloconly inbashkit-cli— this narrows the guarantee for library embedders and needs an explicit, deliberate decision plus a threat-model change.What is otherwise ready on this branch
If the memory question gets resolved, the rest of the port is done and compiles clean:
montyandmonty-typesbumped together — dependabot's split (chore(deps): bump monty-types from 0.0.19 to 0.0.21 #2296 / chore(deps): bump monty from 0.0.19 to 0.0.21 #2297) can never pass alone, because one version of each leaves twomonty-typesin the graph and its result types stop unifying (monty_types::results::ExtFunctionResultvsmonty_types::ExtFunctionResult). That single cause produced ~22 red checks on each of those PRs.ResourceLimits::new()→default();max_recursion_depthnow takesusize, notOption<usize>.[patch.crates-io]jiter git pin is dropped — 0.0.21 tracks the publishedjiter 0.16.0, already on pyo3 0.29, which is precisely the removal condition that pin documented for itself. The workspace builds from published crates again.cargo vetreports only the 3 expected new entries (jiter 0.16.0,ruff_python_codegen 0.0.3,ruff_python_literal 0.0.3).cargo auditon the 0.0.21 graph is clean — no vulnerabilities, only the 2 already-suppressed unmaintained warnings.Time/recursion containment does survive the port (
bash_timeout_clamps_python_durationpasses;max_durationis clamped to the caller's remaining deadline before the VM starts). It is specifically and onlymax_memorythat breaks.