1 use crate::component::matching::InstanceType; 2 use crate::component::resources::{HostResourceData, HostResourceIndex, HostResourceTables}; 3 use crate::component::ResourceType; 4 use crate::prelude::*; 5 use crate::runtime::vm::component::{ 6 CallContexts, ComponentInstance, InstanceFlags, ResourceTable, ResourceTables, 7 }; 8 use crate::runtime::vm::{VMFuncRef, VMMemoryDefinition}; 9 use crate::store::{StoreId, StoreOpaque}; 10 use crate::{FuncType, StoreContextMut}; 11 use alloc::sync::Arc; 12 use core::ptr::NonNull; 13 use wasmtime_environ::component::{ComponentTypes, StringEncoding, TypeResourceTableIndex}; 14 15 /// Runtime representation of canonical ABI options in the component model. 16 /// 17 /// This structure packages up the runtime representation of each option from 18 /// memories to reallocs to string encodings. Note that this is a "standalone" 19 /// structure which has raw pointers internally. This allows it to be created 20 /// out of thin air for a host function import, for example. The `store_id` 21 /// field, however, is what is used to pair this set of options with a store 22 /// reference to actually use the pointers. 23 #[derive(Copy, Clone)] 24 pub struct Options { 25 /// The store from which this options originated from. 26 store_id: StoreId, 27 28 /// An optional pointer for the memory that this set of options is referring 29 /// to. This option is not required to be specified in the canonical ABI 30 /// hence the `Option`. 31 /// 32 /// Note that this pointer cannot be safely dereferenced unless a store, 33 /// verified with `self.store_id`, has the appropriate borrow available. 34 memory: Option<NonNull<VMMemoryDefinition>>, 35 36 /// Similar to `memory` but corresponds to the `canonical_abi_realloc` 37 /// function. 38 /// 39 /// Safely using this pointer has the same restrictions as `memory` above. 40 realloc: Option<NonNull<VMFuncRef>>, 41 42 /// The encoding used for strings, if found. 43 /// 44 /// This defaults to utf-8 but can be changed if necessary. 45 string_encoding: StringEncoding, 46 } 47 48 // The `Options` structure stores raw pointers but they're never used unless a 49 // `Store` is available so this should be threadsafe and largely inherit the 50 // thread-safety story of `Store<T>` itself. 51 unsafe impl Send for Options {} 52 unsafe impl Sync for Options {} 53 54 impl Options { 55 // FIXME(#4311): prevent a ctor where the memory is memory64 56 57 /// Creates a new set of options with the specified components. 58 /// 59 /// # Unsafety 60 /// 61 /// This is unsafety as there is no way to statically verify the validity of 62 /// the arguments. For example pointers must be valid pointers, the 63 /// `StoreId` must be valid for the pointers, etc. 64 pub unsafe fn new( 65 store_id: StoreId, 66 memory: Option<NonNull<VMMemoryDefinition>>, 67 realloc: Option<NonNull<VMFuncRef>>, 68 string_encoding: StringEncoding, 69 ) -> Options { 70 Options { 71 store_id, 72 memory, 73 realloc, 74 string_encoding, 75 } 76 } 77 78 fn realloc<'a, T>( 79 &self, 80 store: &'a mut StoreContextMut<'_, T>, 81 realloc_ty: &FuncType, 82 old: usize, 83 old_size: usize, 84 old_align: u32, 85 new_size: usize, 86 ) -> Result<(&'a mut [u8], usize)> { 87 self.store_id.assert_belongs_to(store.0.id()); 88 89 let realloc = self.realloc.unwrap(); 90 91 let params = ( 92 u32::try_from(old)?, 93 u32::try_from(old_size)?, 94 old_align, 95 u32::try_from(new_size)?, 96 ); 97 98 type ReallocFunc = crate::TypedFunc<(u32, u32, u32, u32), u32>; 99 100 // This call doesn't take any GC refs, and therefore we shouldn't ever 101 // need to GC before entering Wasm. 102 debug_assert!(!ReallocFunc::need_gc_before_call_raw(store.0, ¶ms)); 103 104 // Invoke the wasm malloc function using its raw and statically known 105 // signature. 106 let result = unsafe { ReallocFunc::call_raw(store, realloc_ty, realloc, params)? }; 107 108 if result % old_align != 0 { 109 bail!("realloc return: result not aligned"); 110 } 111 let result = usize::try_from(result)?; 112 113 let memory = self.memory_mut(store.0); 114 115 let result_slice = match memory.get_mut(result..).and_then(|s| s.get_mut(..new_size)) { 116 Some(end) => end, 117 None => bail!("realloc return: beyond end of memory"), 118 }; 119 120 Ok((result_slice, result)) 121 } 122 123 /// Asserts that this function has an associated memory attached to it and 124 /// then returns the slice of memory tied to the lifetime of the provided 125 /// store. 126 pub fn memory<'a>(&self, store: &'a StoreOpaque) -> &'a [u8] { 127 self.store_id.assert_belongs_to(store.id()); 128 129 // The unsafety here is intended to be encapsulated by the two 130 // preceding assertions. Namely we assert that the `store` is the same 131 // as the original store of this `Options`, meaning that we safely have 132 // either a shared reference or a mutable reference (as below) which 133 // means it's safe to view the memory (aka it's not a different store 134 // where our original store is on some other thread or something like 135 // that). 136 // 137 // Additionally the memory itself is asserted to be present as memory 138 // is an optional configuration in canonical ABI options. 139 unsafe { 140 let memory = self.memory.unwrap().as_ref(); 141 core::slice::from_raw_parts(memory.base.as_ptr(), memory.current_length()) 142 } 143 } 144 145 /// Same as above, just `_mut` 146 pub fn memory_mut<'a>(&self, store: &'a mut StoreOpaque) -> &'a mut [u8] { 147 self.store_id.assert_belongs_to(store.id()); 148 149 // See comments in `memory` about the unsafety 150 unsafe { 151 let memory = self.memory.unwrap().as_ref(); 152 core::slice::from_raw_parts_mut(memory.base.as_ptr(), memory.current_length()) 153 } 154 } 155 156 /// Returns the underlying encoding used for strings in this 157 /// lifting/lowering. 158 pub fn string_encoding(&self) -> StringEncoding { 159 self.string_encoding 160 } 161 162 /// Returns the id of the store that this `Options` is connected to. 163 pub fn store_id(&self) -> StoreId { 164 self.store_id 165 } 166 } 167 168 /// A helper structure which is a "package" of the context used during lowering 169 /// values into a component (or storing them into memory). 170 /// 171 /// This type is used by the `Lower` trait extensively and contains any 172 /// contextual information necessary related to the context in which the 173 /// lowering is happening. 174 #[doc(hidden)] 175 pub struct LowerContext<'a, T> { 176 /// Lowering may involve invoking memory allocation functions so part of the 177 /// context here is carrying access to the entire store that wasm is 178 /// executing within. This store serves as proof-of-ability to actually 179 /// execute wasm safely. 180 pub store: StoreContextMut<'a, T>, 181 182 /// Lowering always happens into a function that's been `canon lift`'d or 183 /// `canon lower`'d, both of which specify a set of options for the 184 /// canonical ABI. For example details like string encoding are contained 185 /// here along with which memory pointers are relative to or what the memory 186 /// allocation function is. 187 pub options: &'a Options, 188 189 /// Lowering happens within the context of a component instance and this 190 /// field stores the type information of that component instance. This is 191 /// used for type lookups and general type queries during the 192 /// lifting/lowering process. 193 pub types: &'a ComponentTypes, 194 195 /// A raw unsafe pointer to the component instance that's being lowered 196 /// into. 197 /// 198 /// This pointer is required to be owned by the `store` provided. 199 instance: *mut ComponentInstance, 200 } 201 202 #[doc(hidden)] 203 impl<'a, T> LowerContext<'a, T> { 204 /// Creates a new lowering context from the specified parameters. 205 /// 206 /// # Unsafety 207 /// 208 /// This function is unsafe as it needs to be guaranteed by the caller that 209 /// the `instance` here is valid within `store` and is a valid component 210 /// instance. 211 pub unsafe fn new( 212 store: StoreContextMut<'a, T>, 213 options: &'a Options, 214 types: &'a ComponentTypes, 215 instance: *mut ComponentInstance, 216 ) -> LowerContext<'a, T> { 217 LowerContext { 218 store, 219 options, 220 types, 221 instance, 222 } 223 } 224 225 /// Returns a view into memory as a mutable slice of bytes. 226 /// 227 /// # Panics 228 /// 229 /// This will panic if memory has not been configured for this lowering 230 /// (e.g. it wasn't present during the specification of canonical options). 231 pub fn as_slice_mut(&mut self) -> &mut [u8] { 232 self.options.memory_mut(self.store.0) 233 } 234 235 /// Invokes the memory allocation function (which is style after `realloc`) 236 /// with the specified parameters. 237 /// 238 /// # Panics 239 /// 240 /// This will panic if realloc hasn't been configured for this lowering via 241 /// its canonical options. 242 pub fn realloc( 243 &mut self, 244 old: usize, 245 old_size: usize, 246 old_align: u32, 247 new_size: usize, 248 ) -> Result<usize> { 249 let realloc_func_ty = Arc::clone(unsafe { (*self.instance).realloc_func_ty() }); 250 let realloc_func_ty = realloc_func_ty.downcast_ref::<FuncType>().unwrap(); 251 self.options 252 .realloc( 253 &mut self.store, 254 &realloc_func_ty, 255 old, 256 old_size, 257 old_align, 258 new_size, 259 ) 260 .map(|(_, ptr)| ptr) 261 } 262 263 /// Returns a fixed mutable slice of memory `N` bytes large starting at 264 /// offset `N`, panicking on out-of-bounds. 265 /// 266 /// It should be previously verified that `offset` is in-bounds via 267 /// bounds-checks. 268 /// 269 /// # Panics 270 /// 271 /// This will panic if memory has not been configured for this lowering 272 /// (e.g. it wasn't present during the specification of canonical options). 273 pub fn get<const N: usize>(&mut self, offset: usize) -> &mut [u8; N] { 274 // FIXME: this bounds check shouldn't actually be necessary, all 275 // callers of `ComponentType::store` have already performed a bounds 276 // check so we're guaranteed that `offset..offset+N` is in-bounds. That 277 // being said we at least should do bounds checks in debug mode and 278 // it's not clear to me how to easily structure this so that it's 279 // "statically obvious" the bounds check isn't necessary. 280 // 281 // For now I figure we can leave in this bounds check and if it becomes 282 // an issue we can optimize further later, probably with judicious use 283 // of `unsafe`. 284 self.as_slice_mut()[offset..].first_chunk_mut().unwrap() 285 } 286 287 /// Lowers an `own` resource into the guest, converting the `rep` specified 288 /// into a guest-local index. 289 /// 290 /// The `ty` provided is which table to put this into. 291 pub fn guest_resource_lower_own( 292 &mut self, 293 ty: TypeResourceTableIndex, 294 rep: u32, 295 ) -> Result<u32> { 296 self.resource_tables().guest_resource_lower_own(rep, ty) 297 } 298 299 /// Lowers a `borrow` resource into the guest, converting the `rep` to a 300 /// guest-local index in the `ty` table specified. 301 pub fn guest_resource_lower_borrow( 302 &mut self, 303 ty: TypeResourceTableIndex, 304 rep: u32, 305 ) -> Result<u32> { 306 // Implement `lower_borrow`'s special case here where if a borrow is 307 // inserted into a table owned by the instance which implemented the 308 // original resource then no borrow tracking is employed and instead the 309 // `rep` is returned "raw". 310 // 311 // This check is performed by comparing the owning instance of `ty` 312 // against the owning instance of the resource that `ty` is working 313 // with. 314 // 315 // Note that the unsafety here should be valid given the contract of 316 // `LowerContext::new`. 317 if unsafe { (*self.instance).resource_owned_by_own_instance(ty) } { 318 return Ok(rep); 319 } 320 self.resource_tables().guest_resource_lower_borrow(rep, ty) 321 } 322 323 /// Lifts a host-owned `own` resource at the `idx` specified into the 324 /// representation of that resource. 325 pub fn host_resource_lift_own(&mut self, idx: HostResourceIndex) -> Result<u32> { 326 self.resource_tables().host_resource_lift_own(idx) 327 } 328 329 /// Lifts a host-owned `borrow` resource at the `idx` specified into the 330 /// representation of that resource. 331 pub fn host_resource_lift_borrow(&mut self, idx: HostResourceIndex) -> Result<u32> { 332 self.resource_tables().host_resource_lift_borrow(idx) 333 } 334 335 /// Lowers a resource into the host-owned table, returning the index it was 336 /// inserted at. 337 /// 338 /// Note that this is a special case for `Resource<T>`. Most of the time a 339 /// host value shouldn't be lowered with a lowering context. 340 pub fn host_resource_lower_own( 341 &mut self, 342 rep: u32, 343 dtor: Option<NonNull<VMFuncRef>>, 344 flags: Option<InstanceFlags>, 345 ) -> Result<HostResourceIndex> { 346 self.resource_tables() 347 .host_resource_lower_own(rep, dtor, flags) 348 } 349 350 /// Returns the underlying resource type for the `ty` table specified. 351 pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType { 352 self.instance_type().resource_type(ty) 353 } 354 355 /// Returns the instance type information corresponding to the instance that 356 /// this context is lowering into. 357 pub fn instance_type(&self) -> InstanceType<'_> { 358 // Note that the unsafety here should be valid given the contract of 359 // `LowerContext::new`. 360 InstanceType::new(unsafe { &*self.instance }) 361 } 362 363 fn resource_tables(&mut self) -> HostResourceTables<'_> { 364 let (calls, host_table, host_resource_data) = self.store.0.component_resource_state(); 365 HostResourceTables::from_parts( 366 ResourceTables { 367 host_table: Some(host_table), 368 calls, 369 // Note that the unsafety here should be valid given the contract of 370 // `LowerContext::new`. 371 tables: Some(unsafe { (*self.instance).component_resource_tables() }), 372 }, 373 host_resource_data, 374 ) 375 } 376 377 /// See [`HostResourceTables::enter_call`]. 378 #[inline] 379 pub fn enter_call(&mut self) { 380 self.resource_tables().enter_call() 381 } 382 383 /// See [`HostResourceTables::exit_call`]. 384 #[inline] 385 pub fn exit_call(&mut self) -> Result<()> { 386 self.resource_tables().exit_call() 387 } 388 } 389 390 /// Contextual information used when lifting a type from a component into the 391 /// host. 392 /// 393 /// This structure is the analogue of `LowerContext` except used during lifting 394 /// operations (or loading from memory). 395 #[doc(hidden)] 396 pub struct LiftContext<'a> { 397 /// Like lowering, lifting always has options configured. 398 pub options: &'a Options, 399 400 /// Instance type information, like with lowering. 401 pub types: &'a Arc<ComponentTypes>, 402 403 memory: Option<&'a [u8]>, 404 405 instance: *mut ComponentInstance, 406 407 host_table: &'a mut ResourceTable, 408 host_resource_data: &'a mut HostResourceData, 409 410 calls: &'a mut CallContexts, 411 } 412 413 #[doc(hidden)] 414 impl<'a> LiftContext<'a> { 415 /// Creates a new lifting context given the provided context. 416 /// 417 /// # Unsafety 418 /// 419 /// This is unsafe for the same reasons as `LowerContext::new` where the 420 /// validity of `instance` is required to be upheld by the caller. 421 #[inline] 422 pub unsafe fn new( 423 store: &'a mut StoreOpaque, 424 options: &'a Options, 425 types: &'a Arc<ComponentTypes>, 426 instance: *mut ComponentInstance, 427 ) -> LiftContext<'a> { 428 // From `&mut StoreOpaque` provided the goal here is to project out 429 // three different disjoint fields owned by the store: memory, 430 // `CallContexts`, and `ResourceTable`. There's no native API for that 431 // so it's hacked around a bit. This unsafe pointer cast could be fixed 432 // with more methods in more places, but it doesn't seem worth doing it 433 // at this time. 434 let (calls, host_table, host_resource_data) = 435 (&mut *(store as *mut StoreOpaque)).component_resource_state(); 436 let memory = options.memory.map(|_| options.memory(store)); 437 438 LiftContext { 439 memory, 440 options, 441 types, 442 instance, 443 calls, 444 host_table, 445 host_resource_data, 446 } 447 } 448 449 /// Returns the entire contents of linear memory for this set of lifting 450 /// options. 451 /// 452 /// # Panics 453 /// 454 /// This will panic if memory has not been configured for this lifting 455 /// operation. 456 pub fn memory(&self) -> &'a [u8] { 457 self.memory.unwrap() 458 } 459 460 /// Returns an identifier for the store from which this `LiftContext` was 461 /// created. 462 pub fn store_id(&self) -> StoreId { 463 self.options.store_id 464 } 465 466 /// Returns the component instance raw pointer that is being lifted from. 467 pub fn instance_ptr(&self) -> *mut ComponentInstance { 468 self.instance 469 } 470 471 /// Lifts an `own` resource from the guest at the `idx` specified into its 472 /// representation. 473 /// 474 /// Additionally returns a destructor/instance flags to go along with the 475 /// representation so the host knows how to destroy this resource. 476 pub fn guest_resource_lift_own( 477 &mut self, 478 ty: TypeResourceTableIndex, 479 idx: u32, 480 ) -> Result<(u32, Option<NonNull<VMFuncRef>>, Option<InstanceFlags>)> { 481 let idx = self.resource_tables().guest_resource_lift_own(idx, ty)?; 482 // Note that the unsafety here should be valid given the contract of 483 // `LiftContext::new`. 484 let (dtor, flags) = unsafe { (*self.instance).dtor_and_flags(ty) }; 485 Ok((idx, dtor, flags)) 486 } 487 488 /// Lifts a `borrow` resource from the guest at the `idx` specified. 489 pub fn guest_resource_lift_borrow( 490 &mut self, 491 ty: TypeResourceTableIndex, 492 idx: u32, 493 ) -> Result<u32> { 494 self.resource_tables().guest_resource_lift_borrow(idx, ty) 495 } 496 497 /// Lowers a resource into the host-owned table, returning the index it was 498 /// inserted at. 499 pub fn host_resource_lower_own( 500 &mut self, 501 rep: u32, 502 dtor: Option<NonNull<VMFuncRef>>, 503 flags: Option<InstanceFlags>, 504 ) -> Result<HostResourceIndex> { 505 self.resource_tables() 506 .host_resource_lower_own(rep, dtor, flags) 507 } 508 509 /// Lowers a resource into the host-owned table, returning the index it was 510 /// inserted at. 511 pub fn host_resource_lower_borrow(&mut self, rep: u32) -> Result<HostResourceIndex> { 512 self.resource_tables().host_resource_lower_borrow(rep) 513 } 514 515 /// Returns the underlying type of the resource table specified by `ty`. 516 pub fn resource_type(&self, ty: TypeResourceTableIndex) -> ResourceType { 517 self.instance_type().resource_type(ty) 518 } 519 520 /// Returns instance type information for the component instance that is 521 /// being lifted from. 522 pub fn instance_type(&self) -> InstanceType<'_> { 523 // Note that the unsafety here should be valid given the contract of 524 // `LiftContext::new`. 525 InstanceType::new(unsafe { &*self.instance }) 526 } 527 528 fn resource_tables(&mut self) -> HostResourceTables<'_> { 529 HostResourceTables::from_parts( 530 ResourceTables { 531 host_table: Some(self.host_table), 532 calls: self.calls, 533 // Note that the unsafety here should be valid given the contract of 534 // `LiftContext::new`. 535 tables: Some(unsafe { (*self.instance).component_resource_tables() }), 536 }, 537 self.host_resource_data, 538 ) 539 } 540 541 /// See [`HostResourceTables::enter_call`]. 542 #[inline] 543 pub fn enter_call(&mut self) { 544 self.resource_tables().enter_call() 545 } 546 547 /// See [`HostResourceTables::exit_call`]. 548 #[inline] 549 pub fn exit_call(&mut self) -> Result<()> { 550 self.resource_tables().exit_call() 551 } 552 } 553