Skip to content

chore: release v0.10.0#180

Merged
avrabe merged 1 commit into
mainfrom
chore/release-v0.10.0
May 24, 2026
Merged

chore: release v0.10.0#180
avrabe merged 1 commit into
mainfrom
chore/release-v0.10.0

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented May 24, 2026

Canonical-ABI correctness sweep — 15 fixes since v0.9.0, all driven by the mythos-auto delta-pass on #179. Bumps meld-core and meld-cli from 0.9.0 → 0.10.0.

Headline (memory-safety / priority high)

  • LS-P-6 params/return-area accumulator wrap → OOB write in cabi_realloc (now saturating).
  • LS-P-7 conditional-pointer CopyLayout per-leaf instead of composite-payload (7/8 under-copy for list<u64> inside option/result).
  • LS-P-8 Record/Tuple field-walk used unpadded child size at 25 sites; spec-correct now uses canonical_abi_element_size.
  • LS-P-9 total_flat_params saturating fold (was sum::<u32>); fixes calling-convention swap.
  • LS-P-10 nested-conditional outer-guard chain on ConditionalPointerPair — closes an arbitrary-cross-component-memory-read with attacker-controlled (ptr, len) for result<option<string>, u32> etc.
  • LS-P-14 nested-list inner-copy buf_len overflow guard.
  • LS-P-16 UTF-16→UTF-8 lone-high-surrogate OOB read; conservative trap (U+FFFD upgrade is follow-up).
  • LS-P-19 UTF-8→UTF-16 mirror: each multi-byte branch guards continuation-byte reads.

Defensive mitigations / hardening

  • LS-P-12 + LS-P-18 refuse list<option/result/variant-with-pointer> (silent stale-pointer corruption); full per-element conditional fixup is follow-up.
  • LS-P-11 flat-name resolver rejects duplicate module exports.
  • LS-P-13 async adapter param-copy uses resolver positions, not the (i32,i32) heuristic.
  • LS-P-15 OOB resource_type_id dropped + warned, not silently misclassified.
  • LS-P-17 warn on mixed-encoding caller before the heuristic fallback miscompiles string_transcoding.

Hygiene

  • flat_byte_size element-wise variant JOIN (was max; no in-tree consumers, no LS-N).

Tests / verification

  • 266 lib tests (+34 from v0.9.0's 232)
  • LS-N gate: 33/33 approved loss scenarios with regression tests
  • mythos-auto delta-pass: clean per-file on parser.rs / fact.rs / resolver.rs
  • Each finding clean-room-verified by an independent agent following validate.md before fix-or-dispute. One false positive (auto-runner disputed LS-P-8's spec-correct fix, directly contradicting canonical-abi.py::record_size) was disputed rather than fixed.

🤖 Generated with Claude Code

Canonical-ABI correctness sweep — 14 commits / 15 fixes since v0.9.0,
driven by the mythos-auto delta-pass on PR #179. All 33 approved
loss scenarios in `safety/stpa/loss-scenarios.yaml` have matching
regression tests (LS-N gate 33/33).

## Confirmed memory-safety fixes (priority high)

- **LS-P-6** — `params_area_byte_size` / `return_area_byte_size`
  used a bare `+=` against a saturating value; once
  `canonical_abi_size_unpadded` saturated to `u32::MAX`, the next
  field's `+=` wrapped to a small value, under-sizing the params
  buffer and producing an OOB write in `cabi_realloc`. Now uses
  `saturating_add`.
- **LS-P-7** — `collect_conditional_pointers` /
  `collect_conditional_result_pointers` computed `CopyLayout` for
  the whole composite payload instead of the per-leaf type, so a
  `list<u64>` leaf inside `option<tuple<u32, list<u64>>>` got
  `Bulk{1}` instead of `Bulk{8}` — a 7/8 silent under-copy. New
  `_with_layout` collectors carry each leaf's own layout.
- **LS-P-8** — Record/Tuple field-walk added the *unpadded* child
  size instead of `canonical_abi_element_size`, so
  `tuple<record{u32,u8}, u8>` came out 6 instead of the spec's
  12; offsets for everything downstream were wrong. Fixed at 25
  field-walk sites.
- **LS-P-9** — `total_flat_params` used `Iterator::sum::<u32>()`
  instead of saturating fold; a nested `FixedSizeList` saturating
  to `u32::MAX` then wrapped on the next add, selecting flat
  convention for a function that genuinely needed params-ptr —
  call-site lowering and callee-side lifting disagreed.
- **LS-P-10** — Nested conditional pointer pair omitted the
  outer-discriminant guard. For `result<option<string>, u32>` an
  `Err(v)` whose byte at the option slot read as 1 fired a
  cross-component `memory.copy` with attacker-controlled
  `(ptr, len)`. Added `DiscriminantGuard` + `outer_guards` chain
  on `ConditionalPointerPair`; the FACT adapter ANDs every guard
  before each copy.
- **LS-P-14** — Nested-list inner copy `buf_len =
  callee_len * sub_elem_size` lacked the overflow guard; wraps
  produced truncated copies + OOB reads/writes adjacent. Added
  `emit_overflow_guard`.
- **LS-P-16** — UTF-16→UTF-8 transcoder read 2 bytes past input
  for a lone high surrogate at the buffer end, leaking adjacent
  caller memory into the callee's UTF-8 output. Conservative
  mitigation traps; U+FFFD replacement upgrade is follow-up.
- **LS-P-19** — UTF-8→UTF-16 mirror: each multi-byte branch
  unconditionally read continuation bytes at `src_idx + N` for
  N = 1/2/3 without bounds checking; truncated multi-byte UTF-8
  leaked 1–3 bytes of caller memory into the callee's UTF-16
  output. Conservative mitigation traps in each branch.

## Defensive mitigations (refusal + loud-fail)

- **LS-P-12** — `list<option-with-pointer>` /
  `list<result<...>>` / `list<variant<...>>` silently produced
  stale cross-memory pointers in callee elements (the per-element
  conditional fixup `element_inner_pointers` cannot yet emit).
  `copy_layout` now panics at adapter generation rather than
  emitting a corrupting `Bulk` layout; structural fix tracked as
  follow-up.
- **LS-P-18** — Refinement of LS-P-12: a record mixing a covered
  bare-pointer field with a hidden conditional pointer field
  (e.g. `record { items: list<u8>, maybe: option<string> }`)
  bypassed the LS-P-12 emptiness check. New recursive
  `has_pointer_bearing_conditional` helper closes the bypass.

## Defensive hardening (priority low)

- **LS-P-11** — Flat-name resolver overwrote duplicate module
  exports silently (last-writer wins); now rejects with new
  `Error::DuplicateModuleExport`. Unreachable for production
  multi-module components (the instance-graph resolver is
  immune), hardening for synthetic fixtures.
- **LS-P-15** — Out-of-bounds `resource_type_id` was silently
  misclassified as callee-defined via `unwrap_or(true)`, swapping
  `[resource-rep]`/`[resource-new]` calls. Now drops with
  `log::warn!` + `continue`; downstream surfaces a loud
  missing-fixup error rather than silently swapping.
- **LS-P-13** — Async adapter param-copy step treated every
  consecutive `(i32, i32)` flat-param pair as a string/list
  pointer pair via `.any(|_| true)` (= `!is_empty()`). Replaced
  with `site.requirements.pointer_pair_positions.clone()` —
  the resolver already returns flat indices that match the
  caller's lowered layout, so the heuristic walk was unneeded.
- **LS-P-17** — Caller-encoding name match filtered on
  `ComponentTypeRef::Func` only; WIT interface imports are
  `Instance`, so the loop always missed and the heuristic
  fallback miscalibrated `string_transcoding` for mixed-encoding
  callers. Mitigation warns when caller has multiple distinct
  encodings; structural per-interface attribution is follow-up.

## Hygiene

- **flat_byte_size** — `result<T, E>` / `variant` payload width
  was `max(flat_byte_size(arm))` instead of the Component Model
  element-wise `flatten_variant` JOIN. Rewritten over a new
  `flat_width_list` helper. Hygiene-only; no in-tree consumers,
  no LS-N entry, no reachable hazard.

## Test coverage

- 266 lib tests (+34 from v0.9.0's 232)
- LS-N verification gate: 33/33 approved scenarios pinned
- Full mythos delta-pass: clean on parser.rs, fact.rs, resolver.rs
  per-file scans
- Aggregate findings + label (label-gated escape) closed by
  maintainer's `mythos-pass-done` after independent clean-room
  verification of each finding

## Process notes

The mythos-auto delta-pass was the primary engine for this
release: AI-driven discover.md scans on every Tier-5 PR file,
each finding clean-room-verified by an independent agent
following validate.md before fix-or-dispute. One false positive
(auto-runner claimed LS-P-8's spec-correct
`canonical_abi_element_size` should be reverted to
`canonical_abi_size_unpadded` — directly contradicting
`canonical-abi.py::record_size`'s `s += size(f.t)`) was
disputed on the PR rather than fixed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

LS-N verification gate

33/33 approved LS entries verified

count
Passed (≥1 test, all green) 33
Failed (≥1 test failure) 0
Missing (no ls_*_NN_* test found) 0

Approved loss-scenarios.yaml entries are expected to have a
regression test named ls_<letter>_<num>_* (e.g. LS-A-11
ls_a_11_*). The gate runs each prefix via cargo test --lib --no-fail-fast and aggregates pass/fail/missing.

Failed LS entries

(none)

Missing regression tests

(none)

Updated automatically by tools/post_verification_comment.py.
Source of truth: safety/stpa/loss-scenarios.yaml.

@avrabe avrabe merged commit 5932dfd into main May 24, 2026
14 checks passed
@avrabe avrabe deleted the chore/release-v0.10.0 branch May 24, 2026 11:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant