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