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