-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Description
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:
wasmtime/tests/all/component_model/resources.rs
Lines 181 to 203 in 23c0f09
| { | |
| 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&t1instead oftandu) - 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
ResourceAnyisCopyand 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?