Repository navigation
Harden timed value traversal and serialization resource errors - #920
Anand Krishnamoorthi (anakrish) merged 7 commits into
Conversation
Use checked template and alias prefix slices, consume complete UTF-8 layout characters, handle empty array subsets, and reserve numeric ranges fallibly with checked counts. Add focused interpreter, RVM, and alias-loading regressions. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2665dfb6-f2d7-48ed-bdd8-ca120a5df099
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2665dfb6-f2d7-48ed-bdd8-ca120a5df099
Reuse iterative comparisons for timer-aware RVM execution, preserve serialization limits through engine errors, and bound strict diagnostics. Add isolated 1 MiB traversal, formatting, recovery, and ownership regressions without changing upstream destruction. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Restore caller state before propagating typed resource errors from default functions. Add strict/non-strict identity and recovery coverage and document the guarantee. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Comparison traversal can grow exponentially and bypass per-execution memory budgets, while additional limit-error propagation and cleanup defects can mask failures or retain execution state.
6 open issues
Enforce execution budgets during traversal scratch allocation · New Retain pointer-identity shortcuts in timed ordering · New Propagate serialization limits from with replacements · New Unwind comprehension contexts when evaluation fails · New Preserve the instruction PC in timed comparison memory errors · New Clean up resumed execution on global memory-limit errors · New
What changed in this PR
Hardens value handling across Regorus’s interpreter, Rego Virtual Machine (RVM), and native FFI, building on the existing iterative traversal and destruction implementations.
Changes:
- Add timer-aware comparisons and membership checks, with cleanup after resource failures.
- Preserve typed serialization-depth errors and bound builtin diagnostic output.
- Add isolated 1 MiB-stack regressions for traversal, serialization, destruction, ownership, and recovery.
| File | Description |
|---|---|
tests/value_walk_safety.rs |
Tests deep traversal, deadlines, formatting, and recovery. |
tests/value_serialization_limits.rs |
Tests serialization budgets and error propagation across execution paths. |
tests/value_drop_safety.rs |
Tests destruction, shared ownership, and allocation behavior. |
src/value/tests.rs |
Updates diagnostic assertions and tests serialization-error identity. |
src/value/set/serde.rs |
Propagates serialization-budget exhaustion tracking through sets. |
src/value/object/serde.rs |
Propagates serialization-budget exhaustion tracking through objects. |
src/value/mod.rs |
Adds checked traversal, bounded diagnostics, and typed serialization handling. |
src/value/array/serde.rs |
Propagates serialization-budget exhaustion tracking through arrays. |
src/utils/limits/error.rs |
Adds the typed serialization-depth resource error. |
src/rvm/vm/state.rs |
Extends cleanup for deadline and serialization-depth failures. |
src/rvm/vm/rules.rs |
Classifies serialization-depth failures as fatal. |
src/rvm/vm/mod.rs |
Registers the comparison module. |
src/rvm/vm/machine.rs |
Extracts reusable timer ticking and maps serialization-depth errors. |
src/rvm/vm/errors.rs |
Adds the VM serialization-depth error variant and conversion. |
src/rvm/vm/dispatch.rs |
Routes comparisons and membership through checked helpers. |
src/rvm/vm/comparison.rs |
Implements timer-aware comparison and membership helpers with tests. |
src/interpreter.rs |
Preserves resource errors through function alternatives and defaults. |
src/builtins/utils.rs |
Bounds shared builtin argument diagnostics. |
src/builtins/encoding.rs |
Preserves resource-error identity during JSON and YAML serialization. |
docs/rvm/vm-runtime.md |
Documents traversal checkpoints, serialization budgets, and cleanup behavior. |
bindings/ffi/src/value_safety_tests.rs |
Tests native formatting, serialization limits, destruction, and recovery. |
bindings/ffi/src/rvm.rs |
Registers the native value-safety tests. |
Review effort: Max
| if e.downcast_ref::<crate::LimitError>().is_some() { | ||
| if let Some(with_functions) = with_functions_saved { | ||
| self.with_functions = with_functions; | ||
| } | ||
| return Err(e); |
| if e.downcast_ref::<crate::LimitError>().is_some() { | ||
| self.scopes = scopes; |
Keep iterative timer-aware comparisons and interruptible set membership, retain main comparison regressions, and reconcile resource-error mapping and runtime documentation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Comparison and JSON-output paths can bypass memory limits or exhaust the process, while serializing valid deeply nested programs can panic.
13 open issues
Comparison traversal skips memory checks for scratch space · New Pretty-printing can exceed memory limits before checking · New Deep values make program JSON serialization panic · New Skip subprocess-based tests under Miri · New Retain pointer-identity shortcuts in timed ordering Enforce execution budgets during traversal scratch allocation Timed comparison memory errors report pc 0 · New Equality bypasses length checks and can cause exponential work · New JSON endpoints reject accepted deeply nested values · New Clean up resumed execution on global memory-limit errors Preserve the instruction PC in timed comparison memory errors Unwind comprehension contexts when evaluation fails Propagate serialization limits from with replacements
Review effort: Max
|
|
||
| impl PartialEq for Value { | ||
| fn eq(&self, other: &Self) -> bool { | ||
| self.cmp(other).is_eq() |
| SerializeWithBudget { | ||
| value: self, | ||
| remaining: MAX_SERIALIZATION_CONTAINERS, | ||
| limit_exceeded: None, | ||
| } | ||
| .serialize(serializer) |
Address High AI review feedback on comparison scratch accounting, aliased ordering, iterative JSON output, fallible program serialization, and Miri subprocess tests. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved critical findings allow exponential equality traversal on compact DAGs and swallowed resource-limit errors that can let evaluation succeed after exhaustion.
9 open issues
Honor equality mode in the unchecked comparison path · New Preserve nested length shortcuts in container equality · New Enforce output limits before formatting large numbers · New Propagate memory-limit errors from container stringification · New Preserve typed limit errors through serialization · New JSON endpoints reject accepted deeply nested values Equality bypasses length checks and can cause exponential work Unwind comprehension contexts when evaluation fails Propagate serialization limits from with replacements
9 resolved since last review
Skip subprocess-based tests under Miri Deep values make program JSON serialization panic Pretty-printing can exceed memory limits before checking Comparison traversal skips memory checks for scratch space Retain pointer-identity shortcuts in timed ordering Enforce execution budgets during traversal scratch allocation Timed comparison memory errors report pc 0 Clean up resumed execution on global memory-limit errors Preserve the instruction PC in timed comparison memory errors
Review effort: Max
| left.cmp(right) | ||
| }); | ||
| if !self.needs_checked_comparison() { | ||
| return Ok(self.get_register(left)?.cmp(self.get_register(right)?)); |
|
|
||
| impl PartialEq for Array { | ||
| fn eq(&self, other: &Self) -> bool { | ||
| self.len() == other.len() && self.cmp(other).is_eq() |
| fn scalar<T: Serialize + ?Sized>(&mut self, value: &T) -> core::result::Result<(), E> { | ||
| #[cfg(feature = "std")] | ||
| { | ||
| let result = value.serialize(&mut serde_json::Serializer::new(&mut *self)); |
| match self.json_string(false) { | ||
| Ok(s) => write!(f, "{s}"), | ||
| Err(_e) => Err(fmt::Error), | ||
| Err(e) => write!(f, "<value cannot be serialized: {e}>"), |
| let key_str = serde_json::to_string(k).map_err(Error::custom)?; | ||
| map.serialize_entry(&key_str, v)?; | ||
| // Non-string keys retain their compact JSON string encoding. | ||
| let key_str = k.json_string(false).map_err(Error::custom)?; |
Resolve the numeric range conflict by retaining main allocation checks and the existing value hardening. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Critical issues can silently turn memory-limit failures into successful policy results and cause exponential work in equality comparisons.
13 open issues
Preserve nested length shortcuts when comparing set elements · New Preserve typed limit errors through serialization Propagate memory-limit errors from container stringification Enforce output limits before formatting large numbers Preserve nested length shortcuts in container equality Honor equality mode in the unchecked comparison path Avoid quadratic scans for scalar set membership under memory limits · New Avoid allocating continuations when dropping scalar children · New Stream escaped strings instead of buffering a second copy · New JSON endpoints reject accepted deeply nested values Equality bypasses length checks and can cause exponential work Unwind comprehension contexts when evaluation fails Propagate serialization limits from with replacements
Review effort: Max
|
|
||
| impl PartialEq for Set { | ||
| fn eq(&self, other: &Self) -> bool { | ||
| self.len() == other.len() && self.cmp(other).is_eq() |
| Value::Set(ref items) => { | ||
| for item in items.iter_sorted() { |
| if iter.len() != 0 { | ||
| pending.push(ReleaseFrame::Object(iter)); | ||
| } | ||
| pending.push(ReleaseFrame::Value(value)); |
| // serde_json does not expose its writer-based Serializer without std. | ||
| #[cfg(not(feature = "std"))] | ||
| { | ||
| self.append(&serde_json::to_vec(value).map_err(anyhow::Error::new)?) |
Anand Krishnamoorthi (anakrish)
left a comment
There was a problem hiding this comment.
LGTM. The following must be fixed in subequent PRs
• Restore nested equality shortcuts across Value , arrays, sets, objects, and unchecked RVM equality to prevent newly exponential comparisons.
• Preserve typed resource errors through non-string-key serialization and function-valued with replacements so exhaustion cannot be swallowed.
• Propagate serialization failures at semantic string-conversion sites instead of returning diagnostic text as successful policy data.
• Restore FFI query-result and host-argument JSON compatibility for accepted values deeper than 64 containers.
• Preserve efficient scalar set membership under memory limits instead of replacing logarithmic lookups with linear scans.
76cdb52
into
microsoft:main


Summary
Value traversal/drop hardening targeting
main. Includes the iterativeValue/container comparison, formatting, serialization budget, and final-owner
destruction from
anakrish/hardening-value-safetyat5cd97dc7de316269de127f3500c47b148c682ce9, plus the focused supplement below.same-type values, string checkpoints, resumed timeout cleanup, and recovery.
through strict/non-strict builtin calls, rules, function alternatives, and
default-function comprehensions. Ordinary type-error behavior is unchanged.
summaries while preserving public Value comparison, Debug, and successful
JSON semantics.
equality, ordering, Debug, JSON/Display/native returned-value formatting,
destruction, ownership/COW, deadline exhaustion, and process recovery.
The supplement does not change the inherited production destructor. Its tests
exercise the final-owner drain and preserve early exhausted Object iterator
release.
After retargeting the PR,
mainwas merged into the feature branch. Itscomparison deadline checks are integrated with the iterative traversal and
interruptible set membership, and its comparison regression coverage is retained.
The original hardening commits are preserved without rebasing.
Validation
Validated on Rust 1.95.0, with that toolchain inherited by xtask children:
cargo +1.95.0 xtask ci-debugextended iterative traversal, interruption, and native-equivalence cases.
value_drop_safety,value_serialization_limits, andvalue_walk_safetysuites: all features (5/10/1 tests), Rc/std (5/7/1),and Rc with allocator memory limits (4/7/1).
cargo +1.95.0 clippy --all-targets --all-features -- -D warningscargo +1.95.0 xtask test-no-stdforthumbv7m-none-eabicargo +1.95.0 test --manifest-path bindings/ffi/Cargo.toml -- --test-threads=1:75 passed, including the exact 1 MiB native worker.
The default-function comprehension regression reproduced lost
LimitErroridentity before the correction. It now checks exact typed identity for both
strict settings and exact healthy output after reusing the same engine.
Review
Two full deep-review pipelines were completed. Confirmed findings were fixed,
with failing regressions demonstrated before their corrections and validation
rerun afterward. The second review found the default-function identity gap and
its missing regression; both are included in the separate follow-up commit.
A third full deep-review pipeline has not been run after that focused fix or
the target-branch merge. The merged tree was validated again on Rust 1.95.0.
The review skill's named models were unavailable; reviewers used runtime-default
models with distinct methodologies and a fresh adversarial verifier.
Compatibility and limitations
keeps native B-tree lookup. Other implicit B-tree comparisons remain
infallible, and scalar number comparison is an indivisible checkpoint unit.
an input admission limit. Iterative Display/JSON output is not subject to
that callback budget.
arbitrary branching/object shapes. Cooperative limits do not guarantee
successful cleanup under physical allocator exhaustion.
frame sizes remain platform-dependent.
absent, so separate C/C++ and all-nine-binding smoke gates were not run.
compiler/indexmap code and are not claimed to pass.
memory limit.