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, 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 196 fn typecheck<P, R>(ty: TypeFuncIndex, types: &InstanceType<'_>) -> Result<()> 197 where 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. 229 unsafe 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<()> 237 where 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 bail!("cannot leave component instance"); 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.traitobj_mut(), 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 661 pub(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 677 unsafe fn call_host_and_handle_result<T>( 678 cx: NonNull<VMOpaqueContext>, 679 func: impl FnOnce(StoreContextMut<'_, T>, Instance) -> Result<()>, 680 ) -> bool 681 where 682 T: 'static, 683 { 684 let cx = unsafe { VMComponentContext::from_opaque(cx) }; 685 unsafe { 686 ComponentInstance::from_vmctx(cx, |store, instance| { 687 let mut store = store.unchecked_context_mut(); 688 689 crate::runtime::vm::catch_unwind_and_record_trap(|| { 690 store.0.call_hook(CallHook::CallingHost)?; 691 let res = func(store.as_context_mut(), instance); 692 store.0.call_hook(CallHook::ReturningFromHost)?; 693 res 694 }) 695 }) 696 } 697 } 698 699 unsafe fn call_host_dynamic<T, F>( 700 mut store: StoreContextMut<'_, T>, 701 instance: Instance, 702 ty: TypeFuncIndex, 703 options_idx: OptionsIndex, 704 storage: &mut [MaybeUninit<ValRaw>], 705 closure: F, 706 ) -> Result<()> 707 where 708 F: Fn( 709 StoreContextMut<'_, T>, 710 Instance, 711 Vec<Val>, 712 usize, 713 ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>> 714 + Send 715 + Sync 716 + 'static, 717 T: 'static, 718 { 719 let options = Options::new_index(store.0, instance, options_idx); 720 let vminstance = instance.id().get(store.0); 721 let opts = &vminstance.component().env_component().options[options_idx]; 722 let async_ = opts.async_; 723 let caller_instance = opts.instance; 724 let mut flags = vminstance.instance_flags(caller_instance); 725 726 // Perform a dynamic check that this instance can indeed be left. Exiting 727 // the component is disallowed, for example, when the `realloc` function 728 // calls a canonical import. 729 if unsafe { !flags.may_leave() } { 730 bail!("cannot leave component instance"); 731 } 732 733 let types = instance.id().get(store.0).component().types().clone(); 734 let func_ty = &types[ty]; 735 let param_tys = &types[func_ty.params]; 736 let result_tys = &types[func_ty.results]; 737 738 let mut params_and_results = Vec::new(); 739 let mut lift = &mut LiftContext::new(store.0.store_opaque_mut(), &options, instance); 740 lift.enter_call(); 741 let max_flat = if async_ { 742 MAX_FLAT_ASYNC_PARAMS 743 } else { 744 MAX_FLAT_PARAMS 745 }; 746 747 let ret_index = unsafe { 748 dynamic_params_load( 749 &mut lift, 750 &types, 751 storage, 752 param_tys, 753 &mut params_and_results, 754 max_flat, 755 )? 756 }; 757 let result_start = params_and_results.len(); 758 for _ in 0..result_tys.types.len() { 759 params_and_results.push(Val::Bool(false)); 760 } 761 762 if async_ { 763 #[cfg(feature = "component-model-async")] 764 { 765 let retptr = if result_tys.types.len() == 0 { 766 0 767 } else { 768 let retptr = unsafe { storage[ret_index].assume_init() }; 769 let mut lower = 770 LowerContext::new(store.as_context_mut(), &options, &types, instance); 771 validate_inbounds_dynamic(&result_tys.abi, lower.as_slice_mut(), &retptr)? 772 }; 773 774 let future = closure( 775 store.as_context_mut(), 776 instance, 777 params_and_results, 778 result_start, 779 ); 780 781 let task = instance.first_poll(store, future, caller_instance, { 782 let types = types.clone(); 783 let result_tys = func_ty.results; 784 move |store: StoreContextMut<T>, instance: Instance, result_vals: Vec<Val>| { 785 let result_tys = &types[result_tys]; 786 let result_vals = &result_vals[result_start..]; 787 assert_eq!(result_vals.len(), result_tys.types.len()); 788 789 unsafe { 790 flags.set_may_leave(false); 791 } 792 793 let mut lower = LowerContext::new(store, &options, &types, instance); 794 let mut ptr = retptr; 795 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) { 796 let offset = types.canonical_abi(ty).next_field32_size(&mut ptr); 797 val.store(&mut lower, *ty, offset)?; 798 } 799 800 unsafe { 801 flags.set_may_leave(true); 802 } 803 804 lower.exit_call()?; 805 806 Ok(()) 807 } 808 })?; 809 810 let status = if let Some(task) = task { 811 Status::Started.pack(Some(task)) 812 } else { 813 Status::Returned.pack(None) 814 }; 815 816 storage[0] = MaybeUninit::new(ValRaw::i32(status as i32)); 817 } 818 #[cfg(not(feature = "component-model-async"))] 819 { 820 unreachable!( 821 "async-lowered imports should have failed validation \ 822 when `component-model-async` feature disabled" 823 ); 824 } 825 } else { 826 let future = closure( 827 store.as_context_mut(), 828 instance, 829 params_and_results, 830 result_start, 831 ); 832 let result_vals = 833 instance.poll_and_block(store.0.traitobj_mut(), future, caller_instance)?; 834 let result_vals = &result_vals[result_start..]; 835 836 unsafe { 837 flags.set_may_leave(false); 838 } 839 840 let mut cx = LowerContext::new(store, &options, &types, instance); 841 if let Some(cnt) = result_tys.abi.flat_count(MAX_FLAT_RESULTS) { 842 let mut dst = storage[..cnt].iter_mut(); 843 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) { 844 val.lower(&mut cx, *ty, &mut dst)?; 845 } 846 assert!(dst.next().is_none()); 847 } else { 848 let ret_ptr = unsafe { storage[ret_index].assume_init_ref() }; 849 let mut ptr = validate_inbounds_dynamic(&result_tys.abi, cx.as_slice_mut(), ret_ptr)?; 850 for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) { 851 let offset = types.canonical_abi(ty).next_field32_size(&mut ptr); 852 val.store(&mut cx, *ty, offset)?; 853 } 854 } 855 856 unsafe { 857 flags.set_may_leave(true); 858 } 859 860 cx.exit_call()?; 861 } 862 863 Ok(()) 864 } 865 866 /// Loads the parameters for a dynamic host function call into `params` 867 /// 868 /// Returns the number of flat `storage` values consumed. 869 /// 870 /// # Safety 871 /// 872 /// Requires that `param_tys` matches the type signature of the `storage` that 873 /// was passed in. 874 unsafe fn dynamic_params_load( 875 cx: &mut LiftContext<'_>, 876 types: &ComponentTypes, 877 storage: &[MaybeUninit<ValRaw>], 878 param_tys: &TypeTuple, 879 params: &mut Vec<Val>, 880 max_flat_params: usize, 881 ) -> Result<usize> { 882 if let Some(param_count) = param_tys.abi.flat_count(max_flat_params) { 883 // NB: can use `MaybeUninit::slice_assume_init_ref` when that's stable 884 let storage = 885 unsafe { mem::transmute::<&[MaybeUninit<ValRaw>], &[ValRaw]>(&storage[..param_count]) }; 886 let mut iter = storage.iter(); 887 for ty in param_tys.types.iter() { 888 params.push(Val::lift(cx, *ty, &mut iter)?); 889 } 890 assert!(iter.next().is_none()); 891 Ok(param_count) 892 } else { 893 let mut offset = validate_inbounds_dynamic(¶m_tys.abi, cx.memory(), unsafe { 894 storage[0].assume_init_ref() 895 })?; 896 for ty in param_tys.types.iter() { 897 let abi = types.canonical_abi(ty); 898 let size = usize::try_from(abi.size32).unwrap(); 899 let memory = &cx.memory()[abi.next_field32_size(&mut offset)..][..size]; 900 params.push(Val::load(cx, *ty, memory)?); 901 } 902 Ok(1) 903 } 904 } 905 906 pub(crate) fn validate_inbounds_dynamic( 907 abi: &CanonicalAbiInfo, 908 memory: &[u8], 909 ptr: &ValRaw, 910 ) -> Result<usize> { 911 // FIXME(#4311): needs memory64 support 912 let ptr = usize::try_from(ptr.get_u32())?; 913 if ptr % usize::try_from(abi.align32)? != 0 { 914 bail!("pointer not aligned"); 915 } 916 let end = match ptr.checked_add(usize::try_from(abi.size32).unwrap()) { 917 Some(n) => n, 918 None => bail!("pointer size overflow"), 919 }; 920 if end > memory.len() { 921 bail!("pointer out of bounds") 922 } 923 Ok(ptr) 924 } 925 926 extern "C" fn dynamic_entrypoint<T, F>( 927 cx: NonNull<VMOpaqueContext>, 928 data: NonNull<u8>, 929 ty: u32, 930 options: u32, 931 storage: NonNull<MaybeUninit<ValRaw>>, 932 storage_len: usize, 933 ) -> bool 934 where 935 F: Fn( 936 StoreContextMut<'_, T>, 937 Instance, 938 Vec<Val>, 939 usize, 940 ) -> Pin<Box<dyn Future<Output = Result<Vec<Val>>> + Send + 'static>> 941 + Send 942 + Sync 943 + 'static, 944 T: 'static, 945 { 946 let data = SendSyncPtr::new(NonNull::new(data.as_ptr() as *mut F).unwrap()); 947 unsafe { 948 call_host_and_handle_result(cx, |store, instance| { 949 call_host_dynamic::<T, _>( 950 store, 951 instance, 952 TypeFuncIndex::from_u32(ty), 953 OptionsIndex::from_u32(options), 954 NonNull::slice_from_raw_parts(storage, storage_len).as_mut(), 955 move |store, instance, params, results| { 956 (*data.as_ptr())(store, instance, params, results) 957 }, 958 ) 959 }) 960 } 961 } 962