Skip to content

fix(macos): prevent use-after-free in stop_task on macOS 11 during WKWebView dealloc#1734

Open
x93008 wants to merge 2 commits into
tauri-apps:devfrom
x93008:fix/macos/macos_11_crash
Open

fix(macos): prevent use-after-free in stop_task on macOS 11 during WKWebView dealloc#1734
x93008 wants to merge 2 commits into
tauri-apps:devfrom
x93008:fix/macos/macos_11_crash

Conversation

@x93008
Copy link
Copy Markdown

@x93008 x93008 commented May 21, 2026

macOS 11 WebKit bug: during WKWebView dealloc, stopAllTasksForPage calls stop_task with already-freed task pointers. Any access (including the implicit objc_release from objc2 reference types) causes SIGSEGV.

Fix:

  • stop_task: use raw pointers (*mut AnyObject) instead of objc2 references to skip automatic retain/release. Body is no-op since task is invalid.
  • start_task response handler: explicit drop(webview) before drop(task) to ensure correct deallocation order.

closes #1733

Details

On macOS 11, WebKit's internal stopAllTasksForPage (triggered during [WKWebView dealloc]) passes already-freed WKURLSchemeTask pointers to the webView:stopURLSchemeTask: callback. Using objc2 reference types (&WryWebView, &ProtocolObject<dyn WKURLSchemeTask>) triggers implicit objc_retain on entry, which crashes on the freed pointer.

The explicit drop ordering (drop(webview) before drop(task)) prevents a scenario where dropping Retained<WryWebView> on a worker thread triggers dealloc → stopAllTasksForPagestop_task, while the task reference is still alive in the closure.

This is the same class of race condition reported in tauri-apps/tauri#11516 (macOS 15) and #1730.

@x93008 x93008 requested a review from a team as a code owner May 21, 2026 02:14
macOS 11 WebKit bug: during WKWebView dealloc, stopAllTasksForPage calls
stop_task with already-freed task pointers. Any access (including the
implicit objc_release from objc2 reference types) causes SIGSEGV.

Fix:
- stop_task: use raw pointers (*mut AnyObject) instead of objc2 references
  to skip automatic retain/release. Body is no-op since task is invalid.
- start_task response handler: explicit drop(webview) before drop(task) to
  ensure correct deallocation order.
@x93008 x93008 force-pushed the fix/macos/macos_11_crash branch from 2c26a8b to e8cf34d Compare May 21, 2026 02:26
@Legend-Master Legend-Master added the ai-slop Low effort content, see https://github.com/tauri-apps/tauri?tab=contributing-ov-file#ai-tool-policy label May 21, 2026
@x93008
Copy link
Copy Markdown
Author

x93008 commented May 21, 2026

Tested Platforms

OS Version Result
macOS 11.7.10 (Big Sur)
macOS 12.7.3 (Monterey)
macOS 13.6 (Ventura)
macOS 14.2.1 (Sonoma)
macOS 15.7.2 (Sequoia)
macOS 26.4.1 (Tahoe)
Windows 10 22H2 (19045.5011)
Windows 11 25H2 (26200.8457)

All platforms pass without crash.

@x93008 x93008 force-pushed the fix/macos/macos_11_crash branch from e8cf34d to 9be1a6b Compare May 21, 2026 03:05
@sftse
Copy link
Copy Markdown
Contributor

sftse commented May 21, 2026

Without knowing a lot about the platform, it would be very odd for pointers passed from the platform to user code to be invalid.

The manual drop order looks really wrong as well, a mixture between manual memory management and refcounting.
Probably worth investigating those arguments of the function on which somebody put FIXME.

@Legend-Master Legend-Master removed the ai-slop Low effort content, see https://github.com/tauri-apps/tauri?tab=contributing-ov-file#ai-tool-policy label May 25, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Package Changes Through 9be1a6b

There are 1 changes which include wry with minor

Planned Package Versions

The following package releases are the planned based on the context of changes in this pull request.

package current next
wry 0.55.1 0.56.0

Add another change file through the GitHub UI by following this link.


Read about change files or the docs at github.com/jbolda/covector

@x93008
Copy link
Copy Markdown
Author

x93008 commented May 27, 2026

Thanks for the feedback. Let me address both points:

Re: "it would be very odd for pointers passed from the platform to user code to be invalid"

You're right that this shouldn't happen, but it's a known WebKit bug that was fixed upstream. The specific issue is in WebURLSchemeHandlerCocoa::platformStopTask — prior to the fix in WebKit changeset 277235 (bug #225373):

"We weren't explicitly keeping a strong reference to the task given to the API client in startURLSchemeTask: and stopURLSchemeTask: which could cause all our internal maps to release their references to the task"

"We were keeping a HashSet of raw WebURLSchemeHandler pointers, then using each of them without keeping it alive."

The fix added auto strongTask = retainPtr(wrapper(task)) before calling into stopURLSchemeTask:. On macOS 11, which ships an older WebKit without this fix, the task pointer passed to stopURLSchemeTask: during dealloc can already be freed. This isn't theoretical — it's reproducible on macOS 11.7.10 and matches the crash in #1733.

See also #1484 which fixed a closely related issue on the start_task side: "The webview and task objects passed to start_task were not retained, leading to use-after-free issues in the responder." My PR addresses the same class of problem on the stop_task path — which #1484 did not cover.

Re: "The manual drop order looks really wrong"

I agree. The explicit drop ordering is a hack and I'm not confident it's the right approach. To be honest, I'm not deeply familiar with objc2's reference semantics in Rust or the idiomatic way to handle this kind of platform-level lifetime issue. My fix was pragmatic (it stops the crash across macOS 11–26) but clearly not elegant.

If someone more experienced with Rust + macOS/objc2 could suggest a cleaner solution, I'd be happy to rework the PR accordingly. The core constraint is: on macOS 11, stop_task will receive freed pointers — we need to handle that somehow without triggering implicit retain/release.

Copy link
Copy Markdown
Contributor

@Legend-Master Legend-Master left a comment

Choose a reason for hiding this comment

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

Also thanks for the context,

FIXME: though we give it a static lifetime, it's not guaranteed to be valid.

is a leftover from #1484 and can be removed now

}
};

// webview must drop before task: if webview drop triggers dealloc →
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.

Does this alone fix the problem?

Copy link
Copy Markdown
Contributor

@sftse sftse May 27, 2026

Choose a reason for hiding this comment

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

Is this change still necessary once we figure out what to do with stop_task?

I'm reading the explanation as, dropping webview is necessary because we call stop_task on the freed task, which the other hunk is supposed to address.

_task: *mut AnyObject,
) {
webview.remove_custom_task_key(task.hash());
// no-op: avoid accessing task/webview — macOS 11 may pass freed pointers here
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.

I don't think we can simply removing this?

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.

stop_task use-after-free crash on macOS 11 during WKWebView dealloc

3 participants