Skip to content

Commit 004065e

Browse files
authored
macOS: clear ivar earlier to prevent double free in release (#108)
1 parent f6e99e9 commit 004065e

2 files changed

Lines changed: 16 additions & 10 deletions

File tree

src/macos/view.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -178,12 +178,7 @@ extern "C" fn release(this: &mut Object, _sel: Sel) {
178178
let retain_count_after_build = WindowState::from_field(this).retain_count_after_build;
179179

180180
if retain_count <= retain_count_after_build {
181-
WindowState::from_field(this).stop();
182-
183-
this.set_ivar(BASEVIEW_STATE_IVAR, ::std::ptr::null() as *const c_void);
184-
185-
// Drop WindowState
186-
Box::from_raw(state_ptr as *mut WindowState);
181+
WindowState::stop_and_free(this);
187182
}
188183
}
189184
}

src/macos/window.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -359,15 +359,26 @@ impl WindowState {
359359
}
360360

361361
/// Call when freeing view
362-
pub(super) unsafe fn stop(&mut self) {
363-
if let Some(frame_timer) = self.frame_timer.take() {
362+
pub(super) unsafe fn stop_and_free(ns_view_obj: &mut Object) {
363+
let state_ptr: *mut c_void = *ns_view_obj.get_ivar(BASEVIEW_STATE_IVAR);
364+
365+
// Take back ownership of Box<WindowState> so that it gets dropped
366+
// when it goes out of scope
367+
let mut window_state = Box::from_raw(state_ptr as *mut WindowState);
368+
369+
if let Some(frame_timer) = window_state.frame_timer.take() {
364370
CFRunLoop::get_current().remove_timer(&frame_timer, kCFRunLoopDefaultMode);
365371
}
366372

367-
self.trigger_event(Event::Window(WindowEvent::WillClose));
373+
// Clear ivar before triggering WindowEvent::WillClose. Otherwise, if the
374+
// handler of the event causes another call to release, this function could be
375+
// called again, leading to a double free.
376+
ns_view_obj.set_ivar(BASEVIEW_STATE_IVAR, ::std::ptr::null() as *const c_void);
377+
378+
window_state.trigger_event(Event::Window(WindowEvent::WillClose));
368379

369380
// If in non-parented mode, we want to also quit the app altogether
370-
if let Some(app) = self.window.ns_app.take() {
381+
if let Some(app) = window_state.window.ns_app.take() {
371382
app.stop_(app);
372383
}
373384
}

0 commit comments

Comments
 (0)