gfx-rs / wgpu-native

Native WebGPU implementation based on wgpu-core
Apache License 2.0
851 stars 98 forks source link

SIGSEGV: Storing wgpu-native objects in global variables crashes on window resize #253

Closed heysokam closed 1 year ago

heysokam commented 1 year ago

Min repro: I created a small repository to reproduce the bug. https://github.com/heysokam/bug-wgpu-native The run.nims script is just for easily calling git and gcc. But the commands are there in clear text. I just don't want to deal with bash syntax. Install nim with choosenim if you want to use it. Might not be worth it if the commands are run once.

System:

The Bug: When I store the wgpu variables in global variables, the app runs but segfaults when the window is resized.

But when they are local to the main function, they behave normally.

Here is the backtrace by gdb:

Thread 1 "bug" received signal SIGSEGV, Segmentation fault.
0x000055555579f76e in wgpu_native::device::wgpuDeviceCreateSwapChain (device=0x55555786bd60, surface=0x5555574d21d0, descriptor=...)
    at src/device.rs:1046
1046        let config = follow_chain!(
(gdb) bt
#0  0x000055555579f76e in wgpu_native::device::wgpuDeviceCreateSwapChain (device=0x55555786bd60, surface=0x5555574d21d0,
    descriptor=...) at src/device.rs:1046
#1  0x000055555568b9ce in rUpdate ()
#2  0x000055555568bc74 in main ()
rajveermalviya commented 1 year ago

so the issue is with this pointer to stack allocated WGPUSwapChainDescriptorExtras struct, memory the pointer references goes out of scope here and starts pointing to a corrupted memory.

in wgpu-native side (after expanding follow_chain macro):

#[no_mangle]
pub unsafe extern "C" fn wgpuDeviceCreateSwapChain(
    device: native::WGPUDevice,
    surface: native::WGPUSurface,
    descriptor: Option<&native::WGPUSwapChainDescriptor>,
) -> native::WGPUSwapChain {
    let (device, _) = device.unwrap_handle();
    let (surface, context) = surface.unwrap_handle();
    let descriptor = descriptor.expect("invalid descriptor");
    let config = {
        let mut WGPUSType_SwapChainDescriptorExtras: Option<
            &native::WGPUSwapChainDescriptorExtras,
        > = None;
        let mut chain_opt: Option<&native::WGPUChainedStruct> = descriptor.nextInChain.as_ref();
        while let Some(next_in_chain) = chain_opt {
            match next_in_chain.sType {            // <------- segfaults here
                native::WGPUSType_SwapChainDescriptorExtras => {
                    let next_in_chain_ptr = next_in_chain as *const native::WGPUChainedStruct;
...

segfault happens when it starts reading the corrupted memory (next_in_chain.sType in above).

one solution in your code could be to use malloc to allocate the struct on heap. Other than that I don't think we can do much on wgpu-native side as we are receiving a corrupted pointer.

heysokam commented 1 year ago

Makes sense. I was wondering about the lifespan of the descriptor objects, and how to know how long they need to live, to be able to start organizing the structure of the renderer beyond the examples at some point. This helps me understand it a bit more. Ty for figuring it out, will keep this in mind moving forwards :writing_hand: