Skip to content

Fix Windows/MSVC handling of large WAT input files and increase stack reserve#2748

Open
aydgelee wants to merge 1 commit into
WebAssembly:mainfrom
aydgelee:fix/windows-large-file-io
Open

Fix Windows/MSVC handling of large WAT input files and increase stack reserve#2748
aydgelee wants to merge 1 commit into
WebAssembly:mainfrom
aydgelee:fix/windows-large-file-io

Conversation

@aydgelee

Copy link
Copy Markdown

Fix Windows/MSVC handling of large WAT input files and increase stack reserve

Summary

This PR fixes Windows/MSVC handling of very large WAT input files in WABT tools, mainly wat2wasm.exe and wasm2wat.exe.

On Windows, wat2wasm can fail when reading WAT files larger than 2GB with errors such as:

value too large
unable to read file

The failure is caused by the MSVC file-reading path using APIs and types that are not safe for large files.

After fixing the large-file read path, processing very large WAT files can also trigger a Windows stack overflow during parsing:

0xC00000FD
STATUS_STACK_OVERFLOW

This PR addresses both issues for Windows/MSVC builds while keeping non-Windows behavior unchanged.

Changes

Windows/MSVC large-file reading

In src/common.cc, the Windows/MSVC file-reading path is updated to use 64-bit file APIs and file-size types:

_stat      -> _stat64
fseek      -> _fseeki64
ftell      -> _ftelli64
long size  -> __int64 / 64-bit file size

The MSVC path also performs size checks before resizing buffers and reads large files in chunks.

Windows/MSVC stack reserve

In CMakeLists.txt, Windows/MSVC builds now set a larger stack reserve for both tools:

if(MSVC)
  if(TARGET wasm2wat)
    target_link_options(wasm2wat PRIVATE "/STACK:536870912")
  endif()
  if(TARGET wat2wasm)
    target_link_options(wat2wasm PRIVATE "/STACK:536870912")
  endif()
endif()

536870912 bytes equals 512MB.

This is only applied for MSVC builds.

Non-Windows behavior preserved

Linux and macOS code paths remain unchanged. The large-file API changes are limited to COMPILER_IS_MSVC / MSVC branches.

No WebAssembly parser semantics are changed.

Validation

Validation was performed on Windows x64 using a WAT file larger than 2GB.

Baseline behavior

Using the baseline code, even with a larger stack reserve, wat2wasm still failed during file reading:

value too large
unable to read file

This confirms that increasing stack size alone does not fix the issue. The file-reading path must be updated.

Large-file IO fix without larger stack

After updating the Windows/MSVC file-reading path, wat2wasm no longer failed with value too large, but processing the large WAT file exited with:

0xC00000FD
STATUS_STACK_OVERFLOW

This confirms that the large-file reading fix works, and the next failure point is parser stack usage.

Full fix

With both changes applied:

  1. Windows/MSVC large-file reading fix
  2. 512MB stack reserve for wasm2wat and wat2wasm

The round-trip conversion succeeds:

wasm2wat.exe input.wasm -o output.wat
wat2wasm.exe output.wat -o roundtrip.wasm

Result:

ExitCode=0
roundtrip.wasm generated successfully

Impact

Windows/MSVC

Windows builds can now handle WAT files larger than 2GB in the tested scenario.

Affected tools:

wasm2wat.exe
wat2wasm.exe

Linux/macOS

Linux and macOS behavior is not changed.

The non-MSVC file-reading path remains the original WABT behavior, and /STACK is only applied under MSVC.

Build instructions

cmake -S . -B build -A x64 -DBUILD_TESTS=OFF
cmake --build build --config Release --target wasm2wat wat2wasm

Expected output files:

build/Release/wasm2wat.exe
build/Release/wat2wasm.exe

Verification commands

Generate WAT from wasm

$wasm2wat = "build\\Release\\wasm2wat.exe"
$wasm = "input.wasm"
$wat = "output.wat"

& $wasm2wat $wasm -o $wat

"LASTEXITCODE=$LASTEXITCODE"

Expected result:

LASTEXITCODE=0
output.wat generated successfully

Convert WAT back to wasm

$wat2wasm = "build\\Release\\wat2wasm.exe"
$out = "roundtrip.wasm"

& $wat2wasm $wat -o $out

"LASTEXITCODE=$LASTEXITCODE"

Expected result:

LASTEXITCODE=0
roundtrip.wasm generated successfully

Notes for reviewers

  • The large-file API changes are limited to the Windows/MSVC path.
  • The /STACK:536870912 option is only applied under MSVC.
  • Non-Windows code paths are intended to remain unchanged.
  • This PR does not change WebAssembly parsing semantics.

@sbc100

sbc100 commented May 28, 2026

Copy link
Copy Markdown
Member

For the 64-bit file ops can we do something simpler like?

#ifdef _MSC_VER
#define ftell _ftelli64
#define fseek _fseeki64
#endif

For the stack size do you know why we would need such a high value? IIUC the 8mb limit on linux seems to be enough there.

@aydgelee

aydgelee commented May 28, 2026

Copy link
Copy Markdown
Author

For the file APIs, I agree the goal is simply to use the 64-bit MSVC CRT variants. I avoided globally redefining ftell / fseek because that changes all uses in the translation unit through macros. Keeping the _fseeki64 / _ftelli64 usage local to the large-file read path makes the change more explicit and avoids affecting unrelated code.

For the stack size, one clarification: /STACK on Windows controls the PE stack reserve size, not memory committed at process startup. It is an upper bound for the thread stack address range, and pages are committed on demand as the stack grows.

The reason this is included here is that, after the 64-bit file I/O fix, the large WAT test no longer failed with value too large; instead, it hit:

0xC00000FD
STATUS_STACK_OVERFLOW

on Windows.

MSVC-linked executables typically default to a 1MB stack reserve, while Linux/macOS environments commonly have larger or configurable stack limits, for example an 8MB soft stack limit on many Linux systems. That likely explains why the same workload can pass on Linux/macOS but fail on Windows.

So the larger /STACK value is intended to raise the Windows stack reserve ceiling for very large WAT parsing workloads. It does not mean the process commits that amount of memory at startup.

@aydgelee aydgelee force-pushed the fix/windows-large-file-io branch 2 times, most recently from 177c0d9 to e5e08dc Compare May 28, 2026 08:17
@aydgelee

Copy link
Copy Markdown
Author

CI only reported a clang-format issue. No behavior change. Applied the suggested formatting diff.

Comment thread CMakeLists.txt Outdated
target_link_options(wasm2wat PRIVATE "/STACK:536870912")
endif ()
if (TARGET wat2wasm)
target_link_options(wat2wasm PRIVATE "/STACK:536870912")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we need a bigger stack on windows can we just go with 8mb (to match the linux default).

Also, can we just include this in wabt_executable so that it applies to all binaries? We might as well be consistent.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I moved the MSVC stack reserve into wabt_executable so it applies consistently to all WABT executables, and changed it to /STACK:8388608.

Verified on Windows x64 with a 2,978,116,509-byte WAT file. wat2wasm completed with exit code 0 and generated a 92,039,608-byte wasm output.

@sbc100

sbc100 commented May 28, 2026

Copy link
Copy Markdown
Member

For the file APIs, I agree the goal is simply to use the 64-bit MSVC CRT variants. I avoided globally redefining ftell / fseek because that changes all uses in the translation unit through macros. Keeping the _fseeki64 / _ftelli64 usage local to the large-file read path makes the change more explicit and avoids affecting unrelated code.

I'd much rather do it by via redefining these macros, keeping the the code otherwise the same on all platforms.

There really aren't very many file I/O codepaths in wabt, and I imagine we want to be able to handle large files in all of them.

Use MSVC macro redirection for stat, fseek, and ftell so the ReadFile path stays shared across platforms.

Move the MSVC stack reserve setting into the common wabt_executable helper and reduce it to 8MB.
@aydgelee aydgelee force-pushed the fix/windows-large-file-io branch from e5e08dc to 180b6bc Compare May 29, 2026 10:06
@aydgelee

Copy link
Copy Markdown
Author

Updated.

  • Switched MSVC large-file file I/O to macro-based redirection:
    • stat -> _stat64
    • fseek -> _fseeki64
    • ftell -> _ftelli64
  • Kept the main ReadFile path shared across platforms.
  • Kept the MSVC-only size_t bounds check and chunked fread for 2GB+ inputs.
  • Moved the MSVC stack reserve into wabt_executable().
  • Changed the stack reserve to /STACK:8388608.
  • Non-MSVC behavior is unchanged.

Windows x64 verification:

  • Commit: 180b6bc
  • wasm2wat.exe --version: 1.0.41
  • wat2wasm.exe --version: 1.0.41
  • unity_temp.wat: 2,978,116,509 bytes
  • roundtrip.wasm: 92,039,608 bytes
  • wat2wasm exit code: 0

No "value too large", no "unable to read file", and no STATUS_STACK_OVERFLOW with the 8MB stack reserve.

Comment thread src/stream.cc
}

static int SeekFile(FILE* file, size_t offset) {
if (offset > static_cast<size_t>(std::numeric_limits<int64_t>::max())) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this SeekFile really needed?

We are never going be seeing outside of int64_t range right? If we did that would fail on all platforms, no just windows.

In fact, fseek on non-windows is limited to long which can be 32-bit on all systems right?

Maybe just add this check for data.size() <= LONG_MAX before the call tofseek` rather than adding a separate function.

@sbc100

sbc100 commented May 29, 2026

Copy link
Copy Markdown
Member

Why do we need to read and write the files in chunks on windows? But not on other platforms? Could you update the PR description to include that maybe?

@sbc100

sbc100 commented May 29, 2026

Copy link
Copy Markdown
Member

Are you seeing these error on 64-bit windows too? Or is this mostly a fix for 32-bit windows?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants