Skip to content

fix(linux): re-apply signal handlers periodically to survive JSC lazy init#5507

Merged
leaanthony merged 4 commits into
masterfrom
fix/linux-signal-handler-jsc-race
May 27, 2026
Merged

fix(linux): re-apply signal handlers periodically to survive JSC lazy init#5507
leaanthony merged 4 commits into
masterfrom
fix/linux-signal-handler-jsc-race

Conversation

@leaanthony

@leaanthony leaanthony commented May 25, 2026

Copy link
Copy Markdown
Member

Problem

Closes #5506

WebKit's JavaScriptCore (JSC) lazily installs signal handlers when JavaScript first executes — not at WebView creation time. These handlers (SIGSEGV for JIT crash recovery, SIGUSR1 for GC thread synchronisation) are registered without SA_ONSTACK, overwriting Go's handlers which require the flag.

When a JIT page fault occurs, the kernel delivers SIGSEGV on the normal stack instead of the alternate signal stack. JSC's handler runs and calls Go's handler as a fallback; Go's sigtramp detects the missing SA_ONSTACK flag and panics:

non-Go code set up signal handler without SA_ONSTACK flag

Root Cause

The existing one-shot fix (g_idle_add in v2, direct call in v3) runs before any JavaScript has executed. JSC installs its bad handlers later — when the first JS runs — wiping out the fix entirely.

The reporter's workaround confirmed this: a g_timeout_add polling at 50ms intervals prevented the crash because it continuously re-applied the fix through the JSC initialisation window.

Fix

Add a g_timeout_add_full periodic timer (50ms, 100 iterations = 5 seconds) on the GTK main thread that re-applies install_signal_handlers() throughout the JSC lazy-initialisation window. The g_free destroy-notify prevents a memory leak if the app exits before the timer completes.

The existing one-shot idle/direct calls are preserved as the "first immediate fix" layer. The periodic timer takes over from there and covers the race window.

Files Changed

  • v2 v2/internal/frontend/desktop/linux/frontend.go — periodic timer added to fix_signal_handlers_after_gtk_init()
  • v3 GTK4 v3/pkg/application/linux_cgo.c + .h + .go — new schedule_signal_handler_fix() called from appNew()
  • v3 GTK3 v3/pkg/application/linux_cgo_gtk3.go — same logic inline, called inside fixSignalHandlers.Do

Summary by CodeRabbit

  • Bug Fixes
    • Improved Linux stability when loading web content by applying an additional signal fix, adding a short (~5s) periodic retry that re-applies the fix every 50ms during startup, and re-applying the fix once a page finishes loading to reduce crashes and improve reliability.

Review Change Stack

… init

WebKit's JavaScriptCore (JSC) lazily installs signal handlers (SIGSEGV for
JIT crash recovery, SIGUSR1 for GC thread sync) when JavaScript first
executes - not at WebView creation. These handlers are registered without
SA_ONSTACK, overwriting Go's handler which requires it.

The existing one-shot fix runs before any JS executes, so JSC wipes it out
immediately when it initialises. A 50ms periodic timer (100 iterations =
5s) running on the GTK main thread re-applies install_signal_handlers()
throughout the JSC initialisation window, ensuring Go's SA_ONSTACK
requirement is always satisfied.

Fixes #5506
Copilot AI review requested due to automatic review settings May 25, 2026 10:04
@coderabbitai

coderabbitai Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7c5a2d55-3956-401e-b1a9-49ce1e08af23

📥 Commits

Reviewing files that changed from the base of the PR and between 7a3459d and 1ce4492.

📒 Files selected for processing (1)
  • v3/pkg/application/linux_cgo.go

Walkthrough

Adds C and Go changes that repeatedly re-apply install_signal_handlers() (now covering SIGUSR1) via a GLib timeout scheduler and calls to re-run the fix during WebView creation and after page load (DomReady / WEBKIT_LOAD_FINISHED).

Changes

Signal Handler Timeout Retry

Layer / File(s) Summary
C GLib timeout and header
v3/pkg/application/linux_cgo.h, v3/pkg/application/linux_cgo.c
Adds schedule_signal_handler_fix(void) declaration; extends install_signal_handlers() to include SIGUSR1; adds a GLib timeout callback that calls install_signal_handlers() every 50ms for a short countdown and exports schedule_signal_handler_fix() which allocates the countdown and registers the timeout.
V3 Go + GTK integration
v3/pkg/application/linux_cgo.go, v3/pkg/application/linux_cgo_gtk3.go
Adds fixSignalHandlers sync.Once, calls C.install_signal_handlers() and C.schedule_signal_handler_fix() on first WebView creation, updates GTK3-side install_signal_handlers() to include SIGUSR1, and re-invokes C.install_signal_handlers() on WEBKIT_LOAD_FINISHED.
V2 frontend timeout and DomReady re-apply
v2/internal/frontend/desktop/linux/frontend.go
Extends install_signal_handlers() to include SIGUSR1; adds install_signal_handlers_timeout scheduled via g_timeout_add_full (50ms ticks for ~5s) in addition to existing g_idle_add; calls C.install_signal_handlers() on "DomReady".

Sequence Diagram(s)

sequenceDiagram
  participant App as Go App
  participant WebView
  participant GLibTimer as GLib Timer
  participant C as install_signal_handlers

  App->>WebView: create WebView (first)
  App->>C: C.install_signal_handlers() (initial)
  App->>C: C.schedule_signal_handler_fix() (starts GLib timer)
  WebView->>C: WebKit/JSC may install signal handlers (including SIGUSR1)
  GLibTimer->>C: timeout tick -> call install_signal_handlers() (every 50ms)
  C-->>GLibTimer: continue until countdown expires (~5s)
  WebView->>App: WEBKIT_LOAD_FINISHED / DomReady
  App->>C: C.install_signal_handlers() (re-apply after load)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • wailsapp/wails#4855: Prior change that added the one-shot g_idle_add mitigation and adjusted fix_signal_handlers_after_gtk_init().
  • wailsapp/wails#4856: Related updates to the Linux WebKit signal-handler workaround and timing around WebView creation.

Suggested labels

Linux, Bug, v3, go

Suggested reviewers

  • atterpac

Poem

🐰 I hop through C and GLib ticks,

fifty-millisecond mends and little tricks,
SIGUSR1 gets SA_ONSTACK back in line,
five seconds of watching keeps handlers fine.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: re-applying signal handlers periodically to address JSC lazy initialization issues on Linux.
Description check ✅ Passed The PR description is comprehensive, including a clear problem statement, root cause analysis, the proposed fix, and files changed. However, the testing section of the template checklist is not filled out.
Linked Issues check ✅ Passed The code changes directly address issue #5506 by implementing a periodic timer-based signal handler re-application (50ms, 5 seconds) and load-finished hooks, matching the verified workaround and addressing the root cause of JSC lazy initialization overwriting Go's SA_ONSTACK handlers.
Out of Scope Changes check ✅ Passed All changes are focused on signal handler re-application for Linux JSC compatibility across v2 and v3 versions, with no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/linux-signal-handler-jsc-race

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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 addresses Linux startup crashes caused by WebKit JavaScriptCore lazily overwriting Go’s signal handlers without SA_ONSTACK by periodically re-applying Wails’ install_signal_handlers() during the early initialization window (instead of relying only on a one-shot fix).

Changes:

  • Add a GLib g_timeout_add_full timer (50ms, 100 iterations) to repeatedly call install_signal_handlers() for ~5 seconds.
  • Introduce schedule_signal_handler_fix() for v3 GTK4 (C + header) and invoke it from appNew().
  • Apply the same periodic scheduling approach to v3 GTK3 and v2 Linux frontend initialization.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
v3/pkg/application/linux_cgo.h Declares schedule_signal_handler_fix() for GTK4 build path.
v3/pkg/application/linux_cgo.go Schedules the periodic signal-handler reapply during app creation (GTK4 path).
v3/pkg/application/linux_cgo.c Implements the periodic timer callback + scheduler (GTK4 path).
v3/pkg/application/linux_cgo_gtk3.go Adds equivalent periodic timer logic and schedules it once (GTK3 path).
v2/internal/frontend/desktop/linux/frontend.go Adds periodic timer scheduling after GTK init (v2 path).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread v3/pkg/application/linux_cgo.go
Comment thread v3/pkg/application/linux_cgo.c
Comment thread v3/pkg/application/linux_cgo_gtk3.go
Comment thread v2/internal/frontend/desktop/linux/frontend.go
…imer

Addresses code review feedback on #5507:

- Add SIGUSR1 to install_signal_handlers(): JSC uses SIGUSR1 for GC
  thread synchronisation and installs it without SA_ONSTACK (visible as
  "Overriding existing handler for signal 10" in stderr). The fix_signal
  helper only adds SA_ONSTACK to whatever handler is already registered,
  so this is safe for both Go's and JSC's handler.

- Hook WEBKIT_LOAD_FINISHED (v3 GTK4/GTK3) and DomReady (v2): by
  page-load completion JSC is guaranteed to have initialised and
  installed all its signal handlers. A targeted call to
  install_signal_handlers() here provides a deterministic, event-driven
  fix that complements the periodic timer.

- Anchor the GTK4 periodic timer to first WebView creation (sync.Once
  inside windowNewWebview) rather than appNew(), mirroring the GTK3
  pattern. This ensures the 5s coverage window starts when JSC can
  actually initialise, not before any WebView exists.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@v3/pkg/application/linux_cgo.go`:
- Around line 1196-1201: The GTK4 path currently only calls
C.schedule_signal_handler_fix() inside fixSignalHandlers.Do, leaving a 0–50ms
window; update the fixSignalHandlers.Do block to first call
C.install_signal_handlers() immediately (mirroring the GTK3 path) and then call
C.schedule_signal_handler_fix() so the handlers are installed once right away
before the periodic 50ms timer is armed; reference the existing
fixSignalHandlers.Do, C.install_signal_handlers, and
C.schedule_signal_handler_fix symbols when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6986ea11-ee10-4b99-8ca0-14c6e81e8d7f

📥 Commits

Reviewing files that changed from the base of the PR and between 1d7d141 and 7a3459d.

📒 Files selected for processing (4)
  • v2/internal/frontend/desktop/linux/frontend.go
  • v3/pkg/application/linux_cgo.c
  • v3/pkg/application/linux_cgo.go
  • v3/pkg/application/linux_cgo_gtk3.go

Comment thread v3/pkg/application/linux_cgo.go
@Adam-D-Lewis

Adam-D-Lewis commented May 26, 2026

Copy link
Copy Markdown

Verified this fixes a real-world Wails 2 app on Ubuntu 26.04 LTS + libwebkit2gtk-4.1-0 2.52.3-0ubuntu0.26.04.2.

Test app: nebari-dev/nebi at main (commit f024a78). Without this PR, nebi-desktop crashes within 1-2 seconds of startup with the canonical fatal:

Overriding existing handler for signal 10. Set JSC_SIGNAL_FOR_GC if you want WebKit to use a different signal
signal 11 received but handler not on signal stack
fatal error: non-Go code set up signal handler without SA_ONSTACK flag

Test method:

  • Fresh worktree of nebi at main
  • go get github.com/wailsapp/wails/v2@v2.12.0
  • replace github.com/wailsapp/wails/v2 => /local/checkout/of/this/PR/v2
  • No app-side workaround in place

With this PR applied: app runs cleanly, serves real HTTP traffic through the embedded asset server, exits cleanly on window close. Reproduced over multiple runs. Looking forward to the merge — would let us delete the workaround.

(Downstream tracking issue: nebari-dev/nebi#350.)

Adam-D-Lewis added a commit to nebari-dev/nebi that referenced this pull request May 26, 2026
…dler clobbering (#351)

* fix(desktop): re-apply SA_ONSTACK so WebKitGTK JSC doesn't crash Go runtime

Wails 2.11.0 installed SA_ONSTACK on signal handlers before gtk_init, which
let WebKit's later signal install (without SA_ONSTACK) clobber it. Bump to
Wails 2.12.0, which defers the fix to g_idle_add after gtk_init, AND add a
linux-only background ticker that re-applies SA_ONSTACK on SIGSEGV/SIGBUS/etc.
JavaScriptCore installs its GC/Wasm trap handlers lazily on first JS context
creation — which can land after the one-shot idle pass — so a periodic
re-fix is needed to be reliable on Ubuntu 24.04+ / libwebkit2gtk-4.1.

Verified: nebi-desktop now boots, serves the embedded API for 30+ seconds
of clicking around, and exits cleanly on window close.

* fix(desktop): document upstream removal condition and add SIGUSR1

Reference upstream Wails fix wailsapp/wails#5507 (closes wails#5506) so a
future maintainer knows this Go-side ticker can be deleted once the Wails
release containing that PR lands and we've bumped to it. Also add SIGUSR1
(JSC GC thread sync) to the fix list, matching the additional signal that
the upstream PR fixes.
@leaanthony leaanthony merged commit 6329e9d into master May 27, 2026
14 of 16 checks passed
@leaanthony leaanthony deleted the fix/linux-signal-handler-jsc-race branch May 27, 2026 13:24
leaanthony added a commit that referenced this pull request Jun 3, 2026
…ze (#5530)

WebKit's JavaScriptCore uses SIGUSR1 to suspend/resume threads for
conservative GC stack scanning. The signal-handler fix added in
alpha.97 (#5507) re-applied SA_ONSTACK to every signal it touched,
including SIGUSR1. Once JSC installs its own SIGUSR1 handler it owns the
signal (Go no longer handles it), so forcing SA_ONSTACK makes that
handler run on Go's alternate signal stack, breaking GC thread
synchronisation. This froze the WebKit UI during idle garbage
collection (most visibly with the inspector open) while the Go backend
kept running.

Remove fix_signal(SIGUSR1) from install_signal_handlers in v3 (GTK4 +
GTK3) and v2. The genuine #5506 fix for SIGSEGV/SIGBUS (which Go needs
SA_ONSTACK for) is unaffected.

Fixes #5527

https://claude.ai/code/session_01AtT1CjDNRArXv1eddXte2a

Co-authored-by: Claude <noreply@anthropic.com>
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.

Linux: SA_ONSTACK lost on some Wails 2.12 apps; one-shot g_idle_add mitigation doesn't catch every case

3 participants