Mono JIT miscompiles overlapping Span writes from net6.0 assemblies on .NET 10
- Dominant language
- C#
- Stars
- 18.3k
- Forks
- 5.6k
- PR merge metrics
- PR metrics pending
Description
Summary: TryComputeHash writing into a Span that aliases the input buffer produces all-zero output on Mono (Android ARM64 + Blazor WASM), but only when the calling assembly targets net6.0. The identical C# source compiled for net8.0 works correctly.
Repro: https://github.com/dangershony/SpanAliasingJitBug
Key findings:
- NBitcoin 7.0.46 (net6.0 TFM) → bug reproduces (all-zero hash output)
- NBitcoin 10.0.3 (net8.0 TFM) → bug does NOT reproduce (correct output)
- Same ComputeTapTweak source code in both versions
- Reproduces on Android ARM64 (Mono AOT/JIT) and Blazor WASM (Mono interpreter) — both use Mono runtime
- Does NOT reproduce on CoreCLR (Windows/Linux/macOS desktop)
- The pattern: Span buf = stackalloc byte[64]; /* write data into buf[0..32] and buf[32..64] */; SHA256.TryComputeHash(buf, buf[32..], out _); — input and output spans overlap within the same stackalloc
Impact: Silent data corruption. In our case, Bitcoin taproot output keys were computed as all-zeros, making funds permanently unspendable. Real funds were lost on signet before we caught it.
Affected runtime: .NET 10 Preview 4 (10.0.0-preview.4.25258.110), Mono JIT. Likely affects .NET 9 Mono as well (untested).
Workaround: Either (a) use separate buffers for input and output, or (b) consume a net8.0 build of the library instead of net6.0.
Contributor guide
Assessment
This issue has not been assessed yet.