Skip to content

ResourceAny API ergonomics #12465

@crepererum

Description

@crepererum

Feature

ResourceAny is in the component model, both when the guest returns resources but also when borrow<...> resources are passed to the guest. The ResourceAny type Copyable handle and is usually passed by value. To destroy a resource resource_drop[_async] is called. Since the handle itself is Copy, you may still have copies of that handle around. Accidently reusing the handle results in:

host-owned resource is being used with the wrong type

as this test shows:

{
let mut store = Store::new(&engine, ());
let i = linker.instantiate(&mut store, &c)?;
let t_ctor = i.get_typed_func::<(u32,), (ResourceAny,)>(&mut store, "[constructor]t")?;
let u_ctor = i.get_typed_func::<(u32,), (ResourceAny,)>(&mut store, "[constructor]u")?;
let t_dtor = i.get_typed_func::<(ResourceAny,), ()>(&mut store, "drop-t")?;
// `t` is placed at host index 0
let (t,) = t_ctor.call(&mut store, (100,))?;
t_ctor.post_return(&mut store)?;
t_dtor.call(&mut store, (t,))?;
t_dtor.post_return(&mut store)?;
// `u` is also placed at host index 0 since `t` was deallocated
let (_u,) = u_ctor.call(&mut store, (100,))?;
u_ctor.post_return(&mut store)?;
// reuse of `t` should fail, despite it pointing to a valid resource
assert_eq!(
t_dtor.call(&mut store, (t,)).unwrap_err().to_string(),
"host-owned resource is being used with the wrong type"
);
}

Now there are two DX issues here:

  • the error message is somewhat misleading (it actually even happens when you allocate the same resource type into the slot, i.e. if the test would have used t1 & t1 instead of t and u)
  • since the handle doesn't have any form of refcounting, it is hard to enforce a "don't reuse a de-allocated resource" in a codebase esp. since ResourceAny is Copy and is often passed by value, hence you cannot really build a strong ref-count guard around it

Benefit

Improving the situation may prevent certain "used after free" bugs (even though they don't panic or SEGFAULT, they are still not great)

Implementation

Ideally ResourceAny wouldn't be Copy and would be passed by reference. Then resource_drop[_async] could consume the type. However there might be other API considerations that could prevent that.

Alternatives

If the type stays Copy, could we at least make the error message a bit clearer?

Metadata

Metadata

Assignees

No one assigned

    Labels

    wasm-proposal:component-modelIssues related to the WebAssembly Component Model proposalwasmtime:apiRelated to the API of the `wasmtime` crate itself

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions