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