1 use crate::StoreContextMut; 2 #[cfg(feature = "component-model-async")] 3 use crate::component::concurrent::ConcurrentState; 4 use crate::component::matching::InstanceType; 5 use crate::component::resources::{HostResourceData, HostResourceIndex, HostResourceTables}; 6 use crate::component::store::ComponentTaskState; 7 use crate::component::{Instance, ResourceType, RuntimeInstance}; 8 use crate::prelude::*; 9 use crate::runtime::vm::VMFuncRef; 10 use crate::runtime::vm::component::{ComponentInstance, HandleTable, ResourceTables}; 11 use crate::store::{StoreId, StoreOpaque}; 12 use alloc::sync::Arc; 13 use core::fmt; 14 use core::pin::Pin; 15 use core::ptr::NonNull; 16 use wasmtime_environ::component::{ 17 CanonicalOptions, CanonicalOptionsDataModel, ComponentTypes, OptionsIndex, 18 TypeResourceTableIndex, 19 }; 20 21 /// A helper structure which is a "package" of the context used during lowering 22 /// values into a component (or storing them into memory). 23 /// 24 /// This type is used by the `Lower` trait extensively and contains any 25 /// contextual information necessary related to the context in which the 26 /// lowering is happening. 27 #[doc(hidden)] 28 pub struct LowerContext<'a, T: 'static> { 29 /// Lowering may involve invoking memory allocation functions so part of the 30 /// context here is carrying access to the entire store that wasm is 31 /// executing within. This store serves as proof-of-ability to actually 32 /// execute wasm safely. 33 pub store: StoreContextMut<'a, T>, 34 35 /// Lowering always happens into a function that's been `canon lift`'d or 36 /// `canon lower`'d, both of which specify a set of options for the 37 /// canonical ABI. For example details like string encoding are contained 38 /// here along with which memory pointers are relative to or what the memory 39 /// allocation function is. 40 options: OptionsIndex, 41 42 /// Lowering happens within the context of a component instance and this 43 /// field stores the type information of that component instance. This is 44 /// used for type lookups and general type queries during the 45 /// lifting/lowering process. 46 pub types: &'a ComponentTypes, 47 48 /// Index of the component instance that's being lowered into. 49 instance: Instance, 50 51 /// Whether to allow `options.realloc` to be used when lowering. 52 allow_realloc: bool, 53 } 54 55 #[doc(hidden)] 56 impl<'a, T: 'static> LowerContext<'a, T> { 57 /// Creates a new lowering context from the specified parameters. new( store: StoreContextMut<'a, T>, options: OptionsIndex, instance: Instance, ) -> LowerContext<'a, T>58 pub fn new( 59 store: StoreContextMut<'a, T>, 60 options: OptionsIndex, 61 instance: Instance, 62 ) -> LowerContext<'a, T> { 63 // Debug-assert that if we can't block that blocking is indeed allowed. 64 // This'll catch when this is accidentally created outside of a fiber 65 // when we need to be on a fiber. 66 if cfg!(debug_assertions) && !store.0.can_block() { 67 store.0.validate_sync_call().unwrap(); 68 } 69 let (component, store) = instance.component_and_store_mut(store.0); 70 LowerContext { 71 store: StoreContextMut(store), 72 options, 73 types: component.types(), 74 instance, 75 allow_realloc: true, 76 } 77 } 78 79 /// Like `new`, except disallows use of `options.realloc`. 80 /// 81 /// The returned object will panic if its `realloc` method is called. 82 /// 83 /// This is meant for use when lowering "flat" values (i.e. values which 84 /// require no allocations) into already-allocated memory or into stack 85 /// slots, in which case the lowering may safely be done outside of a fiber 86 /// since there is no need to make any guest calls. 87 #[cfg(feature = "component-model-async")] new_without_realloc( store: StoreContextMut<'a, T>, options: OptionsIndex, instance: Instance, ) -> LowerContext<'a, T>88 pub(crate) fn new_without_realloc( 89 store: StoreContextMut<'a, T>, 90 options: OptionsIndex, 91 instance: Instance, 92 ) -> LowerContext<'a, T> { 93 let (component, store) = instance.component_and_store_mut(store.0); 94 LowerContext { 95 store: StoreContextMut(store), 96 options, 97 types: component.types(), 98 instance, 99 allow_realloc: false, 100 } 101 } 102 103 /// Returns the `&ComponentInstance` that's being lowered into. instance(&self) -> &ComponentInstance104 pub fn instance(&self) -> &ComponentInstance { 105 self.instance.id().get(self.store.0) 106 } 107 108 /// Returns the `&mut ComponentInstance` that's being lowered into. instance_mut(&mut self) -> Pin<&mut ComponentInstance>109 pub fn instance_mut(&mut self) -> Pin<&mut ComponentInstance> { 110 self.instance.id().get_mut(self.store.0) 111 } 112 113 /// Returns the canonical options that are being used during lifting. options(&self) -> &CanonicalOptions114 pub fn options(&self) -> &CanonicalOptions { 115 &self.instance().component().env_component().options[self.options] 116 } 117 118 /// Returns a view into memory as a mutable slice of bytes. 119 /// 120 /// # Panics 121 /// 122 /// This will panic if memory has not been configured for this lowering 123 /// (e.g. it wasn't present during the specification of canonical options). as_slice_mut(&mut self) -> &mut [u8]124 pub fn as_slice_mut(&mut self) -> &mut [u8] { 125 self.instance.options_memory_mut(self.store.0, self.options) 126 } 127 128 /// Invokes the memory allocation function (which is style after `realloc`) 129 /// with the specified parameters. 130 /// 131 /// # Panics 132 /// 133 /// This will panic if realloc hasn't been configured for this lowering via 134 /// its canonical options. realloc( &mut self, old: usize, old_size: usize, old_align: u32, new_size: usize, ) -> Result<usize>135 pub fn realloc( 136 &mut self, 137 old: usize, 138 old_size: usize, 139 old_align: u32, 140 new_size: usize, 141 ) -> Result<usize> { 142 assert!(self.allow_realloc); 143 144 let (component, store) = self.instance.component_and_store_mut(self.store.0); 145 let instance = self.instance.id().get(store); 146 let options = &component.env_component().options[self.options]; 147 let realloc_ty = component.realloc_func_ty(); 148 let realloc = match options.data_model { 149 CanonicalOptionsDataModel::Gc {} => unreachable!(), 150 CanonicalOptionsDataModel::LinearMemory(m) => m.realloc.unwrap(), 151 }; 152 let realloc = instance.runtime_realloc(realloc); 153 154 let params = ( 155 u32::try_from(old)?, 156 u32::try_from(old_size)?, 157 old_align, 158 u32::try_from(new_size)?, 159 ); 160 161 type ReallocFunc = crate::TypedFunc<(u32, u32, u32, u32), u32>; 162 163 // Invoke the wasm malloc function using its raw and statically known 164 // signature. 165 let result = unsafe { 166 ReallocFunc::call_raw(&mut StoreContextMut(store), &realloc_ty, realloc, params)? 167 }; 168 169 if result % old_align != 0 { 170 bail!("realloc return: result not aligned"); 171 } 172 let result = usize::try_from(result)?; 173 174 if self 175 .as_slice_mut() 176 .get_mut(result..) 177 .and_then(|s| s.get_mut(..new_size)) 178 .is_none() 179 { 180 bail!("realloc return: beyond end of memory") 181 } 182 183 Ok(result) 184 } 185 186 /// Returns a fixed mutable slice of memory `N` bytes large starting at 187 /// offset `N`, panicking on out-of-bounds. 188 /// 189 /// It should be previously verified that `offset` is in-bounds via 190 /// bounds-checks. 191 /// 192 /// # Panics 193 /// 194 /// This will panic if memory has not been configured for this lowering 195 /// (e.g. it wasn't present during the specification of canonical options). get<const N: usize>(&mut self, offset: usize) -> &mut [u8; N]196 pub fn get<const N: usize>(&mut self, offset: usize) -> &mut [u8; N] { 197 // FIXME: this bounds check shouldn't actually be necessary, all 198 // callers of `ComponentType::store` have already performed a bounds 199 // check so we're guaranteed that `offset..offset+N` is in-bounds. That 200 // being said we at least should do bounds checks in debug mode and 201 // it's not clear to me how to easily structure this so that it's 202 // "statically obvious" the bounds check isn't necessary. 203 // 204 // For now I figure we can leave in this bounds check and if it becomes 205 // an issue we can optimize further later, probably with judicious use 206 // of `unsafe`. 207 self.as_slice_mut()[offset..].first_chunk_mut().unwrap() 208 } 209 210 /// Lowers an `own` resource into the guest, converting the `rep` specified 211 /// into a guest-local index. 212 /// 213 /// The `ty` provided is which table to put this into. guest_resource_lower_own( &mut self, ty: TypeResourceTableIndex, rep: u32, ) -> Result<u32>214 pub fn guest_resource_lower_own( 215 &mut self, 216 ty: TypeResourceTableIndex, 217 rep: u32, 218 ) -> Result<u32> { 219 self.resource_tables().guest_resource_lower_own(rep, ty) 220 } 221 222 /// Lowers a `borrow` resource into the guest, converting the `rep` to a 223 /// guest-local index in the `ty` table specified. guest_resource_lower_borrow( &mut self, ty: TypeResourceTableIndex, rep: u32, ) -> Result<u32>224 pub fn guest_resource_lower_borrow( 225 &mut self, 226 ty: TypeResourceTableIndex, 227 rep: u32, 228 ) -> Result<u32> { 229 // Implement `lower_borrow`'s special case here where if a borrow is 230 // inserted into a table owned by the instance which implemented the 231 // original resource then no borrow tracking is employed and instead the 232 // `rep` is returned "raw". 233 // 234 // This check is performed by comparing the owning instance of `ty` 235 // against the owning instance of the resource that `ty` is working 236 // with. 237 if self.instance().resource_owned_by_own_instance(ty) { 238 return Ok(rep); 239 } 240 self.resource_tables().guest_resource_lower_borrow(rep, ty) 241 } 242 243 /// Lifts a host-owned `own` resource at the `idx` specified into the 244 /// representation of that resource. host_resource_lift_own(&mut self, idx: HostResourceIndex) -> Result<u32>245 pub fn host_resource_lift_own(&mut self, idx: HostResourceIndex) -> Result<u32> { 246 self.resource_tables().host_resource_lift_own(idx) 247 } 248 249 /// Lifts a host-owned `borrow` resource at the `idx` specified into the 250 /// representation of that resource. host_resource_lift_borrow(&mut self, idx: HostResourceIndex) -> Result<u32>251 pub fn host_resource_lift_borrow(&mut self, idx: HostResourceIndex) -> Result<u32> { 252 self.resource_tables().host_resource_lift_borrow(idx) 253 } 254 255 /// Lowers a resource into the host-owned table, returning the index it was 256 /// inserted at. 257 /// 258 /// Note that this is a special case for `Resource<T>`. Most of the time a 259 /// host value shouldn't be lowered with a lowering context. host_resource_lower_own( &mut self, rep: u32, dtor: Option<NonNull<VMFuncRef>>, instance: Option<RuntimeInstance>, ) -> Result<HostResourceIndex>260 pub fn host_resource_lower_own( 261 &mut self, 262 rep: u32, 263 dtor: Option<NonNull<VMFuncRef>>, 264 instance: Option<RuntimeInstance>, 265 ) -> Result<HostResourceIndex> { 266 self.resource_tables() 267 .host_resource_lower_own(rep, dtor, instance) 268 } 269 270 /// Returns the underlying resource type for the `ty` table specified. resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType271 pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType { 272 self.instance_type().resource_type(ty) 273 } 274 275 /// Returns the instance type information corresponding to the instance that 276 /// this context is lowering into. instance_type(&self) -> InstanceType<'_>277 pub fn instance_type(&self) -> InstanceType<'_> { 278 InstanceType::new(self.instance()) 279 } 280 resource_tables(&mut self) -> HostResourceTables<'_>281 fn resource_tables(&mut self) -> HostResourceTables<'_> { 282 let (tables, data) = self 283 .store 284 .0 285 .component_resource_tables_and_host_resource_data(Some(self.instance)); 286 HostResourceTables::from_parts(tables, data) 287 } 288 289 /// See [`HostResourceTables::validate_scope_exit`]. 290 #[inline] validate_scope_exit(&mut self) -> Result<()>291 pub fn validate_scope_exit(&mut self) -> Result<()> { 292 self.resource_tables().validate_scope_exit() 293 } 294 } 295 296 /// Contextual information used when lifting a type from a component into the 297 /// host. 298 /// 299 /// This structure is the analogue of `LowerContext` except used during lifting 300 /// operations (or loading from memory). 301 #[doc(hidden)] 302 pub struct LiftContext<'a> { 303 store_id: StoreId, 304 /// Like lowering, lifting always has options configured. 305 options: OptionsIndex, 306 307 /// Instance type information, like with lowering. 308 pub types: &'a Arc<ComponentTypes>, 309 310 memory: &'a [u8], 311 312 instance: Pin<&'a mut ComponentInstance>, 313 instance_handle: Instance, 314 315 host_table: &'a mut HandleTable, 316 host_resource_data: &'a mut HostResourceData, 317 318 task_state: &'a mut ComponentTaskState, 319 320 /// Remaining fuel for this hostcall/lift operation. 321 /// 322 /// This is decremented for strings/lists, for example, to cap the size of 323 /// data the host allocates on behalf of the guest. 324 hostcall_fuel: usize, 325 } 326 327 #[doc(hidden)] 328 impl<'a> LiftContext<'a> { 329 /// Creates a new lifting context given the provided context. 330 #[inline] new( store: &'a mut StoreOpaque, options: OptionsIndex, instance_handle: Instance, ) -> LiftContext<'a>331 pub fn new( 332 store: &'a mut StoreOpaque, 333 options: OptionsIndex, 334 instance_handle: Instance, 335 ) -> LiftContext<'a> { 336 let store_id = store.id(); 337 let hostcall_fuel = store.hostcall_fuel(); 338 // From `&mut StoreOpaque` provided the goal here is to project out 339 // three different disjoint fields owned by the store: memory, 340 // `CallContexts`, and `HandleTable`. There's no native API for that 341 // so it's hacked around a bit. This unsafe pointer cast could be fixed 342 // with more methods in more places, but it doesn't seem worth doing it 343 // at this time. 344 let memory = 345 instance_handle.options_memory(unsafe { &*(store as *const StoreOpaque) }, options); 346 let (task_state, host_table, host_resource_data, instance) = 347 store.lift_context_parts(instance_handle); 348 let (component, instance) = instance.component_and_self(); 349 350 LiftContext { 351 store_id, 352 memory, 353 options, 354 types: component.types(), 355 instance, 356 instance_handle, 357 task_state, 358 host_table, 359 host_resource_data, 360 hostcall_fuel, 361 } 362 } 363 364 /// Returns the canonical options that are being used during lifting. options(&self) -> &CanonicalOptions365 pub fn options(&self) -> &CanonicalOptions { 366 &self.instance.component().env_component().options[self.options] 367 } 368 369 /// Returns the `OptionsIndex` being used during lifting. options_index(&self) -> OptionsIndex370 pub fn options_index(&self) -> OptionsIndex { 371 self.options 372 } 373 374 /// Returns the entire contents of linear memory for this set of lifting 375 /// options. 376 /// 377 /// # Panics 378 /// 379 /// This will panic if memory has not been configured for this lifting 380 /// operation. memory(&self) -> &'a [u8]381 pub fn memory(&self) -> &'a [u8] { 382 self.memory 383 } 384 385 /// Returns an identifier for the store from which this `LiftContext` was 386 /// created. store_id(&self) -> StoreId387 pub fn store_id(&self) -> StoreId { 388 self.store_id 389 } 390 391 /// Returns the component instance that is being lifted from. instance_mut(&mut self) -> Pin<&mut ComponentInstance>392 pub fn instance_mut(&mut self) -> Pin<&mut ComponentInstance> { 393 self.instance.as_mut() 394 } 395 /// Returns the component instance that is being lifted from. instance_handle(&self) -> Instance396 pub fn instance_handle(&self) -> Instance { 397 self.instance_handle 398 } 399 400 #[cfg(feature = "component-model-async")] concurrent_state_mut(&mut self) -> &mut ConcurrentState401 pub(crate) fn concurrent_state_mut(&mut self) -> &mut ConcurrentState { 402 self.task_state.concurrent_state_mut() 403 } 404 405 /// Lifts an `own` resource from the guest at the `idx` specified into its 406 /// representation. 407 /// 408 /// Additionally returns a destructor/instance flags to go along with the 409 /// representation so the host knows how to destroy this resource. guest_resource_lift_own( &mut self, ty: TypeResourceTableIndex, idx: u32, ) -> Result<(u32, Option<NonNull<VMFuncRef>>, Option<RuntimeInstance>)>410 pub fn guest_resource_lift_own( 411 &mut self, 412 ty: TypeResourceTableIndex, 413 idx: u32, 414 ) -> Result<(u32, Option<NonNull<VMFuncRef>>, Option<RuntimeInstance>)> { 415 let idx = self.resource_tables().guest_resource_lift_own(idx, ty)?; 416 let (dtor, instance) = self.instance.dtor_and_instance(ty); 417 Ok((idx, dtor, instance)) 418 } 419 420 /// Lifts a `borrow` resource from the guest at the `idx` specified. guest_resource_lift_borrow( &mut self, ty: TypeResourceTableIndex, idx: u32, ) -> Result<u32>421 pub fn guest_resource_lift_borrow( 422 &mut self, 423 ty: TypeResourceTableIndex, 424 idx: u32, 425 ) -> Result<u32> { 426 self.resource_tables().guest_resource_lift_borrow(idx, ty) 427 } 428 429 /// Lowers a resource into the host-owned table, returning the index it was 430 /// inserted at. host_resource_lower_own( &mut self, rep: u32, dtor: Option<NonNull<VMFuncRef>>, instance: Option<RuntimeInstance>, ) -> Result<HostResourceIndex>431 pub fn host_resource_lower_own( 432 &mut self, 433 rep: u32, 434 dtor: Option<NonNull<VMFuncRef>>, 435 instance: Option<RuntimeInstance>, 436 ) -> Result<HostResourceIndex> { 437 self.resource_tables() 438 .host_resource_lower_own(rep, dtor, instance) 439 } 440 441 /// Lowers a resource into the host-owned table, returning the index it was 442 /// inserted at. host_resource_lower_borrow(&mut self, rep: u32) -> Result<HostResourceIndex>443 pub fn host_resource_lower_borrow(&mut self, rep: u32) -> Result<HostResourceIndex> { 444 self.resource_tables().host_resource_lower_borrow(rep) 445 } 446 447 /// Returns the underlying type of the resource table specified by `ty`. resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType448 pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType { 449 self.instance_type().resource_type(ty) 450 } 451 452 /// Returns instance type information for the component instance that is 453 /// being lifted from. instance_type(&self) -> InstanceType<'_>454 pub fn instance_type(&self) -> InstanceType<'_> { 455 InstanceType::new(&self.instance) 456 } 457 resource_tables(&mut self) -> HostResourceTables<'_>458 fn resource_tables(&mut self) -> HostResourceTables<'_> { 459 HostResourceTables::from_parts( 460 ResourceTables { 461 host_table: self.host_table, 462 task_state: self.task_state, 463 guest: Some(self.instance.as_mut().instance_states()), 464 }, 465 self.host_resource_data, 466 ) 467 } 468 469 /// See [`HostResourceTables::validate_scope_exit`]. 470 #[inline] validate_scope_exit(&mut self) -> Result<()>471 pub fn validate_scope_exit(&mut self) -> Result<()> { 472 self.resource_tables().validate_scope_exit() 473 } 474 475 /// Consumes `amt` units of fuel, typically a number of bytes, from this 476 /// context. 477 /// 478 /// Returns an error if the fuel is exhausted which will cause a trap in the 479 /// guest. Note that this is distinct from Wasm's fuel, this is just for 480 /// keeping track of data flowing from the guest to the host. consume_fuel(&mut self, amt: usize) -> Result<()>481 pub fn consume_fuel(&mut self, amt: usize) -> Result<()> { 482 match self.hostcall_fuel.checked_sub(amt) { 483 Some(new) => self.hostcall_fuel = new, 484 None => bail!(HostcallFuelExhausted), 485 } 486 Ok(()) 487 } 488 489 /// Same as [`Self::consume_fuel`], but safely multiplies `len` and `size` 490 /// together before calling that. consume_fuel_array(&mut self, len: usize, size: usize) -> Result<()>491 pub fn consume_fuel_array(&mut self, len: usize, size: usize) -> Result<()> { 492 match len.checked_mul(size) { 493 Some(bytes) => self.consume_fuel(bytes), 494 None => bail!(HostcallFuelExhausted), 495 } 496 } 497 } 498 499 #[derive(Debug)] 500 struct HostcallFuelExhausted; 501 502 impl fmt::Display for HostcallFuelExhausted { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result503 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 504 write!( 505 f, 506 "too much data is being copied between the host and the guest: \ 507 fuel allocated for hostcalls has been exhausted" 508 ) 509 } 510 } 511 512 impl core::error::Error for HostcallFuelExhausted {} 513