Describe the bug
On macOS 11 (Big Sur), the app crashes with EXC_BAD_ACCESS (objc_release on a freed pointer) during WKWebView deallocation when
custom URL scheme handlers are registered.
The root cause is that during WKWebView dealloc, WebKit internally calls stopAllTasksForPage → platformStopTask, which invokes
the stop_task ObjC callback. On macOS 11, the task and/or webview pointers passed to this callback may already be freed. The
current stop_task function signature uses objc2 reference types (&WryWebView, &ProtocolObject<dyn WKURLSchemeTask>), which
trigger implicit objc_retain on function entry — calling objc_retain on a freed pointer causes the crash.
There is also a related race condition: when the async protocol response handler (running on a tokio worker thread) drops its
Retained<WryWebView>, this can trigger WKWebView dealloc on that thread. The dealloc path then calls stop_task with freed task
pointers, and the Retained<ProtocolObject<dyn WKURLSchemeTask>> in the response closure may still be alive — creating a
use-after-free if drop ordering is not enforced.
Steps To Reproduce
- Register a custom URL scheme handler via
WebViewBuilder::with_custom_protocol
- Load a page that triggers requests to the custom protocol
- Drop/close the WebView while async protocol responses may still be in-flight
- Crash occurs during WKWebView dealloc on macOS 11
Minimal reproduction (conceptual):
use wry::WebViewBuilder;
fn main() {
// ... create window ...
let webview = WebViewBuilder::new()
.with_custom_protocol("app".into(), |_webview_id, request, responder| {
// Simulate async response (e.g., via tokio::spawn)
std::thread::spawn(move || {
let response = http::Response::builder()
.status(200)
.body(std::borrow::Cow::from("hello".as_bytes()))
.unwrap();
responder.respond(response);
});
})
.with_url("app://localhost/index.html")
.build(&window)
.unwrap();
// Drop webview shortly after — on macOS 11, this triggers the crash
// in stop_task during WKWebView dealloc
drop(webview);
}
Note: The crash is timing-dependent and more likely when async responses are pending during webview destruction.
**Expected behavior**
Dropping the WebView should not crash. The stop_task callback should safely handle the case where WebKit passes freed pointers during
dealloc on macOS 11.
**Screenshots**
None
**Platform and Versions (please complete the following information):**
- OS: macOS 11.7.10 (Big Sur)
- Rustc: 1.88
- wry: 0.51 and 0.48
**Additional context**
Proposed fix:
_this: &ProtocolObject<dyn WKURLSchemeHandler>,
_sel: objc2::runtime::Sel,
_webview: *mut AnyObject, // raw pointer — no implicit objc_retain
_task: *mut AnyObject, // raw pointer — no implicit objc_retain
) {
// no-op: avoid accessing task/webview — macOS 11 may pass freed pointers here
}
2. Enforce explicit drop ordering in the async response handler:
// webview must drop before task: if webview drop triggers dealloc →
// stopAllTasksForPage → platformStopTask, the task must still be alive.
drop(webview);
drop(task);
result
Describe the bug
On macOS 11 (Big Sur), the app crashes with
EXC_BAD_ACCESS(objc_releaseon a freed pointer) during WKWebView deallocation whencustom URL scheme handlers are registered.
The root cause is that during WKWebView dealloc, WebKit internally calls
stopAllTasksForPage→platformStopTask, which invokesthe
stop_taskObjC callback. On macOS 11, the task and/or webview pointers passed to this callback may already be freed. Thecurrent
stop_taskfunction signature uses objc2 reference types (&WryWebView,&ProtocolObject<dyn WKURLSchemeTask>), whichtrigger implicit
objc_retainon function entry — callingobjc_retainon a freed pointer causes the crash.There is also a related race condition: when the async protocol response handler (running on a tokio worker thread) drops its
Retained<WryWebView>, this can trigger WKWebView dealloc on that thread. The dealloc path then callsstop_taskwith freed taskpointers, and the
Retained<ProtocolObject<dyn WKURLSchemeTask>>in the response closure may still be alive — creating ause-after-free if drop ordering is not enforced.
Steps To Reproduce
WebViewBuilder::with_custom_protocolMinimal reproduction (conceptual):