You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[wasi] FileStream_ctor_sfh_fa_buffer.InconsistentFileAccessThrows corrupts memory (interpreter and ReadyToRun) #134957
With CoreCLR WASI ReadyToRun, System.IO.FileSystem.DisabledFileLocking.Tests crashes during a garbage collection while running System.IO.Tests.FileStream_ctor_sfh_fa_buffer.InconsistentFileAccessThrows. The GC's stack scan reports a slot through the wasm32 GC info that does not hold a valid object reference, and marking it reads outside linear memory:
memory fault at wasm address 0x800560a0 in linear memory of size 0x8000000
wasm trap: out of bounds memory access
0: corerun!WKS::gc_heap::mark_object_simple(unsigned char**)
1: corerun!WKS::GCHeap::Promote(Object**, ScanContext*, unsigned int)
2: corerun!PromoteCarefully(...)
3: corerun!GcEnumObject(void*, Object**, unsigned int)
4: corerun!TGcInfoDecoder<Wasm32GcInfoEncoding>::ReportUntrackedSlots(GcSlotDecoder<Wasm32GcInfoEncoding>&, REGDISPLAY*, ...)
5: corerun!TGcInfoDecoder<Wasm32GcInfoEncoding>::EnumerateLiveSlots(REGDISPLAY*, bool, unsigned int, ...)
6: corerun!EECodeManager::EnumGcRefs(REGDISPLAY*, EECodeInfo*, unsigned int, ...)
7: corerun!GcStackCrawlCallBack(CrawlFrame*, void*)
...
14: corerun!WKS::gc_heap::mark_phase(int)
...
19: corerun!WKS::gc_heap::allocate_uoh_object(unsigned long, unsigned int, int, long long&)
The collection is triggered by a large object allocation. The untracked slot comes from a ReadyToRun-compiled frame on the stack. The wasmtime backtrace is truncated at 20 frames, so the method with the bad GC info is not identified yet. Rerun with a deeper backtrace or under a debugger to find the frame, then compare its GC info with the slots the compiled code actually initializes.
This may be the same root cause as #134947, an out-of-bounds access that also appears only with ReadyToRun.
Seen in trimmed ReadyToRun runs of the CoreCLR WASI library tests for #134813.
Note
This issue was drafted with the help of GitHub Copilot.
Update: this still reproduces on current main (a1cdbbb), which includes the recent wasm GC reporting fixes (#134779). It is not specific to ReadyToRun.
wasm trap: out of bounds memory access in corerun!pread ← SystemNative_PRead ← InvokeUnmanagedCalli ← InterpExecMethod. With XunitShowProgress, the last started test is FileStream_ctor_sfh_fa_buffer.InconsistentFileAccessThrows.
Trimmed ReadyToRun
The xunit runner dies with ArgumentNullException (Parameter 'source') in TestClassRunner.RunAsync, then an unreachable trap in SfiNextWorker during exception dispatch.
Trimmed ReadyToRun, this test skipped
1103 run, 0 failed
The test reads through a write-only handle and writes through a read-only handle and expects UnauthorizedAccessException. The interpreter failure is in pread for the failing read, which suggests the failed read/write path on WASI hands native code a bad buffer pointer or length. The original R2R GC-time fault address 0x800560a0 (in 128 MB of linear memory) looks like the same kind of invalid pointer. GC slot reporting is probably a symptom rather than the cause. A root-cause investigation is in progress.
On #134813 the quarantine now covers all WASI configurations.
Note
This comment was drafted with the help of GitHub Copilot.
changed the title [-][wasi][R2R] GC stack scan reports an invalid untracked slot from ReadyToRun code[/-][+][wasi] FileStream_ctor_sfh_fa_buffer.InconsistentFileAccessThrows corrupts memory (interpreter and ReadyToRun)[/+]on Sep 30, 2026
This isn't a GC-reporting or R2R bug. It's a wasi-libc bug in the wasip2 pread shipped with wasi-sdk 33, which this repo builds with.
pread consumes the descriptor.read result before checking whether the call failed (pread.c at wasi-sdk-33):
boolok=filesystem_method_descriptor_read(file_handle, nbyte, offset, &contents, &error_code);
bytes_read=contents.f0.len; // uninitialized on failurememcpy(buf, contents.f0.ptr, bytes_read); // garbage src pointer and lengthwasip2_list_u8_free(&contents.f0); // free() of a garbage pointerif (!ok) { ... return-1; }
On failure the binding never writes contents, so pread copies a stale-stack-sized chunk from a stale-stack pointer into the caller's buffer, then frees a stale pointer. Depending on what's on the stack, that traps immediately (the interpreter's out of bounds memory access in corerun!pread) or silently corrupts the heap or managed objects. The corruption case explains the ReadyToRun symptoms: a GC-time memory fault at a garbage address, or ArgumentNullException in the xunit runner followed by an unreachable trap during exception dispatch.
InconsistentFileAccessThrows triggers it by opening the file FileAccess.Write, wrapping the handle as FileAccess.Read, and calling ReadByte(). That goes RandomAccess.ReadAtOffset → SystemNative_PRead → pread on a write-only fd, descriptor.read fails with bad-descriptor, and the bad copy and free follow. Browser passes because emscripten's pread doesn't have this bug.
A 20-line C program built with the repo's wasi-sdk 33 (--target=wasm32-wasip2) that calls pread on an O_WRONLY fd traps with the same out of bounds memory access in pread.
On current main (137a6701515), interpreter, -class System.IO.Tests.FileStream_ctor_sfh_fa_buffer:
Unmodified: traps at corerun!pread <- SystemNative_PRead.
With a guard in SystemNative_PRead: Tests run: 10 Passed: 10 Failed: 0.
With the guard, the full System.IO.FileSystem.DisabledFileLocking.Tests suite runs to completion (1517 tests, no trap). All three InconsistentFileAccessThrows variants pass. The remaining failures are unrelated WASI gaps: PNSE from APM/threading and pipe conformance tests.
Fix
Workaround, draft PR [wasi] Avoid wasi-libc pread memory corruption on write-only descriptors #134988: on TARGET_WASI, SystemNative_PRead and the PReadV fallback reject fds that aren't open for reading (fcntl(F_GETFL)) with EBADF before calling pread. EBADF is what the fixed libc returns, and it maps to the UnauthorizedAccessException the test expects.
Real fix: move to wasi-sdk 34. That also covers other descriptor.read failures, such as I/O errors, that the guard doesn't. The workaround has a TODO-WASI pointing here so it can be removed after the bump.
I moved the label from area-CodeGen-coreclr to area-System.IO, since the runtime-side change is in System.Native's pal_io.c.
Activity
dotnet-policy-service commentedon Sep 30, 2026
Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
See info in area-owners.md if you want to be subscribed.
[wasi] Fix up System.IO.FileSystem.DisabledFileLocking tests on WASI
dotnet-policy-service commentedon Sep 30, 2026
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.
[wasi] Quarantine InconsistentFileAccessThrows on all WASI configurat…
lewing commentedon Sep 30, 2026
Update: this still reproduces on current main (a1cdbbb), which includes the recent wasm GC reporting fixes (#134779). It is not specific to ReadyToRun.
wasm trap: out of bounds memory accessincorerun!pread←SystemNative_PRead←InvokeUnmanagedCalli←InterpExecMethod. WithXunitShowProgress, the last started test isFileStream_ctor_sfh_fa_buffer.InconsistentFileAccessThrows.ArgumentNullException (Parameter 'source')inTestClassRunner.RunAsync, then anunreachabletrap inSfiNextWorkerduring exception dispatch.The test reads through a write-only handle and writes through a read-only handle and expects
UnauthorizedAccessException. The interpreter failure is inpreadfor the failing read, which suggests the failed read/write path on WASI hands native code a bad buffer pointer or length. The original R2R GC-time fault address0x800560a0(in 128 MB of linear memory) looks like the same kind of invalid pointer. GC slot reporting is probably a symptom rather than the cause. A root-cause investigation is in progress.On #134813 the quarantine now covers all WASI configurations.
Note
This comment was drafted with the help of GitHub Copilot.
[-][wasi][R2R] GC stack scan reports an invalid untracked slot from ReadyToRun code[/-][+][wasi] FileStream_ctor_sfh_fa_buffer.InconsistentFileAccessThrows corrupts memory (interpreter and ReadyToRun)[/+]lewing commentedon Sep 30, 2026
Root cause
This isn't a GC-reporting or R2R bug. It's a wasi-libc bug in the wasip2
preadshipped with wasi-sdk 33, which this repo builds with.preadconsumes thedescriptor.readresult before checking whether the call failed (pread.catwasi-sdk-33):On failure the binding never writes
contents, sopreadcopies a stale-stack-sized chunk from a stale-stack pointer into the caller's buffer, then frees a stale pointer. Depending on what's on the stack, that traps immediately (the interpreter'sout of bounds memory accessincorerun!pread) or silently corrupts the heap or managed objects. The corruption case explains the ReadyToRun symptoms: a GC-time memory fault at a garbage address, orArgumentNullExceptionin the xunit runner followed by anunreachabletrap during exception dispatch.InconsistentFileAccessThrowstriggers it by opening the fileFileAccess.Write, wrapping the handle asFileAccess.Read, and callingReadByte(). That goesRandomAccess.ReadAtOffset→SystemNative_PRead→preadon a write-only fd,descriptor.readfails withbad-descriptor, and the bad copy and free follow. Browser passes because emscripten'spreaddoesn't have this bug.Upstream fixed it in WebAssembly/wasi-libc#862 (issue WebAssembly/wasi-libc#861), first released in wasi-sdk 34.
Evidence
--target=wasm32-wasip2) that callspreadon anO_WRONLYfd traps with the sameout of bounds memory accessinpread.137a6701515), interpreter,-class System.IO.Tests.FileStream_ctor_sfh_fa_buffer:corerun!pread <- SystemNative_PRead.SystemNative_PRead:Tests run: 10 Passed: 10 Failed: 0.System.IO.FileSystem.DisabledFileLocking.Testssuite runs to completion (1517 tests, no trap). All threeInconsistentFileAccessThrowsvariants pass. The remaining failures are unrelated WASI gaps: PNSE from APM/threading and pipe conformance tests.Fix
TARGET_WASI,SystemNative_PReadand thePReadVfallback reject fds that aren't open for reading (fcntl(F_GETFL)) withEBADFbefore callingpread.EBADFis what the fixed libc returns, and it maps to theUnauthorizedAccessExceptionthe test expects.descriptor.readfailures, such as I/O errors, that the guard doesn't. The workaround has aTODO-WASIpointing here so it can be removed after the bump.I moved the label from
area-CodeGen-coreclrtoarea-System.IO, since the runtime-side change is in System.Native'spal_io.c.Note
This comment was generated with GitHub Copilot.
dotnet-policy-service commentedon Sep 30, 2026
Tagging subscribers to this area: @dotnet/area-system-io
See info in area-owners.md if you want to be subscribed.
[wasi] Bump WASI SDK to 34.0 and wasmtime to 49.0.1 (#134992)
[wasi] Fix up System.IO.FileSystem.DisabledFileLocking tests on WASI
[wasi] Quarantine InconsistentFileAccessThrows on all WASI configurat…
[wasi] Remove the InconsistentFileAccessThrows quarantine
[wasi] Fix up System.IO.FileSystem.DisabledFileLocking tests on WASI
[wasi] Quarantine InconsistentFileAccessThrows on all WASI configurat…
[wasi] Remove the InconsistentFileAccessThrows quarantine
[wasi] Fix up System.IO.FileSystem.DisabledFileLocking tests on WASI
[wasi] Quarantine InconsistentFileAccessThrows on all WASI configurat…
[wasi] Remove the InconsistentFileAccessThrows quarantine
[wasi] Fix up System.IO.FileSystem.DisabledFileLocking tests on WASI
[wasi] Quarantine InconsistentFileAccessThrows on all WASI configurat…
[wasi] Remove the InconsistentFileAccessThrows quarantine