Skip to content

Commit 8fbe4c2

Browse files
authored
Fix some panics compiling components and resources (#11798)
In working on bytecodealliance/wasm-tools#2335 I found that there's a few test cases in wasm-tools which Wasmtime was panicking to compile. The issues were all related to resource types and how information wasn't registered ahead of time before it was translated from wasmparser's representation to Wasmtime's representation. The high-level cause for this had to do with how component and instance types are handled, as opposed to concrete components or instances themselves. This was effectively a hole in Wasmtime's translation process for components which has never been filled out since the original implementation of resources. The reason that this never came up before is: * Most components don't currently import or export a component itself. * Most components don't currently import or export component or instance types (as opposed to values). One of these was required to trigger this issue. The solution implemented in this commit is to plumb the concept of an "abstract resource" which is part of a type but not actually ever used at runtime except for type equality during type reflection. This is expected to have little-to-no impact on real-world components given that these situations are rarely occurring.
1 parent c9bf144 commit 8fbe4c2

File tree

12 files changed

+256
-51
lines changed

12 files changed

+256
-51
lines changed

crates/cranelift/src/compiler/component.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,7 @@ impl<'a> TrampolineCompiler<'a> {
10581058
let should_run_destructor =
10591059
self.raise_if_negative_one(self.builder.func.dfg.inst_results(call)[0]);
10601060

1061-
let resource_ty = self.types[resource].ty;
1061+
let resource_ty = self.types[resource].unwrap_concrete_ty();
10621062
let resource_def = self
10631063
.component
10641064
.defined_resource_index(resource_ty)
@@ -1136,7 +1136,7 @@ impl<'a> TrampolineCompiler<'a> {
11361136
// the same component instance that defined the resource as the
11371137
// component is calling itself.
11381138
if let Some(def) = resource_def {
1139-
if self.types[resource].instance != def.instance {
1139+
if self.types[resource].unwrap_concrete_instance() != def.instance {
11401140
let flags = self.builder.ins().load(
11411141
ir::types::I32,
11421142
trusted,
@@ -1156,7 +1156,7 @@ impl<'a> TrampolineCompiler<'a> {
11561156
if has_destructor {
11571157
let rep = self.builder.ins().ushr_imm(should_run_destructor, 1);
11581158
let rep = self.builder.ins().ireduce(ir::types::I32, rep);
1159-
let index = self.types[resource].ty;
1159+
let index = self.types[resource].unwrap_concrete_ty();
11601160
// NB: despite the vmcontext storing nullable funcrefs for function
11611161
// pointers we know this is statically never null due to the
11621162
// `has_destructor` check above.

crates/environ/src/component/translate/inline.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1854,7 +1854,7 @@ impl<'a> ComponentItemDef<'a> {
18541854
// Once `path` has been iterated over it must be the case that the final
18551855
// item is a resource type, in which case a lookup can be performed.
18561856
match cur {
1857-
ComponentItemDef::Type(TypeDef::Resource(idx)) => types[idx].ty,
1857+
ComponentItemDef::Type(TypeDef::Resource(idx)) => types[idx].unwrap_concrete_ty(),
18581858
_ => unreachable!(),
18591859
}
18601860
}

crates/environ/src/component/types.rs

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,14 @@ indices! {
243243

244244
/// An index into `Component::options` at the end of compilation.
245245
pub struct OptionsIndex(u32);
246+
247+
/// An index that doesn't actually index into a list but instead represents
248+
/// a unique counter.
249+
///
250+
/// This is used for "abstract" resources which aren't actually instantiated
251+
/// in the component model. For example this represents a resource in a
252+
/// component or instance type, but not an actual concrete instance.
253+
pub struct AbstractResourceIndex(u32);
246254
}
247255

248256
// Reexport for convenience some core-wasm indices which are also used in the
@@ -1106,15 +1114,56 @@ pub struct TypeErrorContextTable {
11061114

11071115
/// Metadata about a resource table added to a component.
11081116
#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
1109-
pub struct TypeResourceTable {
1110-
/// The original resource that this table contains.
1117+
pub enum TypeResourceTable {
1118+
/// This resource is for an actual concrete resource which has runtime state
1119+
/// associated with it.
11111120
///
1112-
/// This is used when destroying resources within this table since this
1113-
/// original definition will know how to execute destructors.
1114-
pub ty: ResourceIndex,
1121+
/// This is used for any resource which might actually enter a component.
1122+
/// For example when a resource is either imported or defined in a component
1123+
/// it'll get this case.
1124+
Concrete {
1125+
/// The original resource that this table contains.
1126+
///
1127+
/// This is used when destroying resources within this table since this
1128+
/// original definition will know how to execute destructors.
1129+
ty: ResourceIndex,
1130+
1131+
/// The component instance that contains this resource table.
1132+
instance: RuntimeComponentInstanceIndex,
1133+
},
1134+
1135+
/// This table does not actually exist at runtime but instead represents
1136+
/// type information for an uninstantiable resource. This tracks, for
1137+
/// example, resources in component and instance types.
1138+
Abstract(AbstractResourceIndex),
1139+
}
11151140

1116-
/// The component instance that contains this resource table.
1117-
pub instance: RuntimeComponentInstanceIndex,
1141+
impl TypeResourceTable {
1142+
/// Asserts that this is `TypeResourceTable::Concrete` and returns the `ty`
1143+
/// field.
1144+
///
1145+
/// # Panics
1146+
///
1147+
/// Panics if this is `TypeResourceTable::Abstract`.
1148+
pub fn unwrap_concrete_ty(&self) -> ResourceIndex {
1149+
match self {
1150+
TypeResourceTable::Concrete { ty, .. } => *ty,
1151+
TypeResourceTable::Abstract(_) => panic!("not a concrete resource table"),
1152+
}
1153+
}
1154+
1155+
/// Asserts that this is `TypeResourceTable::Concrete` and returns the
1156+
/// `instance` field.
1157+
///
1158+
/// # Panics
1159+
///
1160+
/// Panics if this is `TypeResourceTable::Abstract`.
1161+
pub fn unwrap_concrete_instance(&self) -> RuntimeComponentInstanceIndex {
1162+
match self {
1163+
TypeResourceTable::Concrete { instance, .. } => *instance,
1164+
TypeResourceTable::Abstract(_) => panic!("not a concrete resource table"),
1165+
}
1166+
}
11181167
}
11191168

11201169
/// Shape of a "list" interface type.

crates/environ/src/component/types_builder.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ pub struct ComponentTypesBuilder {
6060
type_info: TypeInformationCache,
6161

6262
resources: ResourcesBuilder,
63+
64+
// Total number of abstract resources allocated.
65+
//
66+
// These are only allocated within component and instance types when
67+
// translating them.
68+
abstract_resources: u32,
6369
}
6470

6571
impl<T> Index<T> for ComponentTypesBuilder
@@ -111,6 +117,7 @@ impl ComponentTypesBuilder {
111117
component_types: ComponentTypes::default(),
112118
type_info: TypeInformationCache::default(),
113119
resources: ResourcesBuilder::default(),
120+
abstract_resources: 0,
114121
}
115122
}
116123

@@ -313,12 +320,14 @@ impl ComponentTypesBuilder {
313320
let ty = &types[id];
314321
let mut result = TypeComponent::default();
315322
for (name, ty) in ty.imports.iter() {
323+
self.register_abstract_component_entity_type(types, *ty);
316324
result.imports.insert(
317325
name.clone(),
318326
self.convert_component_entity_type(types, *ty)?,
319327
);
320328
}
321329
for (name, ty) in ty.exports.iter() {
330+
self.register_abstract_component_entity_type(types, *ty);
322331
result.exports.insert(
323332
name.clone(),
324333
self.convert_component_entity_type(types, *ty)?,
@@ -336,6 +345,7 @@ impl ComponentTypesBuilder {
336345
let ty = &types[id];
337346
let mut result = TypeComponentInstance::default();
338347
for (name, ty) in ty.exports.iter() {
348+
self.register_abstract_component_entity_type(types, *ty);
339349
result.exports.insert(
340350
name.clone(),
341351
self.convert_component_entity_type(types, *ty)?,
@@ -344,6 +354,23 @@ impl ComponentTypesBuilder {
344354
Ok(self.component_types.component_instances.push(result))
345355
}
346356

357+
fn register_abstract_component_entity_type(
358+
&mut self,
359+
types: TypesRef<'_>,
360+
ty: ComponentEntityType,
361+
) {
362+
let mut path = Vec::new();
363+
self.resources.register_abstract_component_entity_type(
364+
&types,
365+
ty,
366+
&mut path,
367+
&mut |_path| {
368+
self.abstract_resources += 1;
369+
AbstractResourceIndex::from_u32(self.abstract_resources)
370+
},
371+
);
372+
}
373+
347374
pub(crate) fn convert_module(
348375
&mut self,
349376
types: TypesRef<'_>,

crates/environ/src/component/types_builder/resources.rs

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@
6868
//! methods below.
6969
7070
use crate::component::{
71-
ComponentTypes, ResourceIndex, RuntimeComponentInstanceIndex, TypeResourceTable,
72-
TypeResourceTableIndex,
71+
AbstractResourceIndex, ComponentTypes, ResourceIndex, RuntimeComponentInstanceIndex,
72+
TypeResourceTable, TypeResourceTableIndex,
7373
};
7474
use crate::prelude::*;
7575
use std::collections::HashMap;
@@ -122,13 +122,27 @@ pub struct ResourcesBuilder {
122122
/// phase. This is used to record the actual underlying type of a resource
123123
/// and where it originally comes from. When a resource is later referred to
124124
/// then a table is injected to be referred to.
125-
resource_id_to_resource_index: HashMap<ResourceId, ResourceIndex>,
125+
resource_id_to_resource_index: HashMap<ResourceId, ResourceIndexKind>,
126126

127127
/// The current instance index that's being visited. This is updated as
128128
/// inliner frames are processed and components are instantiated.
129129
current_instance: Option<RuntimeComponentInstanceIndex>,
130130
}
131131

132+
/// Resources are considered either "concrete" or "abstract" depending on where
133+
/// the resource type is defined.
134+
///
135+
/// Resources defined in a component, or imported into a component, are
136+
/// considered "concrete" and may actually be instantiated/have a value at
137+
/// runtime. Resources defined in instance types or component types are
138+
/// considered "abstract" meaning that they won't ever actually exist at runtime
139+
/// so only an integer identifier is tracked for them.
140+
#[derive(Clone, Copy, Debug)]
141+
enum ResourceIndexKind {
142+
Concrete(ResourceIndex),
143+
Abstract(AbstractResourceIndex),
144+
}
145+
132146
impl ResourcesBuilder {
133147
/// Converts the `id` provided into a `TypeResourceTableIndex`.
134148
///
@@ -152,9 +166,14 @@ impl ResourcesBuilder {
152166
.resource_id_to_table_index
153167
.entry(id)
154168
.or_insert_with(|| {
155-
let ty = self.resource_id_to_resource_index[&id];
156-
let instance = self.current_instance.expect("current instance not set");
157-
types.push_resource_table(TypeResourceTable { ty, instance })
169+
let table_ty = match self.resource_id_to_resource_index[&id] {
170+
ResourceIndexKind::Concrete(ty) => {
171+
let instance = self.current_instance.expect("current instance not set");
172+
TypeResourceTable::Concrete { ty, instance }
173+
}
174+
ResourceIndexKind::Abstract(i) => TypeResourceTable::Abstract(i),
175+
};
176+
types.push_resource_table(table_ty)
158177
})
159178
}
160179

@@ -179,6 +198,32 @@ impl ResourcesBuilder {
179198
ty: ComponentEntityType,
180199
path: &mut Vec<&'a str>,
181200
register: &mut dyn FnMut(&[&'a str]) -> ResourceIndex,
201+
) {
202+
self.register_component_entity_type_(types, ty, path, &mut |path| {
203+
ResourceIndexKind::Concrete(register(path))
204+
})
205+
}
206+
207+
/// Same as [`Self::register_component_entity_type`], but for when an
208+
/// [`AbstractResourceIndex`] is created for all resources.
209+
pub fn register_abstract_component_entity_type<'a>(
210+
&mut self,
211+
types: &'a TypesRef<'_>,
212+
ty: ComponentEntityType,
213+
path: &mut Vec<&'a str>,
214+
register: &mut dyn FnMut(&[&'a str]) -> AbstractResourceIndex,
215+
) {
216+
self.register_component_entity_type_(types, ty, path, &mut |path| {
217+
ResourceIndexKind::Abstract(register(path))
218+
})
219+
}
220+
221+
fn register_component_entity_type_<'a>(
222+
&mut self,
223+
types: &'a TypesRef<'_>,
224+
ty: ComponentEntityType,
225+
path: &mut Vec<&'a str>,
226+
register: &mut dyn FnMut(&[&'a str]) -> ResourceIndexKind,
182227
) {
183228
match ty {
184229
// If `ty` is itself a type, and that's a resource type, then this
@@ -202,7 +247,7 @@ impl ResourcesBuilder {
202247
let ty = &types[id];
203248
for (name, ty) in ty.exports.iter() {
204249
path.push(name);
205-
self.register_component_entity_type(types, *ty, path, register);
250+
self.register_component_entity_type_(types, *ty, path, register);
206251
path.pop();
207252
}
208253
}
@@ -216,13 +261,14 @@ impl ResourcesBuilder {
216261
| ComponentEntityType::Value(_) => {}
217262
}
218263
}
219-
220264
/// Declares that the wasmparser `id`, which must point to a resource, is
221265
/// defined by the `ty` provided.
222266
///
223267
/// This is used when a local resource is defined within a component for example.
224268
pub fn register_resource(&mut self, id: ResourceId, ty: ResourceIndex) {
225-
let prev = self.resource_id_to_resource_index.insert(id, ty);
269+
let prev = self
270+
.resource_id_to_resource_index
271+
.insert(id, ResourceIndexKind::Concrete(ty));
226272
assert!(prev.is_none());
227273
}
228274

crates/wasmtime/src/runtime/component/matching.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ use alloc::sync::Arc;
1010
use wasmtime_environ::PrimaryMap;
1111
use wasmtime_environ::component::{
1212
ComponentTypes, NameMap, ResourceIndex, TypeComponentInstance, TypeDef, TypeFuncIndex,
13-
TypeFutureTableIndex, TypeModule, TypeResourceTableIndex, TypeStreamTableIndex,
13+
TypeFutureTableIndex, TypeModule, TypeResourceTable, TypeResourceTableIndex,
14+
TypeStreamTableIndex,
1415
};
1516

1617
pub struct TypeChecker<'a> {
@@ -59,7 +60,7 @@ impl TypeChecker<'_> {
5960
},
6061

6162
TypeDef::Resource(i) => {
62-
let i = self.types[i].ty;
63+
let i = self.types[i].unwrap_concrete_ty();
6364
let actual = match actual {
6465
Some(Definition::Resource(actual, _dtor)) => actual,
6566

@@ -193,11 +194,14 @@ impl<'a> InstanceType<'a> {
193194
}
194195

195196
pub fn resource_type(&self, index: TypeResourceTableIndex) -> ResourceType {
196-
let index = self.types[index].ty;
197-
self.resources
198-
.get(index)
199-
.copied()
200-
.unwrap_or_else(|| ResourceType::uninstantiated(&self.types, index))
197+
match self.types[index] {
198+
TypeResourceTable::Concrete { ty, .. } => self
199+
.resources
200+
.get(ty)
201+
.copied()
202+
.unwrap_or_else(|| ResourceType::uninstantiated(&self.types, ty)),
203+
TypeResourceTable::Abstract(ty) => ResourceType::abstract_(&self.types, ty),
204+
}
201205
}
202206

203207
pub fn future_type(&self, index: TypeFutureTableIndex) -> FutureType {

crates/wasmtime/src/runtime/component/resources.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use core::mem::MaybeUninit;
1515
use core::ptr::NonNull;
1616
use core::sync::atomic::{AtomicU32, Ordering::Relaxed};
1717
use wasmtime_environ::component::{
18-
CanonicalAbiInfo, ComponentTypes, DefinedResourceIndex, InterfaceType, ResourceIndex,
19-
TypeResourceTableIndex,
18+
AbstractResourceIndex, CanonicalAbiInfo, ComponentTypes, DefinedResourceIndex, InterfaceType,
19+
ResourceIndex, TypeResourceTableIndex,
2020
};
2121

2222
/// Representation of a resource type in the component model.
@@ -80,6 +80,15 @@ impl ResourceType {
8080
},
8181
}
8282
}
83+
84+
pub(crate) fn abstract_(types: &ComponentTypes, index: AbstractResourceIndex) -> ResourceType {
85+
ResourceType {
86+
kind: ResourceTypeKind::Abstract {
87+
component: types as *const _ as usize,
88+
index,
89+
},
90+
}
91+
}
8392
}
8493

8594
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
@@ -101,6 +110,13 @@ enum ResourceTypeKind {
101110
component: usize,
102111
index: ResourceIndex,
103112
},
113+
/// The type of this resource is considered "abstract" meaning that it
114+
/// doesn't actually correspond to anything at runtime but instead it just
115+
/// needs to be kept distinct from everything but itself.
116+
Abstract {
117+
component: usize,
118+
index: AbstractResourceIndex,
119+
},
104120
}
105121

106122
/// A host-defined resource in the component model.

0 commit comments

Comments
 (0)