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