Skip to content

Fixed macOS window close with pending async Go-bound function call crashes - #4354

Closed
joshhardy wants to merge 3 commits into
wailsapp:v3-alphafrom
orbforge:v3-alpha-bugfix/4353_window-close-execjs-crash
Closed

joshhardy wants to merge 3 commits into
wailsapp:v3-alphafrom
orbforge:v3-alpha-bugfix/4353_window-close-execjs-crash

Conversation

@joshhardy

@joshhardy joshhardy commented Jun 13, 2025 •

Copy link
Copy Markdown
Contributor

Description

This fixes an issue where pending async results from Go-bound JS calls would trigger C.windowExecJS in webview_window_darwin.go even if the underlying window was destroyed by adding a guard to check if the parent window is destroyed.

This fixes a crash due to use-after-free (invalid webView access).

Fixes #4353

Type of change

Please select the option that is relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

This is a macOS-specific issue that has been tested on macOS and confirmed to fix my production app as well as my minimal repro example app. All changes are in a darwin file.

  • Windows
  • macOS
  • Linux

Test Configuration

wails3 doctor
 Wails (v3.0.0-alpha.9)  Wails Doctor 
                                                                                                                                                                                                     
# System 

┌──────────────────────────────────────────────────┐
| Name          | MacOS                            |
| Version       | 15.5                             |
| ID            | 24F74                            |
| Branding      | MacOS 15.5                       |
| Platform      | darwin                           |
| Architecture  | arm64                            |
| Apple Silicon | true                             |
| CPU           | Apple M3 Pro                     |
| CPU 1         | Apple M3 Pro                     |
| CPU 2         | Apple M3 Pro                     |
| GPU           | 14 cores, Metal Support: Metal 3 |
| Memory        | 18 GB                            |
└──────────────────────────────────────────────────┘

# Build Environment 

┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
| Wails CLI      | v3.0.0-alpha.9                                                                                                            |
| Go Version     | go1.23.5                                                                                                                  |
| -buildmode     | exe                                                                                                                       |
| -compiler      | gc                                                                                                                        |
| CGO_CFLAGS     |                                                                                                                           |
| CGO_CPPFLAGS   |                                                                                                                           |
| CGO_CXXFLAGS   |                                                                                                                           |
| CGO_ENABLED    | 1                                                                                                                         |
| CGO_LDFLAGS    |                                                                                                                           |
| DefaultGODEBUG | asynctimerchan=1,gotypesalias=0,httpservecontentkeepheaders=1,tls3des=1,tlskyber=0,x509keypairleaf=0,x509negativeserial=1 |
| GOARCH         | arm64                                                                                                                     |
| GOARM64        | v8.0                                                                                                                      |
| GOOS           | darwin                                                                                                                    |
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

# Dependencies 

┌────────────────────────────────────────────────────────────────────────┐
| Xcode cli tools | 2409                                                 |
| npm             | 10.9.2                                               |
| *NSIS           | Not Installed. Install with `brew install makensis`. |
└─────────────────────── * - Optional Dependency ────────────────────────┘

# Diagnosis 

 SUCCESS  Your system is ready for Wails development!

Checklist:

  • I have updated website/src/pages/changelog.mdx with details of this PR
  • My code follows the general coding style of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Summary by CodeRabbit

  • Documentation

    • Updated the changelog to note a fix for macOS crashes when closing a window with a pending asynchronous Go-bound function call.
  • Bug Fixes

    • Resolved an issue on macOS where closing a window with an ongoing asynchronous operation could cause the application to crash.

@dosubot dosubot Bot added the size:XS This PR changes 0-9 lines, ignoring generated files. label Jun 13, 2025
@coderabbitai

coderabbitai Bot commented Jun 13, 2025 •

Copy link
Copy Markdown
Contributor

Walkthrough

A changelog entry was added documenting a fix for a macOS crash when closing a window with pending async Go-bound calls. The internal logic of the execJS method in macosWebviewWindow was updated to prevent JavaScript execution if the parent window is destroyed, addressing a use-after-free crash scenario.

Changes

File(s) Change Summary
docs/src/content/docs/changelog.mdx Added changelog entry describing the macOS window close crash fix and referencing the PR.
v3/pkg/application/webview_window_darwin.go Updated execJS method to return early if the parent window is destroyed, preventing a crash.

Sequence Diagram(s)

sequenceDiagram
    participant JS as JavaScript (async call)
    participant Go as Go-bound function
    participant Window as macosWebviewWindow
    participant Parent as Parent Window

    JS->>Go: Initiate async Go-bound call
    Go-->>JS: Completes, triggers execJS
    Go->>Window: execJS(js)
    alt Window or Parent destroyed
        Window->>Window: Early return, skip JS execution
    else Window and Parent alive
        Window->>Window: Execute JS via C.windowExecJS
    end
Loading

Assessment against linked issues

Objective Addressed Explanation
Prevent crash when closing macOS window with pending async Go-bound call (#4353) ✅
Ensure JS execution is not attempted on destroyed window/parent (#4353) ✅

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes found.

Suggested reviewers

  • leaanthony

Poem

A window closed, async in flight,
No more crashes in the night!
Mac bunnies hop with joyful glee,
For use-after-free is history.
With careful checks and changelog penned,
The code is safe—no need to mend!
🐇✨


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5dd8794 and 83edc4e.

📒 Files selected for processing (2)
  • docs/src/content/docs/changelog.mdx (1 hunks)
  • v3/pkg/application/webview_window_darwin.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/src/content/docs/changelog.mdx
  • v3/pkg/application/webview_window_darwin.go
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@dosubot dosubot Bot added the Bug Something isn't working label Jun 13, 2025
@github-actions github-actions Bot added the Documentation Improvements or additions to documentation label Jun 13, 2025

@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: 0

🧹 Nitpick comments (1)
docs/src/content/docs/changelog.mdx (1)

126-131: Minor wording / consistency nitpick

The new bullet reads:

Fixed macOS window close with pending async Go-bound function call crashes …

Grammar feels off (plural crashes vs. singular fix items). Everywhere else the pattern is “Fixed … crash” or “Fixed … issue”. For consistency:

- - Fixed macOS window close with pending async Go-bound function call crashes
+ - Fixed crash when closing a macOS window with a pending async Go-bound function call

Pure docs update – apply if you care about wording consistency.

🧰 Tools
🪛 LanguageTool

[style] ~126-~126: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...m/leaanthony) based on work by @Kron. - Fixed focus calls when window is disabled or ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~127-~127: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...m/leaanthony) based on work by @Kron. - Fixed system trays not showing after taskbar ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~128-~128: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...m/leaanthony) based on work by @Kron. - Fixed fallbackResponseWriter not implementing...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d126a31 and 5dd8794.

📒 Files selected for processing (2)
  • docs/src/content/docs/changelog.mdx (1 hunks)
  • v3/pkg/application/webview_window_darwin.go (1 hunks)
🔇 Additional comments (1)
v3/pkg/application/webview_window_darwin.go (1)

1041-1052: Early-exit guard prevents use-after-free, but a race still exists

👍 The additional w.parent.isDestroyed() check is the right direction for avoiding the macOS crash.
⚠️ However, a very small time window remains between the guard (line 1048) and the actual invocation of C.windowExecJS (line 1051): the parent may be destroyed after the check but before the C call runs, re-introducing the use-after-free.

Consider one of the following to make the call 100 % safe:

-        if w.nsWindow == nil || w.parent.isDestroyed() {
+        // Capture pointer once and validate as late as possible
+        ns := w.nsWindow
+        if ns == nil || w.parent.isDestroyed() {
             return
         }
-        C.windowExecJS(w.nsWindow, C.CString(js))
+        // Re-check immediately before the C call.
+        if !w.parent.isDestroyed() {
+            C.windowExecJS(ns, C.CString(js))
+        }

or mark w.nsWindow = nil atomically inside destroy() right after the Objective-C window is closed; the nil check would then be sufficient.

Please double-check that destroy() is always invoked before the Objective-C object is freed and that it runs on the same goroutine to avoid data races.

leaanthony
leaanthony previously approved these changes Jun 14, 2025
@leaanthony
leaanthony enabled auto-merge (squash) June 14, 2025 00:21
@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jun 14, 2025
@leaanthony
leaanthony disabled auto-merge June 14, 2025 00:34
@sonarqubecloud

Copy link
Copy Markdown

@leaanthony

Copy link
Copy Markdown
Member

Hey @joshhardy - I had to update the semgrep runner too and both changes ended up being in the same branch so your changes are merged via #4356 with full attribution. Will close this for now.

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

Labels

Bug Something isn't working Documentation Improvements or additions to documentation lgtm This PR has been approved by a maintainer Linux MacOS size:XS This PR changes 0-9 lines, ignoring generated files. v3 Windows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants