Skip to content

fix(tauri-runtime-wry): stop leaking ObjC retains in with_webview - #15224

Merged
Legend-Master merged 6 commits into
tauri-apps:devfrom
krishpranav:with-webview-retained-leak-15210
Jul 9, 2026
Merged

fix(tauri-runtime-wry): stop leaking ObjC retains in with_webview#15224
Legend-Master merged 6 commits into
tauri-apps:devfrom
krishpranav:with-webview-retained-leak-15210

Conversation

@krishpranav

@krishpranav krishpranav commented Apr 11, 2026

Copy link
Copy Markdown
Contributor

fix(tauri-runtime-wry): stop leaking ObjC retains in with_webview

Problem

WebviewMessage::WithWebview used Retained::into_raw on Apple targets, transferring ownership to raw pointers in this path without reclaiming it.

Fix

  • Replaced Retained::into_raw with scoped retained bindings + Retained::as_ptr in the macOS/iOS WithWebview branches.
  • Added .changes/fix-objc-retain-leak.md with valid covector front matter ("tauri-runtime-wry": patch:bug).

Testing

cargo fmt --check
cargo clippy -p tauri-runtime-wry --all-features -- -D warnings
cargo check -p tauri-runtime-wry --all-features
cargo test -p tauri-runtime-wry --lib
cargo clippy -p tauri-runtime-wry --target aarch64-apple-darwin --all-features -- -D warnings

Manual stress run (with_webview loop, 3 min) shows stable RSS without unbounded growth: 116.5 MB -> 116.9 MB (+0.4 MB).

Visual Verification

App running (active loop)

Start End
Stress test start Stress test end

RSS monitor (terminal)

Terminal RSS log

Fixes #15210

@krishpranav
krishpranav requested a review from a team as a code owner April 11, 2026 11:52
@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 Apr 11, 2026
@Legend-Master Legend-Master reopened this Apr 11, 2026
@krishpranav

krishpranav commented Apr 11, 2026

Copy link
Copy Markdown
Contributor Author

Hey @Legend-Master

Just a small fix for the Apple WithWebview pointer handoff. Swapped Retained::into_raw for a scoped Retained + as_ptr to keep ownership balanced. Also fixed the .changes metadata so the checks pass cleanly. No API changes or anything big. Attached a stress run with stable RSS.

Lemme know!

@github-actions

github-actions Bot commented Apr 11, 2026

Copy link
Copy Markdown
Contributor

Package Changes Through 9c3ef2d

There are 14 changes which include tauri with minor, tauri-bundler with minor, tauri-cli with minor, @tauri-apps/cli with minor, tauri-utils with minor, tauri-build with minor, tauri-macos-sign with minor, tauri-runtime-wry with minor, tauri-runtime with minor, tauri-codegen with minor, tauri-macros with minor, tauri-plugin with minor, tauri-driver with minor, @tauri-apps/api 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
@tauri-apps/api 2.11.1 2.12.0
tauri-utils 2.9.3 2.10.0
tauri-macos-sign 2.3.4 2.4.0
tauri-bundler 2.9.4 2.10.0
tauri-runtime 2.11.3 2.12.0
tauri-runtime-wry 2.11.4 2.12.0
tauri-codegen 2.6.3 2.7.0
tauri-macros 2.6.3 2.7.0
tauri-plugin 2.6.3 2.7.0
tauri-build 2.6.3 2.7.0
tauri 2.11.5 2.12.0
@tauri-apps/cli 2.11.4 2.12.0
tauri-cli 2.11.4 2.12.0
tauri-driver 2.0.6 2.1.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

@sftse

sftse commented Apr 11, 2026

Copy link
Copy Markdown
Contributor

Transforming a &T into *mut T through *const T is not a good pattern and can easily lead to UB, although it's probably safe here as they point to an ObjC Arc which doesn't have sensible mutation. Could fix this by changing the pointers in the WebView to *const.

@krishpranav

Copy link
Copy Markdown
Contributor Author

Just a follow-up, any updates?

@min-median-max

Copy link
Copy Markdown

Hi @krishpranav — are you still working on this?

Asking because we're seeing the same leak in a shipping app. On macOS each with_webview call moves the WKWebView / manager / NSWindow out via Retained::into_raw and never recovers them, leaking one retain each — for us that keeps the WebContent process alive for the whole lifetime of the app; closing a view never releases it, so they accumulate. We confirmed it's the runtime and not our own misuse (no leak with our native hooks off; per-view PID deltas show zero survivors only after the into_rawas_ptr change). So this fix matters to us too.

On @sftse's cast note — we borrowed with Retained::as_ptr and made the Webview pointer fields *const, which avoids the *mut cast without touching the public accessors.

Would you be able to update the PR along those lines?

Legend-Master
Legend-Master previously approved these changes Jul 9, 2026

@Legend-Master Legend-Master 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.

Thanks, just a bit of nitpicks and you'll need to sign your commits for me to merge this

https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits

Also, let's add a todo comment on those fields to change the types to *const std::ffi::c_void in v3, as well as documenting you should not try to mutate those pointer (there shouldn't be a case in normal use cases for you to mutate them anyways)

Comment thread crates/tauri-runtime-wry/src/lib.rs Outdated
let ns_window = webview.ns_window();
f(Webview {
webview: Retained::into_raw(webview.webview()) as *mut objc2::runtime::AnyObject
webview: Retained::as_ptr(&platform_webview) as *const std::ffi::c_void

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.

Suggested change
webview: Retained::as_ptr(&platform_webview) as *const std::ffi::c_void
webview: Retained::as_ptr(&platform_webview).cast_mut()

Also the other ones

@krishpranav

Copy link
Copy Markdown
Contributor Author

Hi, sure will do those changes.

WebviewMessage::WithWebview converted Retained<T> values to raw pointers with Retained::into_raw on Apple targets, transferring ownership without a corresponding release in this path.

Use scoped Retained bindings and Retained::as_ptr for pointer handoff to the callback payload. This preserves borrowing semantics for callback lifetime and keeps Objective-C retain/release balanced when the retained values drop after callback return.

Fixes tauri-apps#15210
Format .changes/fix-objc-retain-leak.md with valid covector front matter and package bump metadata.

This resolves CI failures in check-change-tags and covector status for this PR.
Add a doc/TODO note on the Apple with_webview pointer fields
(webview/manager/ns_window/view_controller): they're borrowed from
ObjC Retained handles and must not be mutated through, and should
become *const c_void in v3.
@krishpranav
krishpranav force-pushed the with-webview-retained-leak-15210 branch from ea0d25d to 7bc0dc5 Compare July 9, 2026 13:01
@krishpranav

Copy link
Copy Markdown
Contributor Author

@Legend-Master rebased and signed all commits, added that TODO/doc comment you asked for should be good for a re-review whenever you get a chance!

@krishpranav
krishpranav requested a review from Legend-Master July 9, 2026 13:02
@Legend-Master

Copy link
Copy Markdown
Contributor

In case you didn't see

#15224 (comment)

Apply Legend-Master's suggestion: use .cast_mut() instead of casting
through *const c_void first, across all macOS/iOS with_webview fields.
@krishpranav

Copy link
Copy Markdown
Contributor Author

@Legend-Master it's done.

@Legend-Master Legend-Master 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.

Thanks

@Legend-Master
Legend-Master merged commit a370f65 into tauri-apps:dev Jul 9, 2026
2 checks passed
@sftse

sftse commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

This stops the leak for sure because it doesn't take "ownership" of the object anymore, but it's not super clear to me whether this occurs at the cost of a crash. The Webview struct has no lifetime constraint and is passed to some opaque closure which could be sending it anywhere. Even if this is currently sound, this looks to be separated from disaster by merely one small, remote refactor.

@Legend-Master

Legend-Master commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

The lifetime is a problem here for sure... Although you should not do, you can send the PlatformWebview to outside of the closure which will result in dangling pointers.

Since tauri-runtime-wry is not a stable crate, we can change the Webview fields, what about we change them to Retain<...> and only do the as_ptr in tauri's PlatformWebview methods (PlatformWebview will be safe while the raw pointers are always unsafe with no lifetime guarantees)? @sftse

We can also deprecate the old methods and adding in new safe objc2-* equivalents like what we did for other platforms.

@sftse

sftse commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Since tauri-runtime-wry is not a stable crate, we can change the Webview fields, what about we change them to Retain<...> and only do the as_ptr in tauri's PlatformWebview methods (PlatformWebview will be safe while the raw pointers are always unsafe with no lifetime guarantees)?

Yeah, was thinking along those lines. There's a lot of channels involved with Tauri, and I honestly don't have the full picture of what the lifecycle of the objects are. As a rule of thumb, borrowing and channels interact poorly, it's either borrowing with apis like in rayon that are able to close over the lifetime (block until borrow is no longer needed) or channels + refcounting. I'd say this fix went in the wrong direction.

@Legend-Master

Copy link
Copy Markdown
Contributor

Actually, PlatformWebview is not Send while with_webview requires the closure to be Send meaning you can't use a channel to move out the PlatformWebview. That being said, storing the objc-* Retain<...>s still have other benefits.

Proksima pushed a commit to Proksima/tauri that referenced this pull request Aug 5, 2026
…uri-apps#15224)

* tauri-runtime-wry: avoid leaking ObjC retains in with_webview

WebviewMessage::WithWebview converted Retained<T> values to raw pointers with Retained::into_raw on Apple targets, transferring ownership without a corresponding release in this path.

Use scoped Retained bindings and Retained::as_ptr for pointer handoff to the callback payload. This preserves borrowing semantics for callback lifetime and keeps Objective-C retain/release balanced when the retained values drop after callback return.

Fixes tauri-apps#15210

* chore: add changes file for tauri-apps#15210

* changes: fix covector metadata for objc leak patch

Format .changes/fix-objc-retain-leak.md with valid covector front matter and package bump metadata.

This resolves CI failures in check-change-tags and covector status for this PR.

* chore(tauri-runtime-wry): document Apple Webview pointer fields

Add a doc/TODO note on the Apple with_webview pointer fields
(webview/manager/ns_window/view_controller): they're borrowed from
ObjC Retained handles and must not be mutated through, and should
become *const c_void in v3.

* refactor(tauri-runtime-wry): simplify pointer cast in with_webview

Apply Legend-Master's suggestion: use .cast_mut() instead of casting
through *const c_void first, across all macOS/iOS with_webview fields.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-slop Low effort content, see https://github.com/tauri-apps/tauri?tab=contributing-ov-file#ai-tool-policy

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] WebviewMessage::WithWebview on macOS leaks Objective-C objects due to Retained::into_raw without corresponding release

4 participants