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