Skip to content

Commit 5f53a39

Browse files
committed
[metal]: Fix double-free when re-using layer
1 parent 0dd6fa1 commit 5f53a39

File tree

2 files changed

+12
-6
lines changed

2 files changed

+12
-6
lines changed

wgpu-hal/src/metal/surface.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![allow(clippy::let_unit_value)] // `let () =` being used to constrain result type
22

33
use std::ffi::c_uint;
4+
use std::mem::ManuallyDrop;
45
use std::ptr::NonNull;
56
use std::sync::Once;
67
use std::thread;
@@ -14,7 +15,7 @@ use objc::{
1415
class,
1516
declare::ClassDecl,
1617
msg_send,
17-
rc::autoreleasepool,
18+
rc::{autoreleasepool, StrongPtr},
1819
runtime::{Class, Object, Sel, BOOL, NO, YES},
1920
sel, sel_impl,
2021
};
@@ -81,7 +82,9 @@ impl super::Surface {
8182
delegate: Option<&HalManagedMetalLayerDelegate>,
8283
) -> Self {
8384
let layer = unsafe { Self::get_metal_layer(view, delegate) };
84-
// SAFETY: The layer is an initialized instance of `CAMetalLayer`.
85+
let layer = ManuallyDrop::new(layer);
86+
// SAFETY: The layer is an initialized instance of `CAMetalLayer`, and
87+
// we transfer the retain count to `MetalLayer` using `ManuallyDrop`.
8588
let layer = unsafe { metal::MetalLayer::from_ptr(layer.cast()) };
8689
let view: *mut Object = msg_send![view.as_ptr(), retain];
8790
let view = NonNull::new(view).expect("retain should return the same object");
@@ -108,7 +111,7 @@ impl super::Surface {
108111
pub(crate) unsafe fn get_metal_layer(
109112
view: NonNull<Object>,
110113
delegate: Option<&HalManagedMetalLayerDelegate>,
111-
) -> *mut Object {
114+
) -> StrongPtr {
112115
let is_main_thread: BOOL = msg_send![class!(NSThread), isMainThread];
113116
if is_main_thread == NO {
114117
panic!("get_metal_layer cannot be called in non-ui thread.");
@@ -142,7 +145,7 @@ impl super::Surface {
142145
// render directly into that; after all, the user passed a view
143146
// with an explicit Metal layer to us, so this is very likely what
144147
// they expect us to do.
145-
root_layer
148+
unsafe { StrongPtr::retain(root_layer) }
146149
} else {
147150
// The view does not have a `CAMetalLayer` as the root layer (this
148151
// is the default for most views).
@@ -247,7 +250,7 @@ impl super::Surface {
247250
if let Some(delegate) = delegate {
248251
let () = msg_send![new_layer, setDelegate: delegate.0];
249252
}
250-
new_layer
253+
unsafe { StrongPtr::new(new_layer) }
251254
}
252255
}
253256

wgpu-hal/src/vulkan/instance.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,13 +515,16 @@ impl super::Instance {
515515
}
516516

517517
let layer = unsafe { crate::metal::Surface::get_metal_layer(view.cast(), None) };
518+
// NOTE: The layer is retained by Vulkan's `vkCreateMetalSurfaceEXT`,
519+
// so no need to retain it beyond the scope of this function.
520+
let layer_ptr = (*layer).cast();
518521

519522
let surface = {
520523
let metal_loader =
521524
ext::metal_surface::Instance::new(&self.shared.entry, &self.shared.raw);
522525
let vk_info = vk::MetalSurfaceCreateInfoEXT::default()
523526
.flags(vk::MetalSurfaceCreateFlagsEXT::empty())
524-
.layer(layer.cast());
527+
.layer(layer_ptr);
525528

526529
unsafe { metal_loader.create_metal_surface(&vk_info, None).unwrap() }
527530
};

0 commit comments

Comments
 (0)