wasmtime/runtime/component/func/
host.rs

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