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