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