Skip to content

Commit 0170dfe

Browse files
committed
[metal]: Fix double-free when re-using layer
1 parent e782be1 commit 0170dfe

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
};
@@ -80,7 +81,9 @@ impl super::Surface {
8081
delegate: Option<&HalManagedMetalLayerDelegate>,
8182
) -> Self {
8283
let layer = unsafe { Self::get_metal_layer(view, delegate) };
83-
// SAFETY: The layer is an initialized instance of `CAMetalLayer`.
84+
let layer = ManuallyDrop::new(layer);
85+
// SAFETY: The layer is an initialized instance of `CAMetalLayer`, and
86+
// we transfer the retain count to `MetalLayer` using `ManuallyDrop`.
8487
let layer = unsafe { metal::MetalLayer::from_ptr(layer.cast()) };
8588
let view: *mut Object = msg_send![view.as_ptr(), retain];
8689
let view = NonNull::new(view).expect("retain should return the same object");
@@ -107,7 +110,7 @@ impl super::Surface {
107110
pub(crate) unsafe fn get_metal_layer(
108111
view: NonNull<Object>,
109112
delegate: Option<&HalManagedMetalLayerDelegate>,
110-
) -> *mut Object {
113+
) -> StrongPtr {
111114
let is_main_thread: BOOL = msg_send![class!(NSThread), isMainThread];
112115
if is_main_thread == NO {
113116
panic!("get_metal_layer cannot be called in non-ui thread.");
@@ -141,7 +144,7 @@ impl super::Surface {
141144
// render directly into that; after all, the user passed a view
142145
// with an explicit Metal layer to us, so this is very likely what
143146
// they expect us to do.
144-
root_layer
147+
unsafe { StrongPtr::retain(root_layer) }
145148
} else {
146149
// The view does not have a `CAMetalLayer` as the root layer (this
147150
// is the default for most views).
@@ -246,7 +249,7 @@ impl super::Surface {
246249
if let Some(delegate) = delegate {
247250
let () = msg_send![new_layer, setDelegate: delegate.0];
248251
}
249-
new_layer
252+
unsafe { StrongPtr::new(new_layer) }
250253
}
251254
}
252255

wgpu-hal/src/vulkan/instance.rs

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

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

523526
let surface = {
524527
let metal_loader =
525528
ext::metal_surface::Instance::new(&self.shared.entry, &self.shared.raw);
526529
let vk_info = vk::MetalSurfaceCreateInfoEXT::default()
527530
.flags(vk::MetalSurfaceCreateFlagsEXT::empty())
528-
.layer(layer.cast());
531+
.layer(layer_ptr);
529532

530533
unsafe { metal_loader.create_metal_surface(&vk_info, None).unwrap() }
531534
};

0 commit comments

Comments
 (0)