Skip to content

[wasm] Fail hard when the engine rejects a Webcil R2R image - #132870

Merged
pavelsavara merged 2 commits into
dotnet:mainfrom
lewing:lewing-wasm-r2r-errors
Aug 28, 2026
Merged

[wasm] Fail hard when the engine rejects a Webcil R2R image#132870
pavelsavara merged 2 commits into
dotnet:mainfrom
lewing:lewing-wasm-r2r-errors

Conversation

@lewing

@lewing lewing commented Aug 28, 2026

Copy link
Copy Markdown
Member

Problem

Related to #132865 / #132855.

CoreCLR-on-wasm loads a per-assembly R2R image as a .wasm file sitting beside the assembly's .dll (src/coreclr/hosts/corerun/wasm/libCorerun.js, BrowserHost_ExternalAssemblyProbe). If that .wasm file exists but new WebAssembly.Module(...) throws - for example because crossgen2 emitted a function type exceeding the wasm engine's parameter-count limit (#132855) - the probe caught the exception and returned false, which is exactly what happens when there's simply no R2R image for that assembly at all.

(Note: the emitted wasm is well-formed; it's rejected because it exceeds an implementation-defined engine limit, not because it's malformed.)

The result: the runtime silently falls back to fully interpreting the whole assembly. The app still runs and tests still pass, with the only trace being a generic Ready to Run header not found: "<assembly>" log line - indistinguishable from the normal "never R2R compiled" case. A rejected R2R image can regress to full interpretation with zero visible signal.

Fix

Distinguish the two cases:

  • The .wasm sibling doesn't exist -> legitimate, silent fallback (unchanged).
  • The .wasm sibling exists but the engine rejects it during WebAssembly.Module construction -> this is not a normal fallback path, so it now throws instead of being swallowed.

Verification

Reproduced against an unfixed (pre-#132865) crossgen2 targeting browser-wasm, compiling System.Text.Json.Tests (which contains the 1003-param ClassWithManyConstructorParameters type from #132855) and running it under a rebuilt corerun.js/corerun.wasm:

Before this change:

Ready to Run header not found: "System.Text.Json.Tests".

...followed by Tests run: 346 Passed: 345 Failed: 1 - the whole assembly silently ran fully interpreted.

After this change:

Failed to construct WebAssembly module for Webcil image: {
  wasmPath: 'System.Text.Json.Tests.wasm',
  errorMessage: 'WebAssembly.Module(): param count of 1003 exceeds internal limit of 1000 @+1433'
}

process exits 1, no tests run at all - the rejection is immediately visible instead of silently degrading.

Note

This PR description and commit were drafted with GitHub Copilot assistance.

When a per-assembly R2R .wasm sibling exists but fails to parse as a
WebAssembly module (e.g. a function type exceeding the wasm parameter
limit), the runtime silently treated this the same as "no R2R image
for this assembly" and fell back to fully interpreting the assembly.
The only trace was an unrelated-looking "Ready to Run header not
found" log line, so a broken R2R image could go completely unnoticed.

Since the file exists, this is not the legitimate fallback case -
throw instead of returning false so the failure is loud.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7f88abc1-143e-4858-b69f-ce01228a87ad
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@lewing
lewing marked this pull request as ready for review August 28, 2026 01:33
Copilot AI lite review requested due to automatic review settings August 28, 2026 01:33
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@lewing lewing added the arch-wasm WebAssembly architecture label Aug 28, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
See info in area-owners.md if you want to be subscribed.

@lewing lewing changed the title [wasm] Fail hard when a Webcil R2R image is not valid wasm [wasm] Fail hard when a Webcil R2R image is not loadable wasm Aug 28, 2026
The module is well-formed wasm; it's rejected because it exceeds an
implementation-defined limit (e.g. the wasm parameter count), not
because it's malformed.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 7f88abc1-143e-4858-b69f-ce01228a87ad

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR changes the CoreCLR-on-wasm corerun host’s Webcil (per-assembly R2R) probe so that a present-but-unloadable sibling .wasm image no longer looks identical to the “no R2R image exists” case, by throwing on WebAssembly.Module(...) construction failure instead of returning false.

Changes:

  • Treat WebAssembly.Module(wasmBytes) failures as fatal by throwing, rather than silently falling back via return false.
  • Update the logged message to reflect that the .wasm module is invalid for a Webcil image.

Comment thread src/coreclr/hosts/corerun/wasm/libCorerun.js Outdated
Copilot AI review requested due to automatic review settings August 28, 2026 01:38
@lewing lewing changed the title [wasm] Fail hard when a Webcil R2R image is not loadable wasm [wasm] Fail hard when the engine rejects a Webcil R2R image Aug 28, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/coreclr/hosts/corerun/wasm/libCorerun.js:200

  • The new throw replaces the original exception with a new Error, which drops the original error object/stack. Preserving the original exception as an error cause makes this much easier to diagnose (especially for engine-specific rejection messages).
            } catch (e) {
                const errorMessage = e instanceof Error ? e.message : String(e);
                console.error("Failed to construct WebAssembly module for Webcil image:", { wasmPath, errorMessage });
                throw new Error(`Failed to construct WebAssembly module for Webcil image '${wasmPath}': ${errorMessage}`);
            }

@pavelsavara
pavelsavara merged commit a333841 into dotnet:main Aug 28, 2026
118 checks passed
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 12.0-preview1 milestone Aug 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-Host

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants