std79 exerciser campaign: 27/27 legs clean, two real D. bugs found (FABRIC-3.md §XII.4)
Completed the FORTH-79 standard-dictionary cross-ISA exerciser campaign: all
9 identities (zuse, rajames/bob, 00-06) x all 3 architectures (amd64,
aarch64, riscv64), 27 legs total. No crashes, no heap corruption across the
full run -- real-world validation that the WIREBIND use-after-free fix
(commit 9142dda, FABRIC-3.md SXIII) holds under genuine multi-cycle load,
not just the synthetic repro used to verify it.
Cross-identity parity is perfect: 0 diffs across all 9 identities on each
architecture. Two real bugs found in double-precision (D.) output, reported
per this project's standing rule (report, don't fix without being asked):
- M* on a negative operand -> D. reports DOUBLE-OVERFLOW, universally
across all three architectures (engine-level bug, not arch-specific).
- D+ on two negative doubles -> D. reports DOUBLE-OVERFLOW on aarch64
only; amd64 and riscv64 both correctly print -2. cell_t width confirmed
64-bit on all three (ruled out as the cause); exact mechanism still open.
Also fixed a test-harness timing issue (run_identities.sh): the retry
budget for USE-after-attach was too tight for boots with several live VMs
already accumulated, causing false "FAILED to USE" verdicts on identities
that actually succeeded a few seconds later. Widened the budget and
switched to smaller per-boot batches (2-3 identities) as the reliable
pattern for this shape of campaign.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXieurDfDSsDFdnSyusuWo
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
9142dda2d6
commit
cc81edf00a
+58
@@ -2141,6 +2141,64 @@ produced one interesting, unconfirmed data point before the first panic: `-123 4
|
||||
against the other two architectures once the campaign completes; not yet root-caused or reported
|
||||
as a bug on its own.
|
||||
|
||||
### XII.4 — Campaign completed: full 27-leg run (9 identities × 3 architectures), two real
|
||||
FORTH-79 engine bugs found — reported, NOT fixed (2026-09-10, after §XIII's WIREBIND fix)
|
||||
|
||||
All 27 legs run: `zuse` (auto-attached, exercised directly on Hera's own console) plus `rajames`
|
||||
(the `bob` thumbdrive's actual registered identity -- see the naming-mismatch note below) and
|
||||
`00`–`06` (WIREBIND hotplug attach → `USE` → feed the 24-case exerciser → detach), on amd64,
|
||||
aarch64, and riscv64. Run as several smaller boots per architecture rather than one 9-cycle boot,
|
||||
after observing real (non-bug) cumulative slowdown as more live VMs accumulate in a single long
|
||||
boot -- past roughly the 3rd-4th WIREBIND cycle in one boot, per-tick MSG-TICK pump cost grows
|
||||
enough that a fixed script retry budget starts racing the kernel and produces false "FAILED to
|
||||
USE" verdicts on cycles that actually succeeded a few seconds later (confirmed live: the identity
|
||||
in question showed `USE: now using NN` in the serial log immediately after the script had already
|
||||
given up and issued its own premature `device_del`). Splitting into 2-3-identity boots avoided
|
||||
this entirely and is the reliable pattern for any future campaign of this shape. (Separately
|
||||
confirmed: no heap corruption, no crash, across the full run -- real-world validation that
|
||||
§XIII's fix holds under genuine multi-cycle WIREBIND load, not just the synthetic 3-cycle repro
|
||||
used to verify it.)
|
||||
|
||||
**Cross-identity parity: perfect.** All 9 identities produce byte-identical T01-T24 output on
|
||||
each architecture (0 diffs, all three architectures). Confirms the FORTH-79 ACL-lockdown
|
||||
personality (`acl-std79.4th`) and the standard dictionary itself behave identically regardless of
|
||||
which identity is running them -- expected, and a useful negative result on its own.
|
||||
|
||||
**Two real bugs found, both in double-precision (`D.`) output, neither fixed here per this
|
||||
project's standing rule (report, don't fix without being asked):**
|
||||
|
||||
1. **`M*` on a negative operand → `D.` reports `DOUBLE-OVERFLOW`, on all three architectures
|
||||
identically.** `T14: -123 456 M* SWAP D. CR` should print `-56088`; it prints `DOUBLE-OVERFLOW`
|
||||
on amd64, aarch64, *and* riscv64 -- an engine-level bug (in `M*`'s double-cell result, or in
|
||||
`D.`'s own overflow check, or both), not architecture-specific. Universal, 100% reproducible
|
||||
across all 9 identities × 3 architectures.
|
||||
|
||||
2. **`D+` on two negative doubles → `D.` reports `DOUBLE-OVERFLOW`, aarch64 only.**
|
||||
`T19: -5 S>D 3 S>D D+ SWAP D. CR` (computing -5 + 3 in double precision) correctly prints `-2`
|
||||
on amd64 and riscv64, for all 9 identities -- but prints `DOUBLE-OVERFLOW` on aarch64, for all
|
||||
9 identities, 100% consistently. A genuine cross-ISA divergence, not a flaky/intermittent
|
||||
result.
|
||||
|
||||
Investigated (not fixed): read `double_word_d_plus()` (`double_words.c`) and
|
||||
`format_word_d_dot()` (`format_words.c`). `D.`'s check is `(dhigh == 0) || (dhigh == -1 &&
|
||||
dlow < 0)` -- correct FORTH-79 double-overflow semantics for a properly sign-extended result.
|
||||
`D+`'s carry-detection casts through `unsigned long` for the low-cell addition, which would be
|
||||
a real per-architecture bug *if* `unsigned long`'s width differed from `cell_t`'s on any of
|
||||
these targets -- checked and ruled out: `cell_t` is `int64_t` on amd64 and aarch64 explicitly
|
||||
(`vm.h`'s `#if defined(__amd64__) || ... || defined(__aarch64__) || ...`), and `signed
|
||||
long`/`unsigned long` (the `#else` branch, since `__riscv` isn't in that `#if`) on riscv64 --
|
||||
confirmed 64-bit on all three empirically via `T08: -1 1 RSHIFT` printing the identical
|
||||
19-digit `9223372036854775807` on every architecture, which only a genuine 64-bit arithmetic
|
||||
shift can produce. So this is not a cell-width mismatch; the actual mechanism producing a wrong
|
||||
`dhigh` specifically on aarch64, for this specific input, is still open.
|
||||
|
||||
**Next step, if picked up:** trace `D+`'s carry computation with a targeted probe on aarch64
|
||||
specifically for the `-5 S>D 3 S>D D+` sequence, comparing the pushed `result_high` against the
|
||||
expected `-1` right before `SWAP D.` runs -- narrows whether the defect is in `D+` itself, in
|
||||
`S>D`'s sign-extension, or somewhere in the stack-manipulation path between them. `M*`'s
|
||||
universal failure (bug 1) is a separate, likely unrelated defect and would need its own trace
|
||||
starting from `M*`'s own double-cell multiply, not `D+`'s.
|
||||
|
||||
## XIII. Heap corruption under repeated WIREBIND attach/detach cycling — root-caused and CLOSED
|
||||
2026-09-10
|
||||
|
||||
|
||||
Reference in New Issue
Block a user