# FIXP replay bounds and native output review — 2026-10-13

Session `codex-review-bounds-1013` reviews replay-store capacity/range handling
and Java native FIXP output-buffer contracts. Three parallel reviewers inspected
C++, Java and Rust; root reviewed the .NET implementations and integrated the
findings. The qualification base is `788764935`.

## Changes

Rust's fixed replay-store constructor multiplied the slot count by payload
capacity without checking overflow. In a release build, two slots with a
capacity of `usize::MAX / 2 + 1` produced a zero-length backing vector and an
apparently constructed store that could not save records. The file replay
store delegates to this constructor and inherited the defect. Checked
multiplication now rejects the dimensions before allocating either vector.
Two public integration tests exercise both constructors and two overflowing
dimension pairs without attempting a large allocation.

Nine Java native FIXP wrappers passed read-only direct buffers to JNI as
writable outputs. JNI can obtain their native address and write through it,
bypassing Java's read-only contract. The wrappers now reject those outputs
before entering JNI, using the existing error code for each operation.
Coverage includes file/mmap replay lookup, session-ID snapshots, simulator
responses, socket-session sends and pumping, and step/drain operations on all
four runtime wrappers. Closed-handle checking retains its existing precedence.
Read-only inputs continue to work. Regression fixtures check ordinary and
sliced views, unchanged backing storage, unchanged sequence/cursor state,
retained pending messages and successful writable retries.

.NET's fixed, file and mmap replay stores could scan a retained window before
rejecting a range larger than capacity. The range methods now reject those
requests before iteration. The original per-record checks remain: fixed/mmap stores permit
a range ending at `ulong.MaxValue`; file stores permit payloads only through
`long.MaxValue - 1`, reserving the endpoint for the next-sequence checkpoint.
Six regression cases cover full/partial ranges, holes, eviction, invalid counts,
endpoint boundaries and durable close/reopen. These behavioral cases also
pass the original implementation; the .NET change is a latency optimization.

## Qualification

All builds run in this session's worktree with outputs under
`LIBHFT_BUILD_ROOT`, an actual shared host CPU lease and a guard against foreign
heavy jobs. Native builds cap make parallelism at 16; Rust uses 8 jobs.
Java and .NET build development/open native libraries at their normal separate
locations and require the native-load control to pass. Test identities are
matched against discovery and skip/ignored counts are checked explicitly.

| Final candidate selection | Development | Open | Skipped/ignored |
| --- | ---: | ---: | ---: |
| Java FIXP native/output and protocol controls | 154 | 134 | 0 |
| .NET replay stores and finalization controls | 90 | 61 | 0 |
| Rust ordinary-profile store/lifecycle controls | 167 | 130 | 0 |
| Rust release capacity/reset integration | 4 | 4 | 0 |

These are 744 test executions across editions, not 744 distinct test methods.
Root independently audits the Java raw XML, discovery, baseline traces and
2,211 input hashes. Another reviewer independently audits Rust's 852 input
hashes, exact raw test identities, command logs, six candidate binaries and
actual Cargo feature/edition fingerprints. Both default Cargo features and
`telemetry-counters` are enabled. Rust uses toolchain 1.93.1.

Rust's original release constructors fail both new tests as expected: the
constructors return successfully for the overflowing dimensions. Each test
stops at its first dimension pair on the baseline; the candidate checks both
pairs. The baseline executable was subsequently replaced by Cargo; its source
pins, failing logs and command receipts are preserved. An optimized library-test
build hit the 30-minute lease limit before any test or discovery executed.
That timeout is retained and contributes no passing result. Broader checks
subsequently passed in the ordinary test profile; focused constructor/reset
tests passed in release with overflow checks and debug assertions disabled.

Java's baseline probe compiles the nine original wrappers into a separate
classpath directory and places it before the candidate classes, using the same
development JNI library. Class-origin receipts verify which wrappers load.
All 17 selected regressions fail on their intended assertions; the required-native
control passes. They demonstrate returned success/payload lengths for read-only
outputs, rather than setup or load failures. The final candidate has 13 new
tests plus strengthened pending-message assertions in four existing runtime
fixtures. Both native libraries, 4,139 candidate classes and 25 baseline classes
are hashed. The first receipt audit found Surefire had written reports to its
default directory despite the requested override; those actual reports were
preserved and audited before the remaining baseline/open stages continued.
The already passing development tests were not repeated.

.NET's original implementation passes all six new behavioral cases plus the
required-native control. The final development/open runs use the exact measured
capacity-only source hashes; native binaries are reused unchanged from the
initial freshly built libraries, and the test-local copies match their hashes.
The tests use SDK 8.0.423/runtime 8.0.29 and GCC 15/C++17 native builds. Java
uses JDK 21 and GCC 15/C++11/O3 with LTO parallelism 1.
An independent .NET audit verifies all 1,873 final input hashes, exact TRX and
discovery identities, native-copy hashes, retained benchmark assemblies, both
harness manifests and all 24 recomputed measurement rows.

## .NET measurements and rejected variants

Measurements use 4,096 populated slots, one-byte payloads, durable close/reopen
before timing, a non-inlined interface query and disabled tiered compilation.
Each run is pinned to CPU 0. Calibration targets at least 20 ms per sample;
seven samples in each of two processes give 14 samples per table cell. The
second pass reverses the order of the retained baseline/variant assemblies.
Allocation, population and recovery are outside timing. Every query result is
checked in warmup, calibration and measured batches. Both ordinary low sequences
and sequences near the supported endpoint are measured.

The initial version also checked the sequence endpoint before the loop and
removed the per-record wrap check. Although its tests passed, paired measurements
showed valid full-window regressions of about 20% in the fixed store and 36% in
the file store. That version was rejected. A second comparison retained the
original loop and tested a capacity-only guard and an additional single-record
fast path. The latter was unnecessary and regressed the file-store terminal
single-record probe. The final change is just one capacity comparison per store.

| Store/query | Original median | Final median |
| --- | ---: | ---: |
| Fixed: oversized range | 6,869 ns | 2.288 ns |
| File: oversized range | 20,866 ns | 2.940 ns |
| Mmap: oversized range | 6,813 ns | 2.644 ns |
| Fixed: valid single record, low sequence | 3.979 ns | 3.921 ns |
| File: valid single record, low sequence | 7.738 ns | 7.762 ns |
| Mmap: valid single record, low sequence | 4.250 ns | 4.179 ns |
| Fixed: valid full window, low sequence | 6,341 ns | 6,058 ns |
| File: valid full window, low sequence | 21,066 ns | 19,225 ns |
| Mmap: valid full window, low sequence | 6,297 ns | 6,305 ns |

Terminal valid full-window medians are 6,862→6,993 ns (fixed),
20,992→19,239 ns (file), and 6,865→6,896 ns (mmap). Endpoint-overflow
requests within capacity retain their original bounded scan. These measurements
describe the range query on this host, not end-to-end FIXP message latency.

A GCC 15 warning in the native build names index 16 on a 16-peer array in
`SessionRunnerMultiAcceptor::DispatchRetransmitRequest`. Review found every
call path passes an unchanged index through `PumpPeer`'s `IsPeerActive` bounds
check first. No reachable out-of-bounds path was found and no speculative C++
change was made.

## Retained evidence and integration

Evidence is retained under
`~/archive/libhft/reviews/2026-10-13-codex-review-bounds-1013/`:
`scratch/rust` holds baseline/release/ordinary-profile receipts and the timeout;
`scratch/java-output` holds original wrappers, raw reports and class-origin
proofs; `scratch/dotnet` holds initial qualification and the rejected variant;
`scratch/dotnet-refine` holds final comparison and qualification. The archive
manifest hashes every retained file.

The integration review through upstream `fb9805ca3` finds product changes only
in six files of the separately built Java Chronicle compatibility project.
None appears in either qualified Java compiler input list; the qualified FIXP,
native, managed and Rust inputs are otherwise unchanged. Other imported changes
are benchmark evidence, documentation and coordination claims.
