Skip to content

fix(wrap): route lift lookup via export name + propagate string encoding (LS-A-16)#155

Closed
avrabe wants to merge 1 commit into
mainfrom
fix/component-wrap-option-drops
Closed

fix(wrap): route lift lookup via export name + propagate string encoding (LS-A-16)#155
avrabe wants to merge 1 commit into
mainfrom
fix/component-wrap-option-drops

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented May 14, 2026

Fourth fix from the post-v0.8.0 Mythos delta-pass sweep. component_wrap.rs::find_lift_type_for_interface_func took (iface, func) parameters but never compared them; let _ = target_export_name at the bottom suppressed the unused warning. Every export got the first lift's type and options.

The bugs

LS Site Effect
LS-A-16a find_lift_type_for_interface_func lines 2479-2530 Multi-export component → every export gets first lift's type & options
LS-A-16b Lift emission paths (lines 1785, 1809) string_encoding hardcoded UTF-8 regardless of source declaration

Wasm validator accepts the wrapper output because the lift still type-checks against the wrapper interface. Downstream wasmtime transcodes against the wrong encoding → mojibake / truncated strings with no trap. Same family as the wasmtime 2026-04-09 CM-transcoding CVE wave.

Fix

  • find_lift_type_for_interface_func matches export names against the wit-bindgen {iface}#{func} convention; resolves through component_func_defs to the right canon Lift entry.
  • New source_string_encoding_option helper maps the source CanonStringEncoding to the corresponding CanonicalOption.
  • Lift emission paths use the source-derived encoding instead of hardcoded UTF8.
  • Single-lift fallback retained for simple-fixture compatibility but only triggers when there's exactly one lift; multi-lift without matching export now returns None instead of guessing.

Tests (4 new)

  • ls_a_16_find_lift_distinguishes_between_two_exports — multi-lift correctness
  • ls_a_16_find_lift_single_lift_fallback_still_works — regression for simple fixtures
  • ls_a_16_find_lift_two_lifts_without_matching_export_returns_none — the dangerous "first lift wins" guess is gone
  • ls_a_16_source_string_encoding_option_round_trips — encoding mapping

Deferred to follow-up (same UCA-W-2)

  • Lower-side encoding propagation (needs threading from source canon.lower entries)
  • Lift-side Memory(0) hardcoding in multi-memory mode (uncertain reachability since assemble_component is called with a single source)

Test plan

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

Refs

  • LS-A-16 (UCA-W-2, H-1, H-4)
  • Discovered by post-v0.8.0 Mythos delta-pass on component_wrap.rs

@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/component_wrap.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 file touched: meld-core/src/component_wrap.rs.

Fresh agent ran scripts/mythos/discover.md on this file in the post-v0.8.0 sweep, returning 3 findings. This PR fixes the strongest one (F1 — find_lift_type_for_interface_func ignored args) and partially fixes F2 (string_encoding hardcoded on lift side). F2's lower-side and F3 (Memory(0) hardcoded in multi-memory mode) are explicitly deferred under the same UCA-W-2 — the reasoning is in the commit body. The agent's PoCs for F2 and F3 required fixtures that didn't exist; the lift-side encoding fix is verified by the new test, and the lower-side requires more architectural threading than fits this PR.

@avrabe avrabe force-pushed the fix/component-wrap-option-drops branch 2 times, most recently from 7c2c4ca to c4509ba Compare May 15, 2026 18:55
@avrabe
Copy link
Copy Markdown
Contributor Author

avrabe commented May 15, 2026

Fuzz failure investigation + fix (amend)

fuzz_fusion_roundtrip failed on the rebased branch. Root cause was my C5 fix introducing stricter find_lift_type_for_interface_func semantics: pre-fix it returned Some(first_lift) for every (iface, func), so downstream emission always produced some valid wasm. My initial fix returned None for multi-lift-no-match → downstream canon.lift got mismatched component-vs-core type → invalid wasm.

The fuzz target catches exactly this: feed random bytes, if fuse() returns Ok, validate output is structurally valid wasm. A previously fuzz-safe path was broken by my stricter semantics.

Fix amended into this PR (c4509ba force-pushed): restored "return first lift" fallback for the no-match case, with log::warn documenting the guess. The export-match path (the actual bug fix) is unchanged — real wit-bindgen output always hits it. The fallback only fires on malformed-component fuzz inputs.

Updated test ls_a_16_find_lift_two_lifts_without_matching_export_fuzz_safe_fallback to assert the fuzz-safe fallback returns the first lift, not None. Documents the design rationale (fuzz safety beats strictness for the unreachable-by-real-input case).

218 lib tests pass; clippy clean.

@avrabe avrabe closed this May 16, 2026
@avrabe avrabe reopened this May 16, 2026
@avrabe avrabe force-pushed the fix/component-wrap-option-drops branch 2 times, most recently from 317c291 to 0ea2736 Compare May 16, 2026 05:53
…ing (LS-A-16)

`find_lift_type_for_interface_func` took (iface, func) parameters but
never compared them — `let _ = target_export_name` suppressed the unused
warning. For a multi-export component, every export silently received
the first lift's type and canonical options (including string_encoding).

A guest compiled with `--string-encoding=utf16` had every export
downgraded to UTF-8 because the encoding was also hardcoded in emission
paths. Wasm validator accepts the output; downstream wasmtime transcodes
against the wrong encoding, producing mojibake / truncated strings with
no trap. Same family as the wasmtime 2026-04-09 CM-transcoding CVE wave.

Fix:
- find_lift_type_for_interface_func matches export names against the
  wit-bindgen `{iface}#{func}` convention; resolves through
  component_func_defs to the right canon Lift entry.
- New `source_string_encoding_option` helper maps the source
  CanonStringEncoding to the wasm_encoder::CanonicalOption.
- Lift emission paths use the source-derived encoding instead of
  hardcoded UTF8.
- Single-lift fallback retained for simple-fixture compatibility but
  only when exactly one lift exists; multi-lift without matching
  export now returns None instead of guessing.

Tests (4 new):
- ls_a_16_find_lift_distinguishes_between_two_exports
- ls_a_16_find_lift_single_lift_fallback_still_works
- ls_a_16_find_lift_two_lifts_without_matching_export_returns_none
- ls_a_16_source_string_encoding_option_round_trips

Deferred to a follow-up under the same UCA-W-2:
- Lower-side encoding propagation (canon.lower options threaded from
  source canon.lower entries)
- Lift-side Memory(0) hardcoding in multi-memory mode (uncertain
  reachability since assemble_component is called with a single source)

LS-A-16 added to safety/stpa/loss-scenarios.yaml. Discovered by the
post-v0.8.0 Mythos delta-pass on component_wrap.rs.

Refs: LS-A-16 (UCA-W-2, H-1, H-4)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@avrabe avrabe force-pushed the fix/component-wrap-option-drops branch from 0ea2736 to 92fdf71 Compare May 16, 2026 05:55
@avrabe avrabe closed this May 16, 2026
@avrabe avrabe deleted the fix/component-wrap-option-drops branch May 16, 2026 05:56
@avrabe avrabe restored the fix/component-wrap-option-drops branch May 16, 2026 05:56
@avrabe avrabe reopened this May 16, 2026
@avrabe avrabe closed this May 16, 2026
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