Skip to content

SignalsWin: fix VEH return value and register behind AddressSanitizer's handler - #331

Merged
dylan-conway merged 3 commits into
mainfrom
farm/be8272b5/signalswin-continue-search
Jul 24, 2026
Merged

dylan-conway merged 3 commits into
mainfrom
farm/be8272b5/signalswin-continue-search

Conversation

@robobun

@robobun robobun commented Jul 24, 2026 •

Copy link
Copy Markdown
Collaborator

Two fixes to the Windows vectored exception handler in Source/WTF/wtf/win/SignalsWin.cpp.

1. Return EXCEPTION_CONTINUE_SEARCH when no handler claims the fault

vectoredHandler() initialized its result to EXCEPTION_EXECUTE_HANDLER and returned it whenever no registered WTF signal handler reported SignalAction::Handled. A vectored exception handler may only return EXCEPTION_CONTINUE_EXECUTION or EXCEPTION_CONTINUE_SEARCH; EXCEPTION_EXECUTE_HANDLER (1) is a frame-based SEH filter result and is invalid here. The dispatcher does not treat it as "search" — it re-executes the faulting instruction with nothing resolved, re-entering the handler in an unbounded loop.

The visible consequence is under AddressSanitizer: ASan reserves its shadow up front and commits pages lazily from its own vectored handler on the first touch. WTF's handler returning the invalid value on those first-touch faults starved ASan's committer, so instrumented binaries died at startup with a silent access violation. The default result is now EXCEPTION_CONTINUE_SEARCH, matching the POSIX/Mach NotHandled path, so unclaimed faults proceed down the chain.

2. Register behind AddressSanitizer's handler under ASAN_ENABLED

SignalHandlers::finalize() registered with AddVectoredExceptionHandler(1, …), placing WTF's handler ahead of ASan's. ASan's handler unpoisons the exception dispatch context (the CONTEXT record and dispatch stack) for the handlers that run after it; a handler in front of it reads those regions while still poisoned, so every fault produced spurious stack-buffer-* reports attributed to WTF's handler. Under ASAN_ENABLED the handler is now registered with First = 0 (behind ASan's); non-ASan builds keep the existing first-slot registration.

With both changes an ASan-instrumented consumer of these libraries starts and runs cleanly.

…he fault

vectoredHandler() defaulted its result to EXCEPTION_EXECUTE_HANDLER and
returned it from a vectored exception handler. That constant is only valid
as an __except() filter result; a VEH must return either
EXCEPTION_CONTINUE_EXECUTION or EXCEPTION_CONTINUE_SEARCH. Returning 1 is
treated as continue-execution by the dispatcher, so an access violation no
WTF handler claimed would re-execute the faulting instruction, fault again,
and re-enter the handler in an unbounded loop until the stack was gone.

Because WTF installs itself first in the VEH chain, handlers registered
behind it (ASan's shadow-page committer is the obvious one) never saw
exceptions they own.

Default to EXCEPTION_CONTINUE_SEARCH so unclaimed faults continue down the
chain, matching the Mach (KERN_FAILURE) and POSIX (call old handler) paths
for SignalAction::NotHandled. The SignalAction::Handled case still returns
EXCEPTION_CONTINUE_EXECUTION as before.
@coderabbitai

coderabbitai Bot commented Jul 24, 2026 •

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Changes

Windows exception handling

Layer / File(s) Summary
Propagate unhandled exceptions
Source/WTF/wtf/win/SignalsWin.cpp
vectoredHandler now returns EXCEPTION_CONTINUE_SEARCH by default so unhandled faults proceed through the exception chain.
Adjust ASan handler registration
Source/WTF/wtf/win/SignalsWin.cpp
SignalHandlers::finalize registers the vectored handler with priority 0 for ASan builds and priority 1 otherwise.
🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the fixes but does not follow the required WebKit PR template or include the Bugzilla and review fields. Add the template sections: bug title, Bugzilla link, Reviewed by line, bug-fix explanation, and changed-path bullets.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and accurately summarizes the main Windows VEH and ASan registration changes.

Comment @coderabbitai help to get the list of available commands.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM — one-constant fix aligning the VEH return value with the documented Windows contract.

What was reviewed:

  • Confirmed PVECTORED_EXCEPTION_HANDLER must return only EXCEPTION_CONTINUE_EXECUTION or EXCEPTION_CONTINUE_SEARCH; EXCEPTION_EXECUTE_HANDLER is SEH-filter-only (all other uses in this repo are inside __except()).
  • Verified the Mach (KERN_FAILURE on not-handled) and POSIX (chain to oldAction.sa_sigaction) paths in Signals.cpp — this now matches them.
  • The Signal::Unknown early return a few lines up already used EXCEPTION_CONTINUE_SEARCH, and the Handled → EXCEPTION_CONTINUE_EXECUTION path is untouched.
Extended reasoning...

Overview

Single-line semantic change in Source/WTF/wtf/win/SignalsWin.cpp: the default return value of vectoredHandler() (a Windows vectored exception handler) is changed from EXCEPTION_EXECUTE_HANDLER (1) to EXCEPTION_CONTINUE_SEARCH (0), plus an explanatory comment. The SignalAction::Handled branch continues to return EXCEPTION_CONTINUE_EXECUTION as before.

Correctness

Per Microsoft's PVECTORED_EXCEPTION_HANDLER documentation, a VEH callback may return only EXCEPTION_CONTINUE_EXECUTION or EXCEPTION_CONTINUE_SEARCH. EXCEPTION_EXECUTE_HANDLER is only meaningful in an SEH __except() filter expression — every other occurrence of that constant in this repo (jsc.cpp, testRegExp.cpp, MiniBrowser, gtest, ANGLE StackWalker, skia) is inside an __except filter, not a VEH. ANGLE's own VEH in system_utils_win32.cpp:165-169 uses exactly the CONTINUE_EXECUTION / CONTINUE_SEARCH pair, matching this fix.

I also verified the cross-platform-parity claim: Source/WTF/wtf/threads/Signals.cpp:280 returns KERN_FAILURE when no Mach handler claims the fault, and lines 538-542 chain to the previously installed sigaction on POSIX when !didHandle. Returning EXCEPTION_CONTINUE_SEARCH is the Windows equivalent. The same function already returns EXCEPTION_CONTINUE_SEARCH at line 98 for Signal::Unknown, so both "not ours" paths are now consistent.

Security risks

None introduced. The change makes unclaimed hardware faults propagate to downstream handlers (ASan shadow committer, crash reporters, SEH frames) instead of being swallowed into an infinite re-fault loop. It does not add new code paths, allocate, or touch user-controlled data.

Level of scrutiny

Low-to-medium. It is a one-constant correction to a well-documented Windows API contract, with a clear failure mode described (infinite re-fault when a WTF handler declines an access violation). The blast radius is Windows-only and only affects the fallthrough path when no JSC signal handler claims the exception — the wasm/VMTraps handled path is unchanged.

Other factors

No prior human or bot review comments to address (CodeRabbit was rate-limited). The bug hunting system found no issues. The diff is minimal and mechanically verifiable against the Win32 docs, so I'm comfortable approving without human review.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM — one-constant fix to a documented-invalid VEH return value.

What was reviewed:

  • Confirmed EXCEPTION_EXECUTE_HANDLER is not a valid VEH return; EXCEPTION_CONTINUE_SEARCH is the correct "not mine" value and already used a few lines up for Signal::Unknown.
  • Cross-checked the Mach path (didHandle ? KERN_SUCCESS : KERN_FAILURE) and POSIX path (falls through to old sigaction) in Signals.cpp — this now matches both.
  • Checked the registered handlers (Wasm fault handler, VMTraps) — both legitimately return NotHandled for faults they don't own, so continuing the search is the intended behavior; the Handled → EXCEPTION_CONTINUE_EXECUTION path is unchanged.
Extended reasoning...

Overview

Single-line change in Source/WTF/wtf/win/SignalsWin.cpp: the default return value of vectoredHandler() is changed from EXCEPTION_EXECUTE_HANDLER (1) to EXCEPTION_CONTINUE_SEARCH (0), plus a three-line explanatory comment. The SignalAction::Handled branch that sets EXCEPTION_CONTINUE_EXECUTION is untouched.

Security risks

None introduced. The old value was undefined behavior for a vectored exception handler (Microsoft documents only EXCEPTION_CONTINUE_EXECUTION and EXCEPTION_CONTINUE_SEARCH as valid). The new behavior lets unclaimed faults propagate to later VEH handlers and, ultimately, the default unhandled-exception path — which is strictly safer than the previous re-execute-the-faulting-instruction loop. No auth, crypto, or permission surfaces touched.

Level of scrutiny

Low. This is a well-understood Windows API contract fix. The same function already returns EXCEPTION_CONTINUE_SEARCH for the Signal::Unknown early-out, so the change is internally consistent. I verified the parallel logic in Source/WTF/wtf/threads/Signals.cpp for Mach (line 280: KERN_FAILURE on not-handled) and POSIX (line 538+: chain to old sigaction) — the PR description's claim that this aligns Windows with the other platforms is accurate.

Other factors

The registered Signal::AccessFault handlers in JSC (WasmFaultSignalHandler.cpp, VMTraps.cpp) return SignalAction::NotHandled for faults outside their range, so nothing was relying on the old (broken) default to terminate the process — the old value just caused a fault loop. No outstanding reviewer comments; the only timeline entry is a CodeRabbit rate-limit notice.

@github-actions

github-actions Bot commented Jul 24, 2026 •

Copy link
Copy Markdown

Preview Builds

Commit Release Date
13894c73 autobuild-preview-pr-331-13894c73 2026-07-24 22:45:53 UTC
c8caa99b autobuild-preview-pr-331-c8caa99b 2026-07-24 07:41:58 UTC

Under ASAN_ENABLED, register the WTF vectored handler with First=0
instead of First=1. AddressSanitizer's runtime installs its own vectored
handler that commits shadow pages lazily and unpoisons the dispatch
context before the handlers behind it run. Registered ahead of it, the
WTF handler reads the CONTEXT record and the exception-dispatch stack
before ASan has unpoisoned them, so every fault produces spurious
stack-buffer reports attributed to the WTF handler. Registering behind
ASan removes those reports; non-ASan builds keep the existing first-slot
registration.
@dylan-conway dylan-conway changed the title SignalsWin: return EXCEPTION_CONTINUE_SEARCH from the VEH when no handler claims the fault SignalsWin: fix VEH return value and register behind AddressSanitizer's handler Jul 24, 2026

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM — correct fix aligning the VEH return value with the documented Windows contract, plus an ASan-only registration-order tweak.

What was reviewed:

  • Verified EXCEPTION_CONTINUE_SEARCH is the documented valid "not handled" return for a vectored handler; the old EXCEPTION_EXECUTE_HANDLER is a frame-SEH-only value.
  • Confirmed ASAN_ENABLED is always defined (0/1) in Compiler.h, so #if ASAN_ENABLED is correct and the non-ASan registration path is byte-identical to before.
  • Checked the POSIX Signals.cpp — the new default matches its NotHandled fall-through-to-old-action semantics.
Extended reasoning...

Overview

Two small changes to Source/WTF/wtf/win/SignalsWin.cpp:

  1. In vectoredHandler(), the default result when no registered WTF handler returns SignalAction::Handled changes from EXCEPTION_EXECUTE_HANDLER (1) to EXCEPTION_CONTINUE_SEARCH (0).
  2. In SignalHandlers::finalize(), under #if ASAN_ENABLED the vectored handler is registered with AddVectoredExceptionHandler(0, …) (append) instead of (1, …) (prepend), so ASan's own VEH runs first.

Security risks

None. This is Windows-only exception-dispatch plumbing. No user-controlled input, parsing, auth, or crypto is involved. The change makes unclaimed faults propagate to the rest of the handler chain rather than returning an undocumented value — strictly a correctness improvement.

Level of scrutiny

Moderate. Signal/exception handling underpins VM traps and Wasm fault handling, so it's worth reading carefully — but the diff is tiny and mechanical:

  • Change 1 replaces a value that is invalid per Microsoft's VEH contract (a vectored handler may return only EXCEPTION_CONTINUE_EXECUTION or EXCEPTION_CONTINUE_SEARCH) with the documented correct one. The Handled → EXCEPTION_CONTINUE_EXECUTION path is untouched, so JSC's own trap handling (the case that matters in production) behaves identically.
  • Change 2 is compiled in only when ASAN_ENABLED is truthy (defined 0/1 in wtf/Compiler.h), so release/production builds keep the exact prior AddVectoredExceptionHandler(1, …) call. The ASan-only ordering change affects debug/instrumented builds and is well-motivated (ASan unpoisons the dispatch context for handlers that follow it).

Other factors

  • No CODEOWNERS entry covers Source/WTF/wtf/win/.
  • Preview builds for the PR head completed successfully per the CI comment.
  • The new default mirrors the POSIX/Mach path in Signals.cpp, where NotHandled falls through to the previously-installed action — so cross-platform semantics are now consistent.
  • SignalAction::ForceDefault still hits the switch default: and now yields EXCEPTION_CONTINUE_SEARCH; that's the closest Windows analogue to "let the default handler take it" and is no worse than the prior invalid return.
  • The bug hunting system found no issues.

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