[GH-ISSUE #5681] [Bug] Swift vtable coverage mixes examined type entries with decoded slots #3430

Closed
opened 2026-10-31 00:40:24 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 20, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/5681

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Native analysis or decompilation

Observed behavior

inspect-native-dispatch-metadata returns swift_class_vtable_descriptors coverage with examined: 1, decoded: 2 for a source-built Swift class containing an initializer and one method. Adding a second method changes decoded to 3 while examined stays 1. All returned slots are decoded successfully. The counters describe different populations, making quantitative decode coverage misleading; these cases do not demonstrate an incorrect complete status or missing slots.

Reproduction

The independent verifier executed these commands from a built source checkout with locked dependencies on macOS, with Swift and jq available:

work_dir="$(mktemp -d)"
mkdir -p "$work_dir"/{home,cfg,data,cache,state,temp}
cat > "$work_dir/coverage.swift" <<'SWIFT'
public class CoverageUnit {
  public init() {}
  public func m() {}
}
SWIFT
swiftc -Onone -g -parse-as-library -module-name cov \
  -emit-library -o "$work_dir/libcoverage.dylib" "$work_dir/coverage.swift"
env -i PATH="$PATH" HOME="$work_dir/home" \
  XDG_CONFIG_HOME="$work_dir/cfg" XDG_DATA_HOME="$work_dir/data" \
  XDG_CACHE_HOME="$work_dir/cache" XDG_STATE_HOME="$work_dir/state" \
  TMPDIR="$work_dir/temp" REA_LOG_LEVEL=silent \
  node scripts/rea.mjs inspect-native-dispatch-metadata \
  "$work_dir/libcoverage.dylib" --json > "$work_dir/out.json"
jq '.normalized_result.result | {
  slots: [.swift_dispatch_slots[] | {slot_index, decode: .decode.status}],
  coverage: [.coverage[] | select(.facet == "swift_class_vtable_descriptors")]
}' "$work_dir/out.json"
nm "$work_dir/libcoverage.dylib" | xcrun swift-demangle --compact | \
  grep -E 'nominal type descriptor|method descriptor'

As a second observed case, add public func n() {} to the class, recompile and inspect it with the same isolated runner. Both CLI invocations exited 0. The fixtures were read without executing their code. Stored outputs, fixture sources and demangled symbols were inspected again before submission; no new runtime reproduction on main is claimed.

Expected behavior

Per-facet examined and decoded should count the same documented population, with decoded records drawn from examined records. Descriptor accounting could report 1/1 here; slot accounting could report 2/2 and 3/3. Either approach should preserve the returned slots and truthful completeness, including truncation and unresolved-implementation limitations.

Source-derived contract: docs/tool-design.md:171-175 requires facet completeness to derive from examined records and exhaustiveness. Supported class-vtable decoding includes these non-generic, non-resilient classes. The coverage schema exposes both counters but does not itself enforce their relationship.

Impact and scope

P2: ordinary supported Swift classes produce incompatible machine-readable coverage counters. A caller cannot compute a meaningful decode ratio from them. The observed impact is bounded to this accounting: incorrect implementation addresses, omitted slots, security effects and downstream consumer failures were not established. Callers can inspect returned per-slot decode states while continuing to honor facet truncation and unsupported-metadata reasons. MCP transport, other platforms and production-binary prevalence were not tested.

REA version

rea-agents 6.7.0; source baseline 9d8e7803326a382cbbb3ffdb900f5e93e5442d68. Fetched main 60956c8c56475e7aaab9559f780908851677ced6 has no later changes to the cited expected/failure source files. This main recheck used source history, without rebuilding or rerunning that revision.

Runtime and platform

Node.js v26.12.0; macOS 27.0.1 arm64; Apple Swift 6.4 (swiftlang-6.4.0.34.1 clang-2100.3.34.1). No Hopper/Ghidra involved.

Target and artifact context

Self-built thin Mach-O dylibs with one non-generic, non-resilient Swift class. The first has an allocating initializer and m(); the second also has n(). Default inspection options and isolated home/XDG directories were used.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

The first CLI output contains this projection:

{
  "slots": [
    {"slot_index": 10, "decode": "decoded"},
    {"slot_index": 11, "decode": "decoded"}
  ],
  "coverage": [{
    "facet": "swift_class_vtable_descriptors",
    "status": "complete",
    "reason": null,
    "examined": 1,
    "decoded": 2
  }]
}

The added-method control returns decoded slots 10, 11 and 12, with the same facet complete, reason null, examined: 1, decoded: 3. Demangled symbols independently identify one nominal type descriptor for cov.CoverageUnit and two method descriptors in the first fixture, for its allocating initializer and m(). Evidence provenance: observed; local verification: independently reproduced at the baseline.

Typed outcome or recovery expected

Keep the successful metadata result and every returned slot, but use coherent units for the coverage counters. Proposed restoration checks: replay both fixtures; adding a method must not pair one examined descriptor with three decoded slots. Clamping decoded or dropping slots would not establish accurate accounting. Preserve complete status for fully resolved supported fixtures and partial coverage for truncation or unresolved implementations. Those partial cases are proposed checks, not additional observed runs.

Analysis or suspected cause

Source-derived reasoning: AppleSwiftVtables.ts:43-47 increments examined per admitted type-section entry before the class-kind guard, including entries later skipped as non-class. Line 116 increments decoded per resolved vtable slot. AppleDispatchCoverage.ts:215-235 publishes those totals unchanged while calculating status from exhaustiveness, truncation and unresolved slots. This explains the mixed units without implying that the observed complete statuses are false.

Additional context

Prepared with AI assistance and independently reproduced through the public CLI. Related #5464 concerns JavaScript summary counters; #5543 concerns Objective-C method omissions under record-budget exhaustion; #4369 concerns empty Swift owner recovery, fixed by #4469. Their contracts and restoration checks differ from this accounting defect. No last known good version or introducing commit was established.

Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5681 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Native analysis or decompilation ### Observed behavior `inspect-native-dispatch-metadata` returns `swift_class_vtable_descriptors` coverage with `examined: 1, decoded: 2` for a source-built Swift class containing an initializer and one method. Adding a second method changes `decoded` to 3 while `examined` stays 1. All returned slots are decoded successfully. The counters describe different populations, making quantitative decode coverage misleading; these cases do not demonstrate an incorrect `complete` status or missing slots. ### Reproduction The independent verifier executed these commands from a built source checkout with locked dependencies on macOS, with Swift and jq available: ```sh work_dir="$(mktemp -d)" mkdir -p "$work_dir"/{home,cfg,data,cache,state,temp} cat > "$work_dir/coverage.swift" <<'SWIFT' public class CoverageUnit { public init() {} public func m() {} } SWIFT swiftc -Onone -g -parse-as-library -module-name cov \ -emit-library -o "$work_dir/libcoverage.dylib" "$work_dir/coverage.swift" env -i PATH="$PATH" HOME="$work_dir/home" \ XDG_CONFIG_HOME="$work_dir/cfg" XDG_DATA_HOME="$work_dir/data" \ XDG_CACHE_HOME="$work_dir/cache" XDG_STATE_HOME="$work_dir/state" \ TMPDIR="$work_dir/temp" REA_LOG_LEVEL=silent \ node scripts/rea.mjs inspect-native-dispatch-metadata \ "$work_dir/libcoverage.dylib" --json > "$work_dir/out.json" jq '.normalized_result.result | { slots: [.swift_dispatch_slots[] | {slot_index, decode: .decode.status}], coverage: [.coverage[] | select(.facet == "swift_class_vtable_descriptors")] }' "$work_dir/out.json" nm "$work_dir/libcoverage.dylib" | xcrun swift-demangle --compact | \ grep -E 'nominal type descriptor|method descriptor' ``` As a second observed case, add `public func n() {}` to the class, recompile and inspect it with the same isolated runner. Both CLI invocations exited 0. The fixtures were read without executing their code. Stored outputs, fixture sources and demangled symbols were inspected again before submission; no new runtime reproduction on main is claimed. ### Expected behavior Per-facet `examined` and `decoded` should count the same documented population, with decoded records drawn from examined records. Descriptor accounting could report 1/1 here; slot accounting could report 2/2 and 3/3. Either approach should preserve the returned slots and truthful completeness, including truncation and unresolved-implementation limitations. Source-derived contract: [docs/tool-design.md:171-175](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/docs/tool-design.md#L171-L175) requires facet completeness to derive from examined records and exhaustiveness. [Supported class-vtable decoding](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/docs/native-investigation.md#L419-L427) includes these non-generic, non-resilient classes. The [coverage schema](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/src/domain/native/objcSwiftMetadata.ts#L242-L249) exposes both counters but does not itself enforce their relationship. ### Impact and scope P2: ordinary supported Swift classes produce incompatible machine-readable coverage counters. A caller cannot compute a meaningful decode ratio from them. The observed impact is bounded to this accounting: incorrect implementation addresses, omitted slots, security effects and downstream consumer failures were not established. Callers can inspect returned per-slot decode states while continuing to honor facet truncation and unsupported-metadata reasons. MCP transport, other platforms and production-binary prevalence were not tested. ### REA version `rea-agents` 6.7.0; source baseline `9d8e7803326a382cbbb3ffdb900f5e93e5442d68`. Fetched main `60956c8c56475e7aaab9559f780908851677ced6` has no later changes to the cited expected/failure source files. This main recheck used source history, without rebuilding or rerunning that revision. ### Runtime and platform Node.js v26.12.0; macOS 27.0.1 arm64; Apple Swift 6.4 (swiftlang-6.4.0.34.1 clang-2100.3.34.1). No Hopper/Ghidra involved. ### Target and artifact context Self-built thin Mach-O dylibs with one non-generic, non-resilient Swift class. The first has an allocating initializer and `m()`; the second also has `n()`. Default inspection options and isolated home/XDG directories were used. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence The first CLI output contains this projection: ```json { "slots": [ {"slot_index": 10, "decode": "decoded"}, {"slot_index": 11, "decode": "decoded"} ], "coverage": [{ "facet": "swift_class_vtable_descriptors", "status": "complete", "reason": null, "examined": 1, "decoded": 2 }] } ``` The added-method control returns decoded slots 10, 11 and 12, with the same facet `complete`, reason null, `examined: 1, decoded: 3`. Demangled symbols independently identify one nominal type descriptor for `cov.CoverageUnit` and two method descriptors in the first fixture, for its allocating initializer and `m()`. Evidence provenance: observed; local verification: independently reproduced at the baseline. ### Typed outcome or recovery expected Keep the successful metadata result and every returned slot, but use coherent units for the coverage counters. Proposed restoration checks: replay both fixtures; adding a method must not pair one examined descriptor with three decoded slots. Clamping `decoded` or dropping slots would not establish accurate accounting. Preserve complete status for fully resolved supported fixtures and partial coverage for truncation or unresolved implementations. Those partial cases are proposed checks, not additional observed runs. ### Analysis or suspected cause Source-derived reasoning: [AppleSwiftVtables.ts:43-47](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/src/native/AppleSwiftVtables.ts#L43-L47) increments `examined` per admitted type-section entry before the class-kind guard, including entries later skipped as non-class. [Line 116](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/src/native/AppleSwiftVtables.ts#L116) increments `decoded` per resolved vtable slot. [AppleDispatchCoverage.ts:215-235](https://github.com/morluto/rea/blob/9d8e7803326a382cbbb3ffdb900f5e93e5442d68/src/native/AppleDispatchCoverage.ts#L215-L235) publishes those totals unchanged while calculating status from exhaustiveness, truncation and unresolved slots. This explains the mixed units without implying that the observed complete statuses are false. ### Additional context Prepared with AI assistance and independently reproduced through the public CLI. Related #5464 concerns JavaScript summary counters; #5543 concerns Objective-C method omissions under record-budget exhaustion; #4369 concerns empty Swift owner recovery, fixed by #4469. Their contracts and restoration checks differ from this accounting defect. No last known good version or introducing commit was established.
Author
Owner

@matbrgz commented on GitHub (Oct 20, 2026):

Cross-referencing with canonical issue #4368 ([Bug] Empty Swift conformance names escape entry recovery and invalidate dispatch metadata) and sibling Swift dispatch metadata reports #5424, #5529, #5543, #5682, #5683, #5685.

Consolidated under Cluster AZ (Native Dispatch Metadata Inspection & Provider Fallbacks) in tracker #4330.

<!-- gh-comment-id:6303855401 --> @matbrgz commented on GitHub (Oct 20, 2026): Cross-referencing with canonical issue #4368 (`[Bug] Empty Swift conformance names escape entry recovery and invalidate dispatch metadata`) and sibling Swift dispatch metadata reports #5424, #5529, #5543, #5682, #5683, #5685. Consolidated under **Cluster AZ (Native Dispatch Metadata Inspection & Provider Fallbacks)** in tracker #4330.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3430
No description provided.