Skip to content

Commit b6a6439

Browse files
avrabeclaude
andauthored
fix(resource-graph,merger): match full (iface, rn) tuple in 4 sites (LS-A-17/18/19) (#156)
Four sites in meld-core either dropped the interface dimension of the (component, interface, resource_name) tuple or used substring/suffix matching where exact match was required. All led to silent cross- resource confusion or wrong-handle-table routing with no host trap. LS-A-17a — resource_graph.rs definer purge (lines 279-308). The cleanup filter ignored the iface when removing defines_cache entries. A component importing `[resource-rep]X` from interface I_x AND defining its own `[resource-rep]X` in unrelated interface I_y lost the (comp, I_y, X) definer entry. LS-A-17b — resource_graph.rs terminal-exporter pass (lines 242-264). `to_also_imports_resource` checked any iface, mis-classifying a definer as a re-exporter when the consumer imported any unrelated resource interface (e.g. wasi:io/poll). LS-A-18 — resource_graph.rs first pass (~lines 124-172) only stripped `[resource-rep]` and `[resource-new]` prefixes. A re-exporter that imports `[resource-drop]Y` for a foreign resource Y never registered Y in the graph; the drop call then routed to the first matching handle-table fallback (LS-A-15 family), invoking a foreign dtor on a foreign rep. LS-A-19 — merger.rs::add_unresolved_imports dedup-skip path used `imp.name.ends_with(rn)`. Two distinct resources sharing a suffix (`float` / `bigfloat`) collided into the same tracking entry. Fixes: - definer purge: filter by (idx == from_comp && iface == ri && r == rn) - terminal exporter: scope to_also_imports check to the iface under attribution (and [export]-prefixed variants) - first-pass: extracted ResourcePrefixKind enum; handle Drop arm as ImportsFrom edge (matches second pass) - merger dedup: exact-match against format!("[resource-{rep,new}]{rn}") Tests (3 new): - ls_a_17_definer_survives_unrelated_import_with_same_resource_name - ls_a_17_terminal_definer_with_unrelated_resource_import - ls_a_18_drop_on_foreign_resource_registers_node_for_reexporter LS-A-17/18/19 added to safety/stpa/loss-scenarios.yaml. Discovered by the post-v0.8.0 Mythos delta-pass sweep on resource_graph.rs and merger.rs. Refs: LS-A-17, LS-A-18 (UCA-F-2, H-1, H-3), LS-A-19 (UCA-M-9, H-1, H-10) Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
1 parent ff6c317 commit b6a6439

4 files changed

Lines changed: 411 additions & 35 deletions

File tree

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,28 @@ All notable changes to this project will be documented in this file.
66

77
### Fixed
88

9+
- **Resource graph + merger key-matching bugs** (LS-A-17, LS-A-18, LS-A-19;
10+
UCA-F-2 / UCA-M-9). Four sites in `meld-core` either dropped the
11+
interface dimension of `(component, interface, resource_name)` tuples
12+
or used substring/suffix matching where exact match was required:
13+
- `resource_graph.rs` definer purge ignored the iface when removing
14+
defines_cache entries, purging legitimate definers when an
15+
unrelated component imported a resource sharing the name.
16+
- `resource_graph.rs` terminal-exporter pass used over-broad
17+
`to_also_imports_resource` check that classified a component as a
18+
re-exporter if any of its imports carried any resource interface.
19+
- `resource_graph.rs` only registered resource nodes for
20+
`[resource-drop]X` imports on pure-consumer components; re-exporters
21+
that dropped a foreign resource had their drop calls invisible to
22+
the graph.
23+
- `merger.rs::add_unresolved_imports` dedup-skip path matched
24+
`imp.name.ends_with(rn)`, so two resources sharing a suffix (e.g.
25+
`float` / `bigfloat`) collided into the same tracking entry.
26+
All four led to silent cross-resource confusion or wrong-handle-table
27+
routing with no host trap. Fixes scope each comparison to the full
28+
`(comp, iface, rn)` tuple or use exact-match against the full
29+
`[resource-{rep,new,drop}]<name>` import string.
30+
931
- **HashMap iteration non-determinism in resource / realloc fallbacks**
1032
(LS-A-15, UCA-M-10, H-7 / H-3 / H-4.3). Three sites resolved
1133
cross-component routing via `HashMap::iter().find(...)`, which picks

meld-core/src/merger.rs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2144,26 +2144,36 @@ impl Merger {
21442144
// Still record per-component resource tracking: find the
21452145
// func index already assigned to this resource name.
21462146
let eff_field = &dedup_key.1;
2147+
// Exact-match the full `[resource-{rep,new}]<name>`
2148+
// import name. The prior `ends_with(rn)` matched any
2149+
// resource whose name had `rn` as a suffix (e.g.
2150+
// `rn = "float"` collided with both
2151+
// `[resource-rep]float` and `[resource-rep]bigfloat`),
2152+
// letting `resource_rep_by_component` track the
2153+
// wrong import for the wrong-suffix collision — silent
2154+
// cross-resource confusion (LS-A-19).
21472155
if let Some(rn) = eff_field.strip_prefix("[resource-rep]") {
2156+
let expected = format!("[resource-rep]{rn}");
21482157
if let Some(&idx) =
21492158
merged.resource_rep_by_component.values().find(|&&idx| {
2150-
merged.imports.get(idx as usize).is_some_and(|imp| {
2151-
imp.name.starts_with("[resource-rep]")
2152-
&& imp.name.ends_with(rn)
2153-
})
2159+
merged
2160+
.imports
2161+
.get(idx as usize)
2162+
.is_some_and(|imp| imp.name == expected)
21542163
})
21552164
{
21562165
merged
21572166
.resource_rep_by_component
21582167
.insert((unresolved.component_idx, rn.to_string()), idx);
21592168
}
21602169
} else if let Some(rn) = eff_field.strip_prefix("[resource-new]") {
2170+
let expected = format!("[resource-new]{rn}");
21612171
if let Some(&idx) =
21622172
merged.resource_new_by_component.values().find(|&&idx| {
2163-
merged.imports.get(idx as usize).is_some_and(|imp| {
2164-
imp.name.starts_with("[resource-new]")
2165-
&& imp.name.ends_with(rn)
2166-
})
2173+
merged
2174+
.imports
2175+
.get(idx as usize)
2176+
.is_some_and(|imp| imp.name == expected)
21672177
})
21682178
{
21692179
merged

0 commit comments

Comments
 (0)