Skip to content

Functions with 2+ levels of indirection leak memory on cancellation #1179

Open
@alexcrichton

Description

@alexcrichton

This WIT

package test:foo;

world the-world {
  import x: func(x: list<list<u32>>);
}

generates these async bindings currently

pub async fn x(x: &[_rt::Vec<u32>]) -> () {
    unsafe {
        #[repr(align(4))]
        struct RetArea([::core::mem::MaybeUninit<u8>; 8]);
        let mut ret_area = RetArea([::core::mem::MaybeUninit::uninit(); 8]);
        let ptr0 = ret_area.0.as_mut_ptr().cast::<u8>();
        let vec2 = x;
        let len2 = vec2.len();
        let layout2 = _rt::alloc::Layout::from_size_align_unchecked(vec2.len() * 8, 4);
        let result2 = if layout2.size() != 0 {
            let ptr = _rt::alloc::alloc(layout2).cast::<u8>();
            if ptr.is_null() {
                _rt::alloc::handle_alloc_error(layout2);
            }
            ptr
        } else {
            ::core::ptr::null_mut()
        };
        for (i, e) in vec2.into_iter().enumerate() {
            let base = result2.add(i * 8);
            {
                let vec1 = e;
                let ptr1 = vec1.as_ptr().cast::<u8>();
                let len1 = vec1.len();
                *base.add(4).cast::<usize>() = len1;
                *base.add(0).cast::<*mut u8>() = ptr1.cast_mut();
            }
        }
        *ptr0.add(4).cast::<usize>() = len2;
        *ptr0.add(0).cast::<*mut u8>() = result2;
        let ptr3 = core::ptr::null_mut::<u8>();
        #[cfg(target_arch = "wasm32")]
        #[link(wasm_import_module = "$root")]
        extern "C" {
            #[link_name = "[async]x"]
            fn wit_import4(_: *mut u8, _: *mut u8) -> i32;
        }
        #[cfg(not(target_arch = "wasm32"))]
        extern "C" fn wit_import4(_: *mut u8, _: *mut u8) -> i32 {
            unreachable!()
        }
        wit_bindgen::rt::async_support::await_result(wit_import4, ptr0, ptr3).await;
        if layout2.size() != 0 {
            _rt::alloc::dealloc(result2.cast(), layout2);
        }
    }
}

Notably the .await point of the import is a cancellation point at which the cleanup/dealloc code afterwards will not run. We probably need to switch those to being a destructor so they get cleaned up on drop.

Metadata

Metadata

Assignees

No one assigned

    Labels

    asyncRelated to async/streams in the component model.gen-rustRelated to bindings for Rust-compiled-to-WebAssembly

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions