Skip to content

Commit 01ce52f

Browse files
committed
Fixed validation of instantiation before start fn; add component builtin test
1 parent fd09d72 commit 01ce52f

8 files changed

Lines changed: 371 additions & 191 deletions

File tree

crates/wasmtime/src/runtime/component/instance.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use crate::instance::OwnedImports;
99
use crate::linker::DefinitionType;
1010
use crate::prelude::*;
1111
#[cfg(feature = "rr")]
12-
use crate::rr::{RecordBuffer, component_events::InstantiationEvent};
12+
use crate::rr::RecordBuffer;
13+
use crate::rr::component_hooks;
1314
use crate::runtime::vm::component::{
1415
CallContexts, ComponentInstance, ResourceTables, TypedResource, TypedResourceIndex,
1516
};
@@ -1080,7 +1081,6 @@ impl<'a> Instantiator<'a> {
10801081

10811082
/// Convenience helper to return the instance ID of the `ComponentInstance` that's
10821083
/// being instantiated
1083-
#[cfg(feature = "rr")]
10841084
fn id(&self) -> ComponentInstanceId {
10851085
self.id
10861086
}
@@ -1194,12 +1194,13 @@ impl<T: 'static> InstancePre<T> {
11941194
.increment_component_instance_count()?;
11951195
let mut instantiator = Instantiator::new(&self.component, store.0, &self.imports);
11961196

1197+
// Record/replay hooks
11971198
store.0.validate_rr_config()?;
1198-
#[cfg(feature = "rr")]
1199-
store.0.record_event(|| InstantiationEvent {
1200-
component: *self.component.checksum(),
1201-
instance: instantiator.id(),
1202-
})?;
1199+
component_hooks::record_and_replay_validate_instantiation(
1200+
&mut store,
1201+
*self.component.checksum(),
1202+
instantiator.id(),
1203+
)?;
12031204

12041205
instantiator.run(&mut store).await.map_err(|e| {
12051206
store

crates/wasmtime/src/runtime/instance.rs

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use crate::linker::{Definition, DefinitionType};
22
use crate::prelude::*;
3-
#[cfg(feature = "rr")]
4-
use crate::rr::core_events::InstantiationEvent;
3+
use crate::rr::core_hooks;
54
use crate::runtime::vm::{
65
self, Imports, ModuleRuntimeInfo, VMFuncRef, VMFunctionImport, VMGlobalImport, VMMemoryImport,
76
VMStore, VMTableImport, VMTagImport,
@@ -262,18 +261,18 @@ impl Instance {
262261
unsafe { Instance::new_raw(store, limiter.as_mut(), module, imports).await? }
263262
};
264263

264+
// Record/replay hooks
265265
store.0.validate_rr_config()?;
266266
if !from_component {
267-
#[cfg(feature = "rr")]
268-
{
269-
// Components already record instantiation, so do not record their internal modules
270-
rr_validate_module_unexported_memory(module)?;
271-
store.0.record_event(|| InstantiationEvent {
272-
module: *module.checksum(),
273-
instance: instance.id(),
274-
})?;
275-
}
267+
// Components already record instantiation, so do not record their internal modules
268+
core_hooks::rr_validate_module_unexported_memory(&module)?;
269+
core_hooks::record_and_replay_validate_instantiation(
270+
store,
271+
*module.checksum(),
272+
instance.id(),
273+
)?;
276274
}
275+
277276
if let Some(start) = start {
278277
if store.0.async_support() {
279278
#[cfg(feature = "async")]
@@ -1008,22 +1007,3 @@ fn typecheck<I>(
10081007
}
10091008
Ok(())
10101009
}
1011-
1012-
/// Ensure that memories are not exported memories in Core wasm modules when
1013-
/// recording is enabled
1014-
#[cfg(feature = "rr")]
1015-
fn rr_validate_module_unexported_memory(module: &Module) -> Result<()> {
1016-
// Check for exported memories when recording is enabled.
1017-
if module.engine().is_recording()
1018-
&& module.exports().any(|export| {
1019-
if let crate::ExternType::Memory(_) = export.ty() {
1020-
true
1021-
} else {
1022-
false
1023-
}
1024-
})
1025-
{
1026-
bail!("Cannot support recording for core wasm modules when a memory is exported");
1027-
}
1028-
Ok(())
1029-
}

crates/wasmtime/src/runtime/rr/core.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -576,14 +576,20 @@ mod tests {
576576
use wasmtime_environ::FuncIndex;
577577

578578
impl ReplayBuffer {
579+
/// Pop the next replay event and calls `f` with a expected event type
580+
///
581+
/// ## Errors
582+
///
583+
/// See [`next_event_typed`](Replayer::next_event_typed)
584+
#[inline]
579585
fn next_event_and<T, F>(&mut self, f: F) -> Result<(), ReplayError>
580586
where
581587
T: TryFrom<RREvent>,
582588
ReplayError: From<<T as TryFrom<RREvent>>::Error>,
583589
F: FnOnce(T) -> Result<(), ReplayError>,
584590
{
585-
let event = self.next_event_typed::<T>()?;
586-
f(event)
591+
let call_event = self.next_event_typed()?;
592+
Ok(f(call_event)?)
587593
}
588594
}
589595

crates/wasmtime/src/runtime/rr/hooks.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
/// Component specific RR hooks that use `component-model` feature gating
2-
#[cfg(feature = "component-model")]
3-
pub mod component_hooks;
4-
/// Core RR hooks
5-
pub mod core_hooks;
6-
71
use crate::{FuncType, WasmFuncOrigin};
2+
#[cfg(feature = "rr")]
3+
use crate::{StoreContextMut, rr::ReplayHostContext};
84
#[cfg(feature = "component-model")]
95
use alloc::sync::Arc;
106
#[cfg(feature = "component-model")]
117
use wasmtime_environ::component::{ComponentTypes, TypeFuncIndex};
128

9+
/// Component specific RR hooks that use `component-model` feature gating
10+
#[cfg(feature = "component-model")]
11+
pub mod component_hooks;
12+
/// Core RR hooks
13+
pub mod core_hooks;
14+
1315
/// Wasm function type information for RR hooks
1416
pub enum RRWasmFuncType<'a> {
1517
/// Core RR hooks to be performed
@@ -27,3 +29,29 @@ pub enum RRWasmFuncType<'a> {
2729
#[cfg(feature = "component-model")]
2830
None,
2931
}
32+
33+
/// Obtain the replay host context from the store.
34+
///
35+
/// SAFETY: The store's data is always of type `ReplayHostContext` when created by
36+
/// the replay driver. As an additional guarantee, we assert that replay is indeed
37+
/// truly enabled.
38+
#[cfg(feature = "rr")]
39+
unsafe fn replay_data_from_store<'a, T: 'static>(
40+
store: &StoreContextMut<'a, T>,
41+
) -> &'a ReplayHostContext {
42+
assert!(store.0.replay_enabled());
43+
let raw_ptr: *const T = store.data();
44+
unsafe { &*(raw_ptr as *const ReplayHostContext) }
45+
}
46+
47+
/// Same as [replay_data_from_store], but mutable
48+
///
49+
/// SAFETY: See [replay_data_from_store]
50+
#[cfg(feature = "rr")]
51+
unsafe fn replay_data_from_store_mut<'a, T: 'static>(
52+
store: &mut StoreContextMut<'a, T>,
53+
) -> &'a mut ReplayHostContext {
54+
assert!(store.0.replay_enabled());
55+
let raw_ptr: *mut T = store.data_mut();
56+
unsafe { &mut *(raw_ptr as *mut ReplayHostContext) }
57+
}

crates/wasmtime/src/runtime/rr/hooks/component_hooks.rs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
1+
#[cfg(feature = "rr")]
2+
use super::replay_data_from_store_mut;
13
use crate::ValRaw;
24
use crate::component::{ComponentInstanceId, func::LowerContext};
35
#[cfg(feature = "rr")]
4-
use crate::rr::common_events::{HostFuncEntryEvent, WasmFuncReturnEvent};
6+
use crate::rr::common_events::{HostFuncEntryEvent, HostFuncReturnEvent, WasmFuncReturnEvent};
57
#[cfg(feature = "rr")]
68
use crate::rr::component_events::{
7-
LowerFlatEntryEvent, LowerFlatReturnEvent, LowerMemoryEntryEvent, LowerMemoryReturnEvent,
8-
MemorySliceWriteEvent, PostReturnEvent, WasmFuncBeginEvent, WasmFuncEntryEvent,
9+
InstantiationEvent, LowerFlatEntryEvent, LowerFlatReturnEvent, LowerMemoryEntryEvent,
10+
LowerMemoryReturnEvent, MemorySliceWriteEvent, PostReturnEvent, WasmFuncBeginEvent,
11+
WasmFuncEntryEvent,
912
};
1013
#[cfg(feature = "rr")]
11-
use crate::rr::{
12-
RRFuncArgVals, RecordBuffer, Recorder, ResultEvent, common_events::HostFuncReturnEvent,
13-
};
14+
use crate::rr::{RRFuncArgVals, RecordBuffer, Recorder, ResultEvent, Validate};
1415
use crate::store::StoreOpaque;
1516
use crate::{StoreContextMut, prelude::*};
1617
use alloc::sync::Arc;
1718
use core::mem::MaybeUninit;
1819
use core::ops::{Deref, DerefMut};
20+
use wasmtime_environ::WasmChecksum;
1921
use wasmtime_environ::component::{ComponentTypes, ExportIndex, InterfaceType, TypeFuncIndex};
2022
#[cfg(all(feature = "rr"))]
2123
use wasmtime_environ::component::{MAX_FLAT_PARAMS, MAX_FLAT_RESULTS};
@@ -206,6 +208,31 @@ where
206208
lower_result
207209
}
208210

211+
/// Hook for recording a component instantiation event and validating the
212+
/// instantiation during replay.
213+
#[inline]
214+
pub fn record_and_replay_validate_instantiation<T>(
215+
store: &mut StoreContextMut<'_, T>,
216+
component: WasmChecksum,
217+
instance: ComponentInstanceId,
218+
) -> Result<()> {
219+
#[cfg(feature = "rr")]
220+
{
221+
store.0.record_event(|| InstantiationEvent {
222+
component,
223+
instance,
224+
})?;
225+
if store.0.replay_enabled() {
226+
let replay_data = unsafe { replay_data_from_store_mut(store) };
227+
replay_data.take_current_component_instantiation().expect(
228+
"replay driver should have set component instantiate data before trying to validate it",
229+
).validate(&InstantiationEvent { component, instance })?;
230+
}
231+
}
232+
let _ = (store, component, instance);
233+
Ok(())
234+
}
235+
209236
#[cfg(feature = "rr")]
210237
#[inline(always)]
211238
fn create_host_func_entry_event(

crates/wasmtime/src/runtime/rr/hooks/core_hooks.rs

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1+
#[cfg(feature = "rr")]
2+
use super::{replay_data_from_store, replay_data_from_store_mut};
13
use crate::rr::FlatBytes;
24
#[cfg(feature = "rr")]
35
use crate::rr::{
4-
RREvent, RRFuncArgVals, ReplayError, ReplayHostContext, Replayer, ResultEvent,
6+
RREvent, RRFuncArgVals, ReplayError, Replayer, ResultEvent, Validate,
57
common_events::HostFuncEntryEvent, common_events::HostFuncReturnEvent,
6-
common_events::WasmFuncReturnEvent, core_events::WasmFuncEntryEvent,
8+
common_events::WasmFuncReturnEvent, core_events::InstantiationEvent,
9+
core_events::WasmFuncEntryEvent,
710
};
8-
use crate::store::StoreOpaque;
9-
use crate::{Caller, FuncType, StoreContextMut, ValRaw, WasmFuncOrigin, prelude::*};
11+
use crate::store::{InstanceId, StoreOpaque};
12+
use crate::{Caller, FuncType, Module, StoreContextMut, ValRaw, WasmFuncOrigin, prelude::*};
1013
#[cfg(feature = "rr")]
1114
use wasmtime_environ::EntityIndex;
15+
use wasmtime_environ::WasmChecksum;
1216

1317
/// Record and replay hook operation for core wasm function entry events
1418
///
@@ -121,7 +125,7 @@ where
121125
Ok(())
122126
}
123127

124-
/// Replay hook operation for host function return events
128+
/// Replay hook operation for host function return events.
125129
#[inline]
126130
pub fn replay_host_func_return<T, U: 'static>(
127131
args: &mut [T],
@@ -146,14 +150,9 @@ where
146150
RREvent::CoreWasmFuncEntry(event) => {
147151
let entity = EntityIndex::from(event.origin.index);
148152

149-
// SAFETY: The store's data is always of type `ReplayHostContext<T>` when created by
150-
// the replay driver. As an additional guarantee, we assert that replay is indeed
151-
// truly enabled.
152-
assert!(caller.store.0.replay_enabled());
153-
let replay_data = unsafe {
154-
let raw_ptr: *const U = caller.store.data();
155-
&*(raw_ptr as *const ReplayHostContext)
156-
};
153+
// Unwrapping the `replay_buffer_mut()` above ensures that we are in replay mode
154+
// passing the safety contract for `replay_data_from_store`
155+
let replay_data = unsafe { replay_data_from_store(&caller.store) };
157156

158157
// Grab the correct module instance
159158
let instance = replay_data.get_module_instance(event.origin.instance)?;
@@ -194,3 +193,48 @@ where
194193
let _ = (args, caller);
195194
Ok(())
196195
}
196+
197+
/// Hook for recording a module instantiation event and validating the
198+
/// instantiation during replay.
199+
pub fn record_and_replay_validate_instantiation<T: 'static>(
200+
store: &mut StoreContextMut<'_, T>,
201+
module: WasmChecksum,
202+
instance: InstanceId,
203+
) -> Result<()> {
204+
#[cfg(feature = "rr")]
205+
{
206+
store
207+
.0
208+
.record_event(|| InstantiationEvent { module, instance })?;
209+
if store.0.replay_enabled() {
210+
let replay_data = unsafe { replay_data_from_store_mut(store) };
211+
replay_data.take_current_module_instantiation().expect(
212+
"replay driver should have set module instantiate data before trying to validate it",
213+
).validate(&InstantiationEvent { module, instance })?;
214+
}
215+
}
216+
let _ = (store, module, instance);
217+
Ok(())
218+
}
219+
220+
/// Ensure that memories are not exported memories in Core wasm modules when
221+
/// recording is enabled.
222+
pub fn rr_validate_module_unexported_memory(module: &Module) -> Result<()> {
223+
// Check for exported memories when recording is enabled.
224+
#[cfg(feature = "rr")]
225+
{
226+
if module.engine().is_recording()
227+
&& module.exports().any(|export| {
228+
if let crate::ExternType::Memory(_) = export.ty() {
229+
true
230+
} else {
231+
false
232+
}
233+
})
234+
{
235+
bail!("Cannot support recording for core wasm modules when a memory is exported");
236+
}
237+
}
238+
let _ = module;
239+
Ok(())
240+
}

0 commit comments

Comments
 (0)