Skip to content

fix(determinism): replace HashMap.iter().find with sorted-key iteration (LS-A-15)#154

Merged
avrabe merged 1 commit into
mainfrom
fix/hashmap-iteration-determinism
May 15, 2026
Merged

fix(determinism): replace HashMap.iter().find with sorted-key iteration (LS-A-15)#154
avrabe merged 1 commit into
mainfrom
fix/hashmap-iteration-determinism

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented May 14, 2026

Summary

Third fix from the post-v0.8.0 Mythos delta-pass sweep. Three sites in
meld-core resolved cross-component routing via `HashMap::iter().find(...)`,
which picks a hash-seed-randomised entry. When multiple entries
matched, the chosen entry varied per run.

The bugs

Site Effect
`merger.rs::component_realloc_index` fallback (2907-2919) Multi-module component with reallocs in modules other than 0 picks an arbitrary realloc — may be bound to wrong memory (H-4.3)
`merger.rs` handle-table fallbacks (~1040, 1090-1100) Ties between candidate handle tables broken by iteration order
`resolver.rs::resolve_resource_positions` last-resort (1295-1308) Resource-rep / -new / -drop prefix collisions return arbitrary import → cross-resource confusion (H-3)

The wasm validator does not catch any of these because structural types match across candidates: all reallocs share `(i32,i32,i32,i32) -> i32`; all resource-rep imports share `(i32) -> i32`.

Fix

Collect candidate keys → sort → pick `.first()`. Deterministic tie-breaking:

  • `component_realloc_index` picks the lowest module index
  • Handle-table fallbacks pick lexicographically smallest `(owner, iface, rn)`
  • Resource type-id fallback picks the smallest matching type-id

Picks may still be semantically wrong if multiple distinct resources share a prefix (Step 6 alias propagation is the right structural mitigation), but the output is now reproducible across runs.

Tests (2 new)

  • `ls_a_15_component_realloc_index_picks_lowest_module_deterministically` —
    rebuilds the MergedModule across 64 iterations (each with fresh HashMap
    hash seed) and asserts a single observed value.
  • `ls_a_15_component_realloc_index_prefers_module_0` — regression
    for the primary path.

Tier-5 gate

Touches `merger.rs` and `resolver.rs` (both Tier-5). Mythos pass evidence
in a comment below.

Test plan

  • `cargo test -p meld-core --lib` — 210 pass (208 prior + 2 new)
  • `cargo clippy --all-targets -- -D warnings` — clean
  • `cargo fmt --check` — clean
  • YAML lint
  • CI green on smithy
  • `mythos-pass-done` label applied

Refs

  • LS-A-15 (UCA-M-10, H-7, H-3, H-4.3)
  • Discovered by post-v0.8.0 Mythos delta-pass on merger.rs and resolver.rs

🤖 Generated with Claude Code

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 14, 2026

Mythos delta-pass required

This PR modifies one or more Tier-5 source files (per
scripts/mythos/rank.md):

meld-core/src/merger.rs
meld-core/src/resolver.rs

Before merge, run the Mythos discover protocol on the
modified Tier-5 files:

  1. Follow scripts/mythos/discover.md
    — one fresh agent session per touched Tier-5 file.
  2. For each finding, the agent must produce both a Kani
    harness and a failing PoC test (per the protocol's
    "if you cannot produce both, do not report" rule).
  3. Attach a comment on this PR with either the findings
    (formatted per discover.md's output schema) or
    NO FINDINGS.
  4. Add the mythos-pass-done label to this PR.

Why this gate exists: LS-A-10
(CABI alignment padding in async-lift retptr writeback) was
found by the v0.8.0 pre-release Mythos pass — but it had
lived in the callback emitter since #128, across six
releases. A PR-time gate would have caught it at review
time instead of at the release boundary.

The gate check on this PR will pass once the label is
applied.

@avrabe avrabe added the mythos-pass-done Mythos delta-pass completed on Tier-5 file changes; findings (or NO FINDINGS) attached to PR label May 14, 2026
@avrabe
Copy link
Copy Markdown
Contributor Author

avrabe commented May 14, 2026

Mythos delta-pass evidence

Tier-5 files touched: meld-core/src/merger.rs and meld-core/src/resolver.rs.

The post-v0.8.0 sweep ran fresh agents on both files. Both surfaced the HashMap-iteration non-determinism finding (resolver F1 + merger B) with concrete PoCs. The PoCs are folded into the regression test (ls_a_15_component_realloc_index_picks_lowest_module_deterministically).

Other findings from those file scans not in this PR (will land in follow-up PRs):

  • merger.rs: ends_with(rn) suffix matching for resource imports → cluster C3
  • resolver.rs F2/F3 (min_by_key heuristic + single-pass alias propagation) — speculative PoCs not concrete, will validate before triaging

Three sites resolved cross-component routing via HashMap::iter().find()
over keyed maps. HashMap iteration order is hash-seed-randomised per
process, so when more than one entry matched the lookup predicate the
chosen entry varied across runs — producing non-reproducible fused
output bytes and, in some cases, routing a call to the wrong target.

Affected sites:
- merger.rs::component_realloc_index fallback (2907-2919)
- merger.rs handle-table fallbacks (~1040 and 1090-1100)
- resolver.rs::resolve_resource_positions last-resort (1295-1308)

Wasm validator does not catch any of these because structural types
match across candidates (all reallocs share (i32,i32,i32,i32) -> i32;
all resource-rep imports share (i32) -> i32).

Fix: collect candidate keys, sort, pick .first(). Deterministic
tie-breaking by lowest module index / smallest type-id / lexicographic
key. Picks may still be semantically wrong if multiple distinct
resources share a prefix (Step 6 alias propagation is the right
structural mitigation), but the output is now reproducible across runs.

Tests (2 new):
- ls_a_15_component_realloc_index_picks_lowest_module_deterministically
- ls_a_15_component_realloc_index_prefers_module_0

LS-A-15 added to safety/stpa/loss-scenarios.yaml. Discovered by the
post-v0.8.0 Mythos delta-pass sweep on merger.rs and resolver.rs.

Refs: LS-A-15 (UCA-M-10, H-7, H-3, H-4.3)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@avrabe avrabe force-pushed the fix/hashmap-iteration-determinism branch from afb7687 to 9583011 Compare May 15, 2026 10:10
@avrabe avrabe merged commit ff6c317 into main May 15, 2026
10 checks passed
@avrabe avrabe deleted the fix/hashmap-iteration-determinism branch May 15, 2026 17:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mythos-pass-done Mythos delta-pass completed on Tier-5 file changes; findings (or NO FINDINGS) attached to PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant