1 use crate::component::concurrent;
2 #[cfg(feature = "component-model-async")]
3 use crate::component::concurrent::{Accessor, Status};
4 use crate::component::func::{LiftContext, LowerContext};
5 use crate::component::matching::InstanceType;
6 use crate::component::storage::slice_to_storage_mut;
7 use crate::component::types::ComponentFunc;
8 use crate::component::{ComponentNamedList, ComponentType, Instance, Lift, Lower, Val};
9 use crate::prelude::*;
10 use crate::runtime::vm::component::{
11     ComponentInstance, VMComponentContext, VMLowering, VMLoweringCallee,
12 };
13 use crate::runtime::vm::{SendSyncPtr, VMOpaqueContext, VMStore};
14 use crate::{AsContextMut, CallHook, StoreContextMut, ValRaw};
15 use alloc::sync::Arc;
16 use core::any::Any;
17 use core::future::Future;
18 use core::mem::{self, MaybeUninit};
19 use core::pin::Pin;
20 use core::ptr::NonNull;
21 use wasmtime_environ::component::{
22     CanonicalAbiInfo, ComponentTypes, InterfaceType, MAX_FLAT_ASYNC_PARAMS, MAX_FLAT_PARAMS,
23     MAX_FLAT_RESULTS, OptionsIndex, TypeFuncIndex, TypeTuple,
24 };
25 
26 pub struct HostFunc {
27     entrypoint: VMLoweringCallee,
28     typecheck: Box<dyn (Fn(TypeFuncIndex, &InstanceType<'_>) -> Result<()>) + Send + Sync>,
29     func: Box<dyn Any + Send + Sync>,
30 }
31 
32 impl core::fmt::Debug for HostFunc {
33     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
34         f.debug_struct("HostFunc").finish_non_exhaustive()
35     }
36 }
37 
38 enum HostResult<T> {
39     Done(Result<T>),
40     #[cfg(feature = "component-model-async")]
41     Future(Pin<Box<dyn Future<Output = Result<T>> + Send>>),
42 }
43 
44 trait FunctionStyle {
45     const ASYNC: bool;
46 }
47 
48 struct SyncStyle;
49 
50 impl FunctionStyle for SyncStyle {
51     const ASYNC: bool = false;
52 }
53 
54 #[cfg(feature = "component-model-async")]
55 struct AsyncStyle;
56 
57 #[cfg(feature = "component-model-async")]
58 impl FunctionStyle for AsyncStyle {
59     const ASYNC: bool = true;
60 }
61 
62 impl HostFunc {
63     fn from_canonical<T, F, P, R, S>(func: F) -> Arc<HostFunc>
64     where
65         F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R> + Send + Sync + 'static,
66         P: ComponentNamedList + Lift + 'static,
67         R: ComponentNamedList + Lower + 'static,
68         T: 'static,
69         S: FunctionStyle + 'static,
70     {
71         let entrypoint = Self::entrypoint::<T, F, P, R, S>;
72         Arc::new(HostFunc {
73             entrypoint,
74             typecheck: Box::new(typecheck::<P, R, S>),
75             func: Box::new(func),
76         })
77     }
78 
79     pub(crate) fn from_closure<T, F, P, R>(func: F) -> Arc<HostFunc>
80     where
81         T: 'static,
82         F: Fn(StoreContextMut<T>, P) -> Result<R> + Send + Sync + 'static,
83         P: ComponentNamedList + Lift + 'static,
84         R: ComponentNamedList + Lower + 'static,
85     {
86         Self::from_canonical::<T, _, _, _, SyncStyle>(move |store, params| {
87             HostResult::Done(func(store, params))
88         })
89     }
90 
91     #[cfg(feature = "component-model-async")]
92     pub(crate) fn from_concurrent<T, F, P, R>(func: F) -> Arc<HostFunc>
93     where
94         T: 'static,
95         F: Fn(&Accessor<T>, P) -> Pin<Box<dyn Future<Output = Result<R>> + Send + '_>>
96             + Send
97             + Sync
98             + 'static,
99         P: ComponentNamedList + Lift + 'static,
100         R: ComponentNamedList + Lower + 'static,
101     {
102         let func = Arc::new(func);
103         Self::from_canonical::<T, _, _, _, AsyncStyle>(move |store, params| {
104             let func = func.clone();
105             HostResult::Future(Box::pin(
106                 store.wrap_call(move |accessor| func(accessor, params)),
107             ))
108         })
109     }
110 
111     extern "C" fn entrypoint<T, F, P, R, S>(
112         cx: NonNull<VMOpaqueContext>,
113         data: NonNull<u8>,
114         ty: u32,
115         options: u32,
116         storage: NonNull<MaybeUninit<ValRaw>>,
117         storage_len: usize,
118     ) -> bool
119     where
120         F: Fn(StoreContextMut<'_, T>, P) -> HostResult<R> + Send + Sync + 'static,
121         P: ComponentNamedList + Lift,
122         R: ComponentNamedList + Lower + 'static,
123         T: 'static,
124         S: FunctionStyle,
125     {
126         let data = SendSyncPtr::new(NonNull::new(data.as_ptr() as *mut F).unwrap());
127         unsafe {
128             call_host_and_handle_result::<T>(cx, |store, instance| {
129                 call_host::<_, _, _, _, S>(
130                     store,
131                     instance,
132                     TypeFuncIndex::from_u32(ty),
133                     OptionsIndex::from_u32(options),
134                     NonNull::slice_from_raw_parts(storage, storage_len).as_mut(),
135                     move |store, args| (*data.as_ptr())(store, args),
136                 )
137             })
138         }
139     }
140 
141     fn new_dynamic_canonical<T, F, S>(func: F) -> Arc<HostFunc>
142     where
143         F: Fn(
144                 StoreContextMut<'_, T>,
145                 ComponentFunc,
146                 Vec<Val>,
147                 usize,
148             ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>>
149             + Send
150             + Sync
151             + 'static,
152         T: 'static,
153         S: FunctionStyle,
154     {
155         Arc::new(HostFunc {
156             entrypoint: dynamic_entrypoint::<T, F, S>,
157             // This function performs dynamic type checks on its parameters and
158             // results and subsequently does not need to perform up-front type
159             // checks. However, we _do_ verify async-ness here.
160             typecheck: Box::new(move |ty, types| {
161                 let ty = &types.types[ty];
162                 if S::ASYNC != ty.async_ {
163                     bail!("type mismatch with async");
164                 }
165 
166                 Ok(())
167             }),
168             func: Box::new(func),
169         })
170     }
171 
172     pub(crate) fn new_dynamic<T: 'static, F>(func: F) -> Arc<HostFunc>
173     where
174         F: Fn(StoreContextMut<'_, T>, ComponentFunc, &[Val], &mut [Val]) -> Result<()>
175             + Send
176             + Sync
177             + 'static,
178     {
179         Self::new_dynamic_canonical::<T, _, SyncStyle>(
180             move |store, ty, mut params_and_results, result_start| {
181                 let (params, results) = params_and_results.split_at_mut(result_start);
182                 let result = func(store, ty, params, results).map(move |()| params_and_results);
183                 Box::pin(async move { result })
184             },
185         )
186     }
187 
188     #[cfg(feature = "component-model-async")]
189     pub(crate) fn new_dynamic_concurrent<T, F>(func: F) -> Arc<HostFunc>
190     where
191         T: 'static,
192         F: for<'a> Fn(
193                 &'a Accessor<T>,
194                 ComponentFunc,
195                 &'a [Val],
196                 &'a mut [Val],
197             ) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>
198             + Send
199             + Sync
200             + 'static,
201     {
202         let func = Arc::new(func);
203         Self::new_dynamic_canonical::<T, _, AsyncStyle>(
204             move |store, ty, mut params_and_results, result_start| {
205                 let func = func.clone();
206                 Box::pin(store.wrap_call(move |accessor| {
207                     Box::pin(async move {
208                         let (params, results) = params_and_results.split_at_mut(result_start);
209                         func(accessor, ty, params, results).await?;
210                         Ok(params_and_results)
211                     })
212                 }))
213             },
214         )
215     }
216 
217     pub fn typecheck(&self, ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> {
218         (self.typecheck)(ty, types)
219     }
220 
221     pub fn lowering(&self) -> VMLowering {
222         let data = NonNull::from(&*self.func).cast();
223         VMLowering {
224             callee: NonNull::new(self.entrypoint as *mut _).unwrap().into(),
225             data: data.into(),
226         }
227     }
228 }
229 
230 fn typecheck<P, R, S>(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()>
231 where
232     P: ComponentNamedList + Lift,
233     R: ComponentNamedList + Lower,
234     S: FunctionStyle,
235 {
236     let ty = &types.types[ty];
237     if S::ASYNC != ty.async_ {
238         bail!("type mismatch with async");
239     }
240     P::typecheck(&InterfaceType::Tuple(ty.params), types)
241         .context("type mismatch with parameters")?;
242     R::typecheck(&InterfaceType::Tuple(ty.results), types).context("type mismatch with results")?;
243     Ok(())
244 }
245 
246 /// The "meat" of calling a host function from wasm.
247 ///
248 /// This function is delegated to from implementations of
249 /// `HostFunc::from_closure`. Most of the arguments from the `entrypoint` are
250 /// forwarded here except for the `data` pointer which is encapsulated in the
251 /// `closure` argument here.
252 ///
253 /// This function is parameterized over:
254 ///
255 /// * `T` - the type of store this function works with (an unsafe assertion)
256 /// * `Params` - the parameters to the host function, viewed as a tuple
257 /// * `Return` - the result of the host function
258 /// * `F` - the `closure` to actually receive the `Params` and return the
259 ///   `Return`
260 /// * `S` - the expected `FunctionStyle`
261 ///
262 /// It's expected that `F` will "un-tuple" the arguments to pass to a host
263 /// closure.
264 ///
265 /// This function is in general `unsafe` as the validity of all the parameters
266 /// must be upheld. Generally that's done by ensuring this is only called from
267 /// the select few places it's intended to be called from.
268 unsafe fn call_host<T, Params, Return, F, S>(
269     store: StoreContextMut<'_, T>,
270     instance: Instance,
271     ty: TypeFuncIndex,
272     options: OptionsIndex,
273     storage: &mut [MaybeUninit<ValRaw>],
274     closure: F,
275 ) -> Result<()>
276 where
277     F: Fn(StoreContextMut<'_, T>, Params) -> HostResult<Return> + Send + Sync + 'static,
278     Params: Lift,
279     Return: Lower + 'static,
280     S: FunctionStyle,
281 {
282     let (component, store) = instance.component_and_store_mut(store.0);
283     let mut store = StoreContextMut(store);
284     let vminstance = instance.id().get(store.0);
285     let opts = &vminstance.component().env_component().options[options];
286     let async_lower = opts.async_;
287     let caller_instance = opts.instance;
288     let mut flags = vminstance.instance_flags(caller_instance);
289 
290     // Perform a dynamic check that this instance can indeed be left. Exiting
291     // the component is disallowed, for example, when the `realloc` function
292     // calls a canonical import.
293     if unsafe { !flags.may_leave() } {
294         return Err(anyhow!(crate::Trap::CannotLeaveComponent));
295     }
296 
297     let types = component.types();
298     let ty = &types[ty];
299     let param_tys = InterfaceType::Tuple(ty.params);
300     let result_tys = InterfaceType::Tuple(ty.results);
301 
302     if async_lower {
303         #[cfg(feature = "component-model-async")]
304         {
305             let mut storage = unsafe { Storage::<'_, Params, u32>::new_async::<Return>(storage) };
306 
307             // Lift the parameters, either from flat storage or from linear
308             // memory.
309             let lift = &mut LiftContext::new(store.0.store_opaque_mut(), options, instance);
310             lift.enter_call();
311             let params = storage.lift_params(lift, param_tys)?;
312 
313             // Load the return pointer, if present.
314             let retptr = match storage.async_retptr() {
315                 Some(ptr) => {
316                     let mut lower = LowerContext::new(store.as_context_mut(), options, instance);
317                     validate_inbounds::<Return>(lower.as_slice_mut(), ptr)?
318                 }
319                 // If there's no return pointer then `Return` should have an
320                 // empty flat representation. In this situation pretend the
321                 // return pointer was 0 so we have something to shepherd along
322                 // into the closure below.
323                 None => {
324                     assert_eq!(Return::flatten_count(), 0);
325                     0
326                 }
327             };
328 
329             let host_result = closure(store.as_context_mut(), params);
330 
331             let mut lower_result = {
332                 move |store: StoreContextMut<T>, ret: Return| {
333                     unsafe {
334                         flags.set_may_leave(false);
335                     }
336                     let mut lower = LowerContext::new(store, options, instance);
337                     ret.linear_lower_to_memory(&mut lower, result_tys, retptr)?;
338                     unsafe {
339                         flags.set_may_leave(true);
340                     }
341                     lower.exit_call()?;
342                     Ok(())
343                 }
344             };
345             let task = match host_result {
346                 HostResult::Done(result) => {
347                     lower_result(store.as_context_mut(), result?)?;
348                     None
349                 }
350                 #[cfg(feature = "component-model-async")]
351                 HostResult::Future(future) => instance.first_poll(
352                     store.as_context_mut(),
353                     future,
354                     caller_instance,
355                     lower_result,
356                 )?,
357             };
358 
359             let status = if let Some(task) = task {
360                 Status::Started.pack(Some(task))
361             } else {
362                 Status::Returned.pack(None)
363             };
364 
365             let mut lower = LowerContext::new(store, options, instance);
366             storage.lower_results(&mut lower, InterfaceType::U32, status)?;
367         }
368         #[cfg(not(feature = "component-model-async"))]
369         {
370             let _ = caller_instance;
371             unreachable!(
372                 "async-lowered imports should have failed validation \
373                  when `component-model-async` feature disabled"
374             );
375         }
376     } else {
377         if S::ASYNC {
378             // The caller has synchronously lowered an async function, meaning
379             // the caller can only call it from an async task (i.e. a task
380             // created via a call to an async export).  Otherwise, we'll trap.
381             concurrent::check_blocking(store.0)?;
382         }
383 
384         let mut storage = unsafe { Storage::<'_, Params, Return>::new_sync(storage) };
385         let mut lift = LiftContext::new(store.0.store_opaque_mut(), options, instance);
386         lift.enter_call();
387         let params = storage.lift_params(&mut lift, param_tys)?;
388 
389         let ret = match closure(store.as_context_mut(), params) {
390             HostResult::Done(result) => result?,
391             #[cfg(feature = "component-model-async")]
392             HostResult::Future(future) => {
393                 concurrent::poll_and_block(store.0, future, caller_instance)?
394             }
395         };
396 
397         unsafe {
398             flags.set_may_leave(false);
399         }
400         let mut lower = LowerContext::new(store, options, instance);
401         storage.lower_results(&mut lower, result_tys, ret)?;
402         unsafe {
403             flags.set_may_leave(true);
404         }
405         lower.exit_call()?;
406     }
407 
408     return Ok(());
409 
410     /// Type-level representation of the matrix of possibilities of how
411     /// WebAssembly parameters and results are handled in the canonical ABI.
412     ///
413     /// Wasmtime's ABI here always works with `&mut [MaybeUninit<ValRaw>]` as the
414     /// base representation of params/results. Parameters are passed
415     /// sequentially and results are returned by overwriting the parameters.
416     /// That means both params/results start from index 0.
417     ///
418     /// The type-level representation here involves working with the typed
419     /// `P::Lower` and `R::Lower` values which is a type-level representation of
420     /// a lowered value. All lowered values are in essence a sequence of
421     /// `ValRaw` values one after the other to fit within this original array
422     /// that is the basis of Wasmtime's ABI.
423     ///
424     /// The various combinations here are cryptic, but only used in this file.
425     /// This in theory cuts down on the verbosity below, but an explanation of
426     /// the various acronyms here are:
427     ///
428     /// * Pd - params direct - means that parameters are passed directly in
429     ///   their flat representation via `P::Lower`.
430     ///
431     /// * Pi - params indirect - means that parameters are passed indirectly in
432     ///   linear memory and the argument here is `ValRaw` to store the pointer.
433     ///
434     /// * Rd - results direct - means that results are returned directly in
435     ///   their flat representation via `R::Lower`. Note that this is always
436     ///   represented as `MaybeUninit<R::Lower>` as well because the return
437     ///   values may point to uninitialized memory if there were no parameters
438     ///   for example.
439     ///
440     /// * Ri - results indirect - means that results are returned indirectly in
441     ///   linear memory through the pointer specified. Note that this is
442     ///   specified as a `ValRaw` to represent the argument that's being given
443     ///   to the host from WebAssembly.
444     ///
445     /// * Ar - async results - means that the parameters to this call
446     ///   additionally include an async result pointer. Async results are always
447     ///   transmitted via a pointer so this is always a `ValRaw`.
448     ///
449     /// Internally this type makes liberal use of `Union` and `Pair` helpers
450     /// below which are simple `#[repr(C)]` wrappers around a pair of types that
451     /// are a union or a pair.
452     ///
453     /// Note that for any combination of `P` and `R` this `enum` is actually
454     /// pointless as a single variant will be used. In theory we should be able
455     /// to monomorphize based on `P` and `R` to a specific type. This
456     /// monomorphization depends on conditionals like `flatten_count() <= N`,
457     /// however, and I don't know how to encode that in Rust easily. In lieu of
458     /// that we assume LLVM will figure things out and boil away the actual enum
459     /// and runtime dispatch.
460     enum Storage<'a, P: ComponentType, R: ComponentType> {
461         /// Params: direct, Results: direct
462         ///
463         /// The lowered representation of params/results are overlaid on top of
464         /// each other.
465         PdRd(&'a mut Union<P::Lower, MaybeUninit<R::Lower>>),
466 
467         /// Params: direct, Results: indirect
468         ///
469         /// The return pointer comes after the params so this is sequentially
470         /// laid out with one after the other.
471         PdRi(&'a Pair<P::Lower, ValRaw>),
472 
473         /// Params: indirect, Results: direct
474         ///
475         /// Here the return values are overlaid on top of the pointer parameter.
476         PiRd(&'a mut Union<ValRaw, MaybeUninit<R::Lower>>),
477 
478         /// Params: indirect, Results: indirect
479         ///
480         /// Here the two parameters are laid out sequentially one after the
481         /// other.
482         PiRi(&'a Pair<ValRaw, ValRaw>),
483 
484         /// Params: direct + async result, Results: direct
485         ///
486         /// This is like `PdRd` except that the parameters additionally include
487         /// a pointer for where to store the result.
488         #[cfg(feature = "component-model-async")]
489         PdArRd(&'a mut Union<Pair<P::Lower, ValRaw>, MaybeUninit<R::Lower>>),
490 
491         /// Params: indirect + async result, Results: direct
492         ///
493         /// This is like `PiRd` except that the parameters additionally include
494         /// a pointer for where to store the result.
495         #[cfg(feature = "component-model-async")]
496         PiArRd(&'a mut Union<Pair<ValRaw, ValRaw>, MaybeUninit<R::Lower>>),
497     }
498 
499     // Helper structure used above in `Storage` to represent two consecutive
500     // values.
501     #[repr(C)]
502     #[derive(Copy, Clone)]
503     struct Pair<T, U> {
504         a: T,
505         b: U,
506     }
507 
508     // Helper structure used above in `Storage` to represent two values overlaid
509     // on each other.
510     #[repr(C)]
511     union Union<T: Copy, U: Copy> {
512         a: T,
513         b: U,
514     }
515 
516     /// Representation of where parameters are lifted from.
517     enum Src<'a, T> {
518         /// Parameters are directly lifted from `T`, which is under the hood a
519         /// sequence of `ValRaw`. This is `P::Lower` for example.
520         Direct(&'a T),
521 
522         /// Parameters are loaded from linear memory, and this is the wasm
523         /// parameter representing the pointer into linear memory to load from.
524         Indirect(&'a ValRaw),
525     }
526 
527     /// Dual of [`Src`], where to store results.
528     enum Dst<'a, T> {
529         /// Results are stored directly in this pointer.
530         ///
531         /// Note that this is a mutable pointer but it's specifically
532         /// `MaybeUninit` as trampolines do not initialize it. The `T` here will
533         /// be `R::Lower` for example.
534         Direct(&'a mut MaybeUninit<T>),
535 
536         /// Results are stored in linear memory, and this value is the wasm
537         /// parameter given which represents the pointer into linear memory.
538         ///
539         /// Note that this is not mutable as the parameter is not mutated, but
540         /// memory will be mutated.
541         Indirect(&'a ValRaw),
542     }
543 
544     impl<P, R> Storage<'_, P, R>
545     where
546         P: ComponentType + Lift,
547         R: ComponentType + Lower,
548     {
549         /// Classifies a new `Storage` suitable for use with sync functions.
550         ///
551         /// There's a 2x2 matrix of whether parameters and results are stored on the
552         /// stack or on the heap. Each of the 4 branches here have a different
553         /// representation of the storage of arguments/returns.
554         ///
555         /// Also note that while four branches are listed here only one is taken for
556         /// any particular `Params` and `Return` combination. This should be
557         /// trivially DCE'd by LLVM. Perhaps one day with enough const programming in
558         /// Rust we can make monomorphizations of this function codegen only one
559         /// branch, but today is not that day.
560         ///
561         /// # Safety
562         ///
563         /// Requires that the `storage` provided does indeed match an wasm
564         /// function with the signature of `P` and `R` as params/results.
565         unsafe fn new_sync(storage: &mut [MaybeUninit<ValRaw>]) -> Storage<'_, P, R> {
566             // SAFETY: this `unsafe` is due to the `slice_to_storage_*` helpers
567             // used which view the slice provided as a different type. This
568             // safety should be upheld by the contract of the `ComponentType`
569             // trait and its `Lower` type parameter meaning they're valid to
570             // view as a sequence of `ValRaw` types. Additionally the
571             // `ComponentType` trait ensures that the matching of the runtime
572             // length of `storage` should match the actual size of `P::Lower`
573             // and `R::Lower` or such as needed.
574             unsafe {
575                 if P::flatten_count() <= MAX_FLAT_PARAMS {
576                     if R::flatten_count() <= MAX_FLAT_RESULTS {
577                         Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut())
578                     } else {
579                         Storage::PdRi(slice_to_storage_mut(storage).assume_init_ref())
580                     }
581                 } else {
582                     if R::flatten_count() <= MAX_FLAT_RESULTS {
583                         Storage::PiRd(slice_to_storage_mut(storage).assume_init_mut())
584                     } else {
585                         Storage::PiRi(slice_to_storage_mut(storage).assume_init_ref())
586                     }
587                 }
588             }
589         }
590 
591         fn lift_params(&self, cx: &mut LiftContext<'_>, ty: InterfaceType) -> Result<P> {
592             match self.lift_src() {
593                 Src::Direct(storage) => P::linear_lift_from_flat(cx, ty, storage),
594                 Src::Indirect(ptr) => {
595                     let ptr = validate_inbounds::<P>(cx.memory(), ptr)?;
596                     P::linear_lift_from_memory(cx, ty, &cx.memory()[ptr..][..P::SIZE32])
597                 }
598             }
599         }
600 
601         fn lift_src(&self) -> Src<'_, P::Lower> {
602             match self {
603                 // SAFETY: these `unsafe` blocks are due to accessing union
604                 // fields. The safety here relies on the contract of the
605                 // `ComponentType` trait which should ensure that the types
606                 // projected onto a list of wasm parameters are indeed correct.
607                 // That means that the projections here, if the types are
608                 // correct, all line up to initialized memory that's well-typed
609                 // to access.
610                 Storage::PdRd(storage) => unsafe { Src::Direct(&storage.a) },
611                 Storage::PdRi(storage) => Src::Direct(&storage.a),
612                 #[cfg(feature = "component-model-async")]
613                 Storage::PdArRd(storage) => unsafe { Src::Direct(&storage.a.a) },
614                 Storage::PiRd(storage) => unsafe { Src::Indirect(&storage.a) },
615                 Storage::PiRi(storage) => Src::Indirect(&storage.a),
616                 #[cfg(feature = "component-model-async")]
617                 Storage::PiArRd(storage) => unsafe { Src::Indirect(&storage.a.a) },
618             }
619         }
620 
621         fn lower_results<T>(
622             &mut self,
623             cx: &mut LowerContext<'_, T>,
624             ty: InterfaceType,
625             ret: R,
626         ) -> Result<()> {
627             match self.lower_dst() {
628                 Dst::Direct(storage) => ret.linear_lower_to_flat(cx, ty, storage),
629                 Dst::Indirect(ptr) => {
630                     let ptr = validate_inbounds::<R>(cx.as_slice_mut(), ptr)?;
631                     ret.linear_lower_to_memory(cx, ty, ptr)
632                 }
633             }
634         }
635 
636         fn lower_dst(&mut self) -> Dst<'_, R::Lower> {
637             match self {
638                 // SAFETY: these unsafe blocks are due to accessing fields of a
639                 // `union` which is not safe in Rust. The returned value is
640                 // `MaybeUninit<R::Lower>` in all cases, however, which should
641                 // safely model how `union` memory is possibly uninitialized.
642                 // Additionally `R::Lower` has the `unsafe` contract that all
643                 // its bit patterns must be sound, which additionally should
644                 // help make this safe.
645                 Storage::PdRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
646                 Storage::PiRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
647                 #[cfg(feature = "component-model-async")]
648                 Storage::PdArRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
649                 #[cfg(feature = "component-model-async")]
650                 Storage::PiArRd(storage) => unsafe { Dst::Direct(&mut storage.b) },
651                 Storage::PdRi(storage) => Dst::Indirect(&storage.b),
652                 Storage::PiRi(storage) => Dst::Indirect(&storage.b),
653             }
654         }
655 
656         #[cfg(feature = "component-model-async")]
657         fn async_retptr(&self) -> Option<&ValRaw> {
658             match self {
659                 // SAFETY: like above these are `unsafe` due to accessing a
660                 // `union` field. This should be safe via the construction of
661                 // `Storage` which should correctly determine whether or not an
662                 // async return pointer is provided and classify the args/rets
663                 // appropriately.
664                 Storage::PdArRd(storage) => unsafe { Some(&storage.a.b) },
665                 Storage::PiArRd(storage) => unsafe { Some(&storage.a.b) },
666                 Storage::PdRd(_) | Storage::PiRd(_) | Storage::PdRi(_) | Storage::PiRi(_) => None,
667             }
668         }
669     }
670 
671     #[cfg(feature = "component-model-async")]
672     impl<P> Storage<'_, P, u32>
673     where
674         P: ComponentType + Lift,
675     {
676         /// Classifies a new `Storage` suitable for use with async functions.
677         ///
678         /// # Safety
679         ///
680         /// Requires that the `storage` provided does indeed match an `async`
681         /// wasm function with the signature of `P` and `R` as params/results.
682         unsafe fn new_async<R>(storage: &mut [MaybeUninit<ValRaw>]) -> Storage<'_, P, u32>
683         where
684             R: ComponentType + Lower,
685         {
686             // SAFETY: see `Storage::new` for discussion on why this should be
687             // safe given the unsafe contract of the `ComponentType` trait.
688             unsafe {
689                 if P::flatten_count() <= wasmtime_environ::component::MAX_FLAT_ASYNC_PARAMS {
690                     if R::flatten_count() == 0 {
691                         Storage::PdRd(slice_to_storage_mut(storage).assume_init_mut())
692                     } else {
693                         Storage::PdArRd(slice_to_storage_mut(storage).assume_init_mut())
694                     }
695                 } else {
696                     if R::flatten_count() == 0 {
697                         Storage::PiRd(slice_to_storage_mut(storage).assume_init_mut())
698                     } else {
699                         Storage::PiArRd(slice_to_storage_mut(storage).assume_init_mut())
700                     }
701                 }
702             }
703         }
704     }
705 }
706 
707 pub(crate) fn validate_inbounds<T: ComponentType>(memory: &[u8], ptr: &ValRaw) -> Result<usize> {
708     // FIXME(#4311): needs memory64 support
709     let ptr = usize::try_from(ptr.get_u32())?;
710     if ptr % usize::try_from(T::ALIGN32)? != 0 {
711         bail!("pointer not aligned");
712     }
713     let end = match ptr.checked_add(T::SIZE32) {
714         Some(n) => n,
715         None => bail!("pointer size overflow"),
716     };
717     if end > memory.len() {
718         bail!("pointer out of bounds")
719     }
720     Ok(ptr)
721 }
722 
723 unsafe fn call_host_and_handle_result<T>(
724     cx: NonNull<VMOpaqueContext>,
725     func: impl FnOnce(StoreContextMut<'_, T>, Instance) -> Result<()>,
726 ) -> bool
727 where
728     T: 'static,
729 {
730     let cx = unsafe { VMComponentContext::from_opaque(cx) };
731     unsafe {
732         ComponentInstance::enter_host_from_wasm(cx, |store, instance| {
733             let mut store = store.unchecked_context_mut();
734             store.0.call_hook(CallHook::CallingHost)?;
735             let res = func(store.as_context_mut(), instance);
736             store.0.call_hook(CallHook::ReturningFromHost)?;
737             res
738         })
739     }
740 }
741 
742 unsafe fn call_host_dynamic<T, F, S>(
743     store: StoreContextMut<'_, T>,
744     instance: Instance,
745     ty: TypeFuncIndex,
746     options: OptionsIndex,
747     storage: &mut [MaybeUninit<ValRaw>],
748     closure: F,
749 ) -> Result<()>
750 where
751     F: Fn(
752             StoreContextMut<'_, T>,
753             ComponentFunc,
754             Vec<Val>,
755             usize,
756         ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>>
757         + Send
758         + Sync
759         + 'static,
760     T: 'static,
761     S: FunctionStyle,
762 {
763     let (component, store) = instance.component_and_store_mut(store.0);
764     let mut store = StoreContextMut(store);
765     let vminstance = instance.id().get(store.0);
766     let opts = &component.env_component().options[options];
767     let async_lower = opts.async_;
768     let caller_instance = opts.instance;
769     let mut flags = vminstance.instance_flags(caller_instance);
770 
771     // Perform a dynamic check that this instance can indeed be left. Exiting
772     // the component is disallowed, for example, when the `realloc` function
773     // calls a canonical import.
774     if unsafe { !flags.may_leave() } {
775         return Err(anyhow!(crate::Trap::CannotLeaveComponent));
776     }
777 
778     let types = component.types();
779     let func_ty = &types[ty];
780     let param_tys = &types[func_ty.params];
781     let result_tys = &types[func_ty.results];
782 
783     let mut params_and_results = Vec::new();
784     let mut lift = &mut LiftContext::new(store.0.store_opaque_mut(), options, instance);
785     lift.enter_call();
786     let max_flat = if async_lower {
787         MAX_FLAT_ASYNC_PARAMS
788     } else {
789         MAX_FLAT_PARAMS
790     };
791     let ty = ComponentFunc::from(ty, &lift.instance_type());
792 
793     let ret_index = unsafe {
794         dynamic_params_load(
795             &mut lift,
796             types,
797             storage,
798             param_tys,
799             &mut params_and_results,
800             max_flat,
801         )?
802     };
803     let result_start = params_and_results.len();
804     for _ in 0..result_tys.types.len() {
805         params_and_results.push(Val::Bool(false));
806     }
807 
808     if async_lower {
809         #[cfg(feature = "component-model-async")]
810         {
811             let retptr = if result_tys.types.len() == 0 {
812                 0
813             } else {
814                 let retptr = unsafe { storage[ret_index].assume_init() };
815                 let mut lower = LowerContext::new(store.as_context_mut(), options, instance);
816                 validate_inbounds_dynamic(&result_tys.abi, lower.as_slice_mut(), &retptr)?
817             };
818 
819             let future = closure(store.as_context_mut(), ty, params_and_results, result_start);
820 
821             let task = instance.first_poll(store, future, caller_instance, {
822                 let result_tys = func_ty.results;
823                 move |store: StoreContextMut<T>, result_vals: Vec<Val>| {
824                     unsafe {
825                         flags.set_may_leave(false);
826                     }
827 
828                     let mut lower = LowerContext::new(store, options, instance);
829                     let result_tys = &lower.types[result_tys];
830                     let result_vals = &result_vals[result_start..];
831                     assert_eq!(result_vals.len(), result_tys.types.len());
832                     let mut ptr = retptr;
833                     for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
834                         let offset = lower.types.canonical_abi(ty).next_field32_size(&mut ptr);
835                         val.store(&mut lower, *ty, offset)?;
836                     }
837 
838                     unsafe {
839                         flags.set_may_leave(true);
840                     }
841 
842                     lower.exit_call()?;
843 
844                     Ok(())
845                 }
846             })?;
847 
848             let status = if let Some(task) = task {
849                 Status::Started.pack(Some(task))
850             } else {
851                 Status::Returned.pack(None)
852             };
853 
854             storage[0] = MaybeUninit::new(ValRaw::i32(status as i32));
855         }
856         #[cfg(not(feature = "component-model-async"))]
857         {
858             unreachable!(
859                 "async-lowered imports should have failed validation \
860                  when `component-model-async` feature disabled"
861             );
862         }
863     } else {
864         if S::ASYNC {
865             // The caller has synchronously lowered an async function, meaning
866             // the caller can only call it from an async task (i.e. a task
867             // created via a call to an async export).  Otherwise, we'll trap.
868             concurrent::check_blocking(store.0)?;
869         }
870 
871         let future = closure(store.as_context_mut(), ty, params_and_results, result_start);
872         let result_vals = concurrent::poll_and_block(store.0, future, caller_instance)?;
873         let result_vals = &result_vals[result_start..];
874 
875         unsafe {
876             flags.set_may_leave(false);
877         }
878 
879         let mut cx = LowerContext::new(store, options, instance);
880         if let Some(cnt) = result_tys.abi.flat_count(MAX_FLAT_RESULTS) {
881             let mut dst = storage[..cnt].iter_mut();
882             for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
883                 val.lower(&mut cx, *ty, &mut dst)?;
884             }
885             assert!(dst.next().is_none());
886         } else {
887             let ret_ptr = unsafe { storage[ret_index].assume_init_ref() };
888             let mut ptr = validate_inbounds_dynamic(&result_tys.abi, cx.as_slice_mut(), ret_ptr)?;
889             for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
890                 let offset = types.canonical_abi(ty).next_field32_size(&mut ptr);
891                 val.store(&mut cx, *ty, offset)?;
892             }
893         }
894 
895         unsafe {
896             flags.set_may_leave(true);
897         }
898 
899         cx.exit_call()?;
900     }
901 
902     Ok(())
903 }
904 
905 /// Loads the parameters for a dynamic host function call into `params`
906 ///
907 /// Returns the number of flat `storage` values consumed.
908 ///
909 /// # Safety
910 ///
911 /// Requires that `param_tys` matches the type signature of the `storage` that
912 /// was passed in.
913 unsafe fn dynamic_params_load(
914     cx: &mut LiftContext<'_>,
915     types: &ComponentTypes,
916     storage: &[MaybeUninit<ValRaw>],
917     param_tys: &TypeTuple,
918     params: &mut Vec<Val>,
919     max_flat_params: usize,
920 ) -> Result<usize> {
921     if let Some(param_count) = param_tys.abi.flat_count(max_flat_params) {
922         // NB: can use `MaybeUninit::slice_assume_init_ref` when that's stable
923         let storage =
924             unsafe { mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(&storage[..param_count]) };
925         let mut iter = storage.iter();
926         for ty in param_tys.types.iter() {
927             params.push(Val::lift(cx, *ty, &mut iter)?);
928         }
929         assert!(iter.next().is_none());
930         Ok(param_count)
931     } else {
932         let mut offset = validate_inbounds_dynamic(&param_tys.abi, cx.memory(), unsafe {
933             storage[0].assume_init_ref()
934         })?;
935         for ty in param_tys.types.iter() {
936             let abi = types.canonical_abi(ty);
937             let size = usize::try_from(abi.size32).unwrap();
938             let memory = &cx.memory()[abi.next_field32_size(&mut offset)..][..size];
939             params.push(Val::load(cx, *ty, memory)?);
940         }
941         Ok(1)
942     }
943 }
944 
945 pub(crate) fn validate_inbounds_dynamic(
946     abi: &CanonicalAbiInfo,
947     memory: &[u8],
948     ptr: &ValRaw,
949 ) -> Result<usize> {
950     // FIXME(#4311): needs memory64 support
951     let ptr = usize::try_from(ptr.get_u32())?;
952     if ptr % usize::try_from(abi.align32)? != 0 {
953         bail!("pointer not aligned");
954     }
955     let end = match ptr.checked_add(usize::try_from(abi.size32).unwrap()) {
956         Some(n) => n,
957         None => bail!("pointer size overflow"),
958     };
959     if end > memory.len() {
960         bail!("pointer out of bounds")
961     }
962     Ok(ptr)
963 }
964 
965 extern "C" fn dynamic_entrypoint<T, F, S>(
966     cx: NonNull<VMOpaqueContext>,
967     data: NonNull<u8>,
968     ty: u32,
969     options: u32,
970     storage: NonNull<MaybeUninit<ValRaw>>,
971     storage_len: usize,
972 ) -> bool
973 where
974     F: Fn(
975             StoreContextMut<'_, T>,
976             ComponentFunc,
977             Vec<Val>,
978             usize,
979         ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>>
980         + Send
981         + Sync
982         + 'static,
983     T: 'static,
984     S: FunctionStyle,
985 {
986     let data = SendSyncPtr::new(NonNull::new(data.as_ptr() as *mut F).unwrap());
987     unsafe {
988         call_host_and_handle_result(cx, |store, instance| {
989             call_host_dynamic::<T, _, S>(
990                 store,
991                 instance,
992                 TypeFuncIndex::from_u32(ty),
993                 OptionsIndex::from_u32(options),
994                 NonNull::slice_from_raw_parts(storage, storage_len).as_mut(),
995                 &*data.as_ptr(),
996             )
997         })
998     }
999 }
1000