1 use crate::component::matching::InstanceType;
2 use crate::component::resources::{HostResourceData, HostResourceIndex, HostResourceTables};
3 use crate::component::{Instance, ResourceType};
4 use crate::prelude::*;
5 use crate::runtime::vm::component::{
6     CallContexts, ComponentInstance, HandleTable, InstanceFlags, ResourceTables,
7 };
8 use crate::runtime::vm::{VMFuncRef, VMMemoryDefinition};
9 use crate::store::{StoreId, StoreOpaque};
10 use crate::{FuncType, StoreContextMut};
11 use alloc::sync::Arc;
12 use core::pin::Pin;
13 use core::ptr::NonNull;
14 use wasmtime_environ::component::{
15     CanonicalOptions, CanonicalOptionsDataModel, ComponentTypes, OptionsIndex, StringEncoding,
16     TypeResourceTableIndex,
17 };
18 
19 /// Runtime representation of canonical ABI options in the component model.
20 ///
21 /// This structure packages up the runtime representation of each option from
22 /// memories to reallocs to string encodings. Note that this is a "standalone"
23 /// structure which has raw pointers internally. This allows it to be created
24 /// out of thin air for a host function import, for example. The `store_id`
25 /// field, however, is what is used to pair this set of options with a store
26 /// reference to actually use the pointers.
27 #[derive(Copy, Clone)]
28 pub struct Options {
29     /// The store from which this options originated.
30     store_id: StoreId,
31 
32     /// An optional pointer for the memory that this set of options is referring
33     /// to. This option is not required to be specified in the canonical ABI
34     /// hence the `Option`.
35     ///
36     /// Note that this pointer cannot be safely dereferenced unless a store,
37     /// verified with `self.store_id`, has the appropriate borrow available.
38     memory: Option<NonNull<VMMemoryDefinition>>,
39 
40     /// Similar to `memory` but corresponds to the `canonical_abi_realloc`
41     /// function.
42     ///
43     /// Safely using this pointer has the same restrictions as `memory` above.
44     realloc: Option<NonNull<VMFuncRef>>,
45 
46     /// The encoding used for strings, if found.
47     ///
48     /// This defaults to utf-8 but can be changed if necessary.
49     string_encoding: StringEncoding,
50 
51     /// Whether or not this the async option was set when lowering.
52     async_: bool,
53 
54     #[cfg(feature = "component-model-async")]
55     callback: Option<NonNull<VMFuncRef>>,
56 }
57 
58 // The `Options` structure stores raw pointers but they're never used unless a
59 // `Store` is available so this should be threadsafe and largely inherit the
60 // thread-safety story of `Store<T>` itself.
61 unsafe impl Send for Options {}
62 unsafe impl Sync for Options {}
63 
64 impl Options {
65     // FIXME(#4311): prevent a ctor where the memory is memory64
66 
67     /// Creates a new [`Options`] from the given [`OptionsIndex`] belonging to
68     /// the specified [`Instance`]
69     ///
70     /// # Panics
71     ///
72     /// Panics if `instance` is not owned by `store` or if `index` is not valid
73     /// for `instance`'s component.
74     pub fn new_index(store: &StoreOpaque, instance: Instance, index: OptionsIndex) -> Options {
75         let instance = instance.id().get(store);
76         let CanonicalOptions {
77             string_encoding,
78             async_,
79             callback,
80             ref data_model,
81             ..
82         } = instance.component().env_component().options[index];
83         let (memory, realloc) = match data_model {
84             CanonicalOptionsDataModel::Gc { .. } => (None, None),
85             CanonicalOptionsDataModel::LinearMemory(o) => (o.memory, o.realloc),
86         };
87         let memory = memory.map(|i| NonNull::new(instance.runtime_memory(i)).unwrap());
88         let realloc = realloc.map(|i| instance.runtime_realloc(i));
89         let callback = callback.map(|i| instance.runtime_callback(i));
90         let _ = callback;
91 
92         Options {
93             store_id: store.id(),
94             memory,
95             realloc,
96             string_encoding,
97             async_,
98             #[cfg(feature = "component-model-async")]
99             callback,
100         }
101     }
102 
103     fn realloc<'a, T>(
104         &self,
105         store: &'a mut StoreContextMut<'_, T>,
106         realloc_ty: &FuncType,
107         old: usize,
108         old_size: usize,
109         old_align: u32,
110         new_size: usize,
111     ) -> Result<(&'a mut [u8], usize)> {
112         self.store_id.assert_belongs_to(store.0.id());
113 
114         let realloc = self.realloc.unwrap();
115 
116         let params = (
117             u32::try_from(old)?,
118             u32::try_from(old_size)?,
119             old_align,
120             u32::try_from(new_size)?,
121         );
122 
123         type ReallocFunc = crate::TypedFunc<(u32, u32, u32, u32), u32>;
124 
125         // Invoke the wasm malloc function using its raw and statically known
126         // signature.
127         let result = unsafe { ReallocFunc::call_raw(store, realloc_ty, realloc, params)? };
128 
129         if result % old_align != 0 {
130             bail!("realloc return: result not aligned");
131         }
132         let result = usize::try_from(result)?;
133 
134         let memory = self.memory_mut(store.0);
135 
136         let result_slice = match memory.get_mut(result..).and_then(|s| s.get_mut(..new_size)) {
137             Some(end) => end,
138             None => bail!("realloc return: beyond end of memory"),
139         };
140 
141         Ok((result_slice, result))
142     }
143 
144     /// Asserts that this function has an associated memory attached to it and
145     /// then returns the slice of memory tied to the lifetime of the provided
146     /// store.
147     pub fn memory<'a>(&self, store: &'a StoreOpaque) -> &'a [u8] {
148         self.store_id.assert_belongs_to(store.id());
149 
150         // The unsafety here is intended to be encapsulated by the two
151         // preceding assertions. Namely we assert that the `store` is the same
152         // as the original store of this `Options`, meaning that we safely have
153         // either a shared reference or a mutable reference (as below) which
154         // means it's safe to view the memory (aka it's not a different store
155         // where our original store is on some other thread or something like
156         // that).
157         //
158         // Additionally the memory itself is asserted to be present as memory
159         // is an optional configuration in canonical ABI options.
160         unsafe {
161             let memory = self.memory.unwrap().as_ref();
162             core::slice::from_raw_parts(memory.base.as_ptr(), memory.current_length())
163         }
164     }
165 
166     /// Same as above, just `_mut`
167     pub fn memory_mut<'a>(&self, store: &'a mut StoreOpaque) -> &'a mut [u8] {
168         self.store_id.assert_belongs_to(store.id());
169 
170         // See comments in `memory` about the unsafety
171         unsafe {
172             let memory = self.memory.unwrap().as_ref();
173             core::slice::from_raw_parts_mut(memory.base.as_ptr(), memory.current_length())
174         }
175     }
176 
177     /// Returns the underlying encoding used for strings in this
178     /// lifting/lowering.
179     pub fn string_encoding(&self) -> StringEncoding {
180         self.string_encoding
181     }
182 
183     /// Returns the id of the store that this `Options` is connected to.
184     pub fn store_id(&self) -> StoreId {
185         self.store_id
186     }
187 
188     /// Returns whether this lifting or lowering uses the async ABI.
189     pub fn async_(&self) -> bool {
190         self.async_
191     }
192 
193     #[cfg(feature = "component-model-async")]
194     pub(crate) fn callback(&self) -> Option<NonNull<VMFuncRef>> {
195         self.callback
196     }
197 
198     #[cfg(feature = "component-model-async")]
199     pub(crate) fn memory_raw(&self) -> Option<NonNull<VMMemoryDefinition>> {
200         self.memory
201     }
202 }
203 
204 /// A helper structure which is a "package" of the context used during lowering
205 /// values into a component (or storing them into memory).
206 ///
207 /// This type is used by the `Lower` trait extensively and contains any
208 /// contextual information necessary related to the context in which the
209 /// lowering is happening.
210 #[doc(hidden)]
211 pub struct LowerContext<'a, T: 'static> {
212     /// Lowering may involve invoking memory allocation functions so part of the
213     /// context here is carrying access to the entire store that wasm is
214     /// executing within. This store serves as proof-of-ability to actually
215     /// execute wasm safely.
216     pub store: StoreContextMut<'a, T>,
217 
218     /// Lowering always happens into a function that's been `canon lift`'d or
219     /// `canon lower`'d, both of which specify a set of options for the
220     /// canonical ABI. For example details like string encoding are contained
221     /// here along with which memory pointers are relative to or what the memory
222     /// allocation function is.
223     pub options: &'a Options,
224 
225     /// Lowering happens within the context of a component instance and this
226     /// field stores the type information of that component instance. This is
227     /// used for type lookups and general type queries during the
228     /// lifting/lowering process.
229     pub types: &'a ComponentTypes,
230 
231     /// Index of the component instance that's being lowered into.
232     instance: Instance,
233 
234     /// Whether to allow `options.realloc` to be used when lowering.
235     allow_realloc: bool,
236 }
237 
238 #[doc(hidden)]
239 impl<'a, T: 'static> LowerContext<'a, T> {
240     /// Creates a new lowering context from the specified parameters.
241     pub fn new(
242         store: StoreContextMut<'a, T>,
243         options: &'a Options,
244         types: &'a ComponentTypes,
245         instance: Instance,
246     ) -> LowerContext<'a, T> {
247         #[cfg(all(debug_assertions, feature = "component-model-async"))]
248         if store.engine().config().async_support {
249             // Assert that we're running on a fiber, which is necessary in
250             // case we call the guest's realloc function.
251             store.0.with_blocking(|_, _| {});
252         }
253         LowerContext {
254             store,
255             options,
256             types,
257             instance,
258             allow_realloc: true,
259         }
260     }
261 
262     /// Like `new`, except disallows use of `options.realloc`.
263     ///
264     /// The returned object will panic if its `realloc` method is called.
265     ///
266     /// This is meant for use when lowering "flat" values (i.e. values which
267     /// require no allocations) into already-allocated memory or into stack
268     /// slots, in which case the lowering may safely be done outside of a fiber
269     /// since there is no need to make any guest calls.
270     #[cfg(feature = "component-model-async")]
271     pub(crate) fn new_without_realloc(
272         store: StoreContextMut<'a, T>,
273         options: &'a Options,
274         types: &'a ComponentTypes,
275         instance: Instance,
276     ) -> LowerContext<'a, T> {
277         LowerContext {
278             store,
279             options,
280             types,
281             instance,
282             allow_realloc: false,
283         }
284     }
285 
286     /// Returns the `&ComponentInstance` that's being lowered into.
287     pub fn instance(&self) -> &ComponentInstance {
288         self.instance.id().get(self.store.0)
289     }
290 
291     /// Returns the `&mut ComponentInstance` that's being lowered into.
292     pub fn instance_mut(&mut self) -> Pin<&mut ComponentInstance> {
293         self.instance.id().get_mut(self.store.0)
294     }
295 
296     /// Returns a view into memory as a mutable slice of bytes.
297     ///
298     /// # Panics
299     ///
300     /// This will panic if memory has not been configured for this lowering
301     /// (e.g. it wasn't present during the specification of canonical options).
302     pub fn as_slice_mut(&mut self) -> &mut [u8] {
303         self.options.memory_mut(self.store.0)
304     }
305 
306     /// Invokes the memory allocation function (which is style after `realloc`)
307     /// with the specified parameters.
308     ///
309     /// # Panics
310     ///
311     /// This will panic if realloc hasn't been configured for this lowering via
312     /// its canonical options.
313     pub fn realloc(
314         &mut self,
315         old: usize,
316         old_size: usize,
317         old_align: u32,
318         new_size: usize,
319     ) -> Result<usize> {
320         assert!(self.allow_realloc);
321 
322         let realloc_func_ty = Arc::clone(self.instance().component().realloc_func_ty());
323         self.options
324             .realloc(
325                 &mut self.store,
326                 &realloc_func_ty,
327                 old,
328                 old_size,
329                 old_align,
330                 new_size,
331             )
332             .map(|(_, ptr)| ptr)
333     }
334 
335     /// Returns a fixed mutable slice of memory `N` bytes large starting at
336     /// offset `N`, panicking on out-of-bounds.
337     ///
338     /// It should be previously verified that `offset` is in-bounds via
339     /// bounds-checks.
340     ///
341     /// # Panics
342     ///
343     /// This will panic if memory has not been configured for this lowering
344     /// (e.g. it wasn't present during the specification of canonical options).
345     pub fn get<const N: usize>(&mut self, offset: usize) -> &mut [u8; N] {
346         // FIXME: this bounds check shouldn't actually be necessary, all
347         // callers of `ComponentType::store` have already performed a bounds
348         // check so we're guaranteed that `offset..offset+N` is in-bounds. That
349         // being said we at least should do bounds checks in debug mode and
350         // it's not clear to me how to easily structure this so that it's
351         // "statically obvious" the bounds check isn't necessary.
352         //
353         // For now I figure we can leave in this bounds check and if it becomes
354         // an issue we can optimize further later, probably with judicious use
355         // of `unsafe`.
356         self.as_slice_mut()[offset..].first_chunk_mut().unwrap()
357     }
358 
359     /// Lowers an `own` resource into the guest, converting the `rep` specified
360     /// into a guest-local index.
361     ///
362     /// The `ty` provided is which table to put this into.
363     pub fn guest_resource_lower_own(
364         &mut self,
365         ty: TypeResourceTableIndex,
366         rep: u32,
367     ) -> Result<u32> {
368         self.resource_tables().guest_resource_lower_own(rep, ty)
369     }
370 
371     /// Lowers a `borrow` resource into the guest, converting the `rep` to a
372     /// guest-local index in the `ty` table specified.
373     pub fn guest_resource_lower_borrow(
374         &mut self,
375         ty: TypeResourceTableIndex,
376         rep: u32,
377     ) -> Result<u32> {
378         // Implement `lower_borrow`'s special case here where if a borrow is
379         // inserted into a table owned by the instance which implemented the
380         // original resource then no borrow tracking is employed and instead the
381         // `rep` is returned "raw".
382         //
383         // This check is performed by comparing the owning instance of `ty`
384         // against the owning instance of the resource that `ty` is working
385         // with.
386         if self.instance().resource_owned_by_own_instance(ty) {
387             return Ok(rep);
388         }
389         self.resource_tables().guest_resource_lower_borrow(rep, ty)
390     }
391 
392     /// Lifts a host-owned `own` resource at the `idx` specified into the
393     /// representation of that resource.
394     pub fn host_resource_lift_own(&mut self, idx: HostResourceIndex) -> Result<u32> {
395         self.resource_tables().host_resource_lift_own(idx)
396     }
397 
398     /// Lifts a host-owned `borrow` resource at the `idx` specified into the
399     /// representation of that resource.
400     pub fn host_resource_lift_borrow(&mut self, idx: HostResourceIndex) -> Result<u32> {
401         self.resource_tables().host_resource_lift_borrow(idx)
402     }
403 
404     /// Lowers a resource into the host-owned table, returning the index it was
405     /// inserted at.
406     ///
407     /// Note that this is a special case for `Resource<T>`. Most of the time a
408     /// host value shouldn't be lowered with a lowering context.
409     pub fn host_resource_lower_own(
410         &mut self,
411         rep: u32,
412         dtor: Option<NonNull<VMFuncRef>>,
413         flags: Option<InstanceFlags>,
414     ) -> Result<HostResourceIndex> {
415         self.resource_tables()
416             .host_resource_lower_own(rep, dtor, flags)
417     }
418 
419     /// Returns the underlying resource type for the `ty` table specified.
420     pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType {
421         self.instance_type().resource_type(ty)
422     }
423 
424     /// Returns the instance type information corresponding to the instance that
425     /// this context is lowering into.
426     pub fn instance_type(&self) -> InstanceType<'_> {
427         InstanceType::new(self.instance())
428     }
429 
430     fn resource_tables(&mut self) -> HostResourceTables<'_> {
431         let (calls, host_table, host_resource_data, instance) = self
432             .store
433             .0
434             .component_resource_state_with_instance(self.instance);
435         HostResourceTables::from_parts(
436             ResourceTables {
437                 host_table: Some(host_table),
438                 calls,
439                 guest: Some(instance.guest_tables()),
440             },
441             host_resource_data,
442         )
443     }
444 
445     /// See [`HostResourceTables::enter_call`].
446     #[inline]
447     pub fn enter_call(&mut self) {
448         self.resource_tables().enter_call()
449     }
450 
451     /// See [`HostResourceTables::exit_call`].
452     #[inline]
453     pub fn exit_call(&mut self) -> Result<()> {
454         self.resource_tables().exit_call()
455     }
456 }
457 
458 /// Contextual information used when lifting a type from a component into the
459 /// host.
460 ///
461 /// This structure is the analogue of `LowerContext` except used during lifting
462 /// operations (or loading from memory).
463 #[doc(hidden)]
464 pub struct LiftContext<'a> {
465     /// Like lowering, lifting always has options configured.
466     pub options: &'a Options,
467 
468     /// Instance type information, like with lowering.
469     pub types: &'a Arc<ComponentTypes>,
470 
471     memory: Option<&'a [u8]>,
472 
473     instance: Pin<&'a mut ComponentInstance>,
474     instance_handle: Instance,
475 
476     host_table: &'a mut HandleTable,
477     host_resource_data: &'a mut HostResourceData,
478 
479     calls: &'a mut CallContexts,
480 }
481 
482 #[doc(hidden)]
483 impl<'a> LiftContext<'a> {
484     /// Creates a new lifting context given the provided context.
485     #[inline]
486     pub fn new(
487         store: &'a mut StoreOpaque,
488         options: &'a Options,
489         instance_handle: Instance,
490     ) -> LiftContext<'a> {
491         // From `&mut StoreOpaque` provided the goal here is to project out
492         // three different disjoint fields owned by the store: memory,
493         // `CallContexts`, and `HandleTable`. There's no native API for that
494         // so it's hacked around a bit. This unsafe pointer cast could be fixed
495         // with more methods in more places, but it doesn't seem worth doing it
496         // at this time.
497         let memory = options
498             .memory
499             .map(|_| options.memory(unsafe { &*(store as *const StoreOpaque) }));
500         let (calls, host_table, host_resource_data, instance) =
501             store.component_resource_state_with_instance(instance_handle);
502         let (component, instance) = instance.component_and_self();
503 
504         LiftContext {
505             memory,
506             options,
507             types: component.types(),
508             instance,
509             instance_handle,
510             calls,
511             host_table,
512             host_resource_data,
513         }
514     }
515 
516     /// Returns the entire contents of linear memory for this set of lifting
517     /// options.
518     ///
519     /// # Panics
520     ///
521     /// This will panic if memory has not been configured for this lifting
522     /// operation.
523     pub fn memory(&self) -> &'a [u8] {
524         self.memory.unwrap()
525     }
526 
527     /// Returns an identifier for the store from which this `LiftContext` was
528     /// created.
529     pub fn store_id(&self) -> StoreId {
530         self.options.store_id
531     }
532 
533     /// Returns the component instance that is being lifted from.
534     pub fn instance_mut(&mut self) -> Pin<&mut ComponentInstance> {
535         self.instance.as_mut()
536     }
537     /// Returns the component instance that is being lifted from.
538     pub fn instance_handle(&self) -> Instance {
539         self.instance_handle
540     }
541 
542     /// Lifts an `own` resource from the guest at the `idx` specified into its
543     /// representation.
544     ///
545     /// Additionally returns a destructor/instance flags to go along with the
546     /// representation so the host knows how to destroy this resource.
547     pub fn guest_resource_lift_own(
548         &mut self,
549         ty: TypeResourceTableIndex,
550         idx: u32,
551     ) -> Result<(u32, Option<NonNull<VMFuncRef>>, Option<InstanceFlags>)> {
552         let idx = self.resource_tables().guest_resource_lift_own(idx, ty)?;
553         let (dtor, flags) = self.instance.dtor_and_flags(ty);
554         Ok((idx, dtor, flags))
555     }
556 
557     /// Lifts a `borrow` resource from the guest at the `idx` specified.
558     pub fn guest_resource_lift_borrow(
559         &mut self,
560         ty: TypeResourceTableIndex,
561         idx: u32,
562     ) -> Result<u32> {
563         self.resource_tables().guest_resource_lift_borrow(idx, ty)
564     }
565 
566     /// Lowers a resource into the host-owned table, returning the index it was
567     /// inserted at.
568     pub fn host_resource_lower_own(
569         &mut self,
570         rep: u32,
571         dtor: Option<NonNull<VMFuncRef>>,
572         flags: Option<InstanceFlags>,
573     ) -> Result<HostResourceIndex> {
574         self.resource_tables()
575             .host_resource_lower_own(rep, dtor, flags)
576     }
577 
578     /// Lowers a resource into the host-owned table, returning the index it was
579     /// inserted at.
580     pub fn host_resource_lower_borrow(&mut self, rep: u32) -> Result<HostResourceIndex> {
581         self.resource_tables().host_resource_lower_borrow(rep)
582     }
583 
584     /// Returns the underlying type of the resource table specified by `ty`.
585     pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType {
586         self.instance_type().resource_type(ty)
587     }
588 
589     /// Returns instance type information for the component instance that is
590     /// being lifted from.
591     pub fn instance_type(&self) -> InstanceType<'_> {
592         InstanceType::new(&self.instance)
593     }
594 
595     fn resource_tables(&mut self) -> HostResourceTables<'_> {
596         HostResourceTables::from_parts(
597             ResourceTables {
598                 host_table: Some(self.host_table),
599                 calls: self.calls,
600                 // Note that the unsafety here should be valid given the contract of
601                 // `LiftContext::new`.
602                 guest: Some(self.instance.as_mut().guest_tables()),
603             },
604             self.host_resource_data,
605         )
606     }
607 
608     /// See [`HostResourceTables::enter_call`].
609     #[inline]
610     pub fn enter_call(&mut self) {
611         self.resource_tables().enter_call()
612     }
613 
614     /// See [`HostResourceTables::exit_call`].
615     #[inline]
616     pub fn exit_call(&mut self) -> Result<()> {
617         self.resource_tables().exit_call()
618     }
619 }
620