@ -12,7 +12,9 @@
pub mod dummy ;
use arbitrary ::Arbitrary ;
use dummy ::dummy_imports ;
use log ::debug ;
use std ::cell ::Cell ;
use std ::rc ::Rc ;
use std ::sync ::atomic ::{ AtomicUsize , Ordering ::SeqCst } ;
@ -249,24 +251,8 @@ pub fn differential_execution(
( Val ::I32 ( lhs ) , Val ::I32 ( rhs ) ) if lhs = = rhs = > continue ,
( Val ::I64 ( lhs ) , Val ::I64 ( rhs ) ) if lhs = = rhs = > continue ,
( Val ::V128 ( lhs ) , Val ::V128 ( rhs ) ) if lhs = = rhs = > continue ,
( Val ::F32 ( lhs ) , Val ::F32 ( rhs ) ) = > {
let lhs = f32 ::from_bits ( * lhs ) ;
let rhs = f32 ::from_bits ( * rhs ) ;
if lhs = = rhs | | ( lhs . is_nan ( ) & & rhs . is_nan ( ) ) {
continue ;
} else {
fail ( )
}
}
( Val ::F64 ( lhs ) , Val ::F64 ( rhs ) ) = > {
let lhs = f64 ::from_bits ( * lhs ) ;
let rhs = f64 ::from_bits ( * rhs ) ;
if lhs = = rhs | | ( lhs . is_nan ( ) & & rhs . is_nan ( ) ) {
continue ;
} else {
fail ( )
}
}
( Val ::F32 ( lhs ) , Val ::F32 ( rhs ) ) if f32_equal ( * lhs , * rhs ) = > continue ,
( Val ::F64 ( lhs ) , Val ::F64 ( rhs ) ) if f64_equal ( * lhs , * rhs ) = > continue ,
( Val ::ExternRef ( _ ) , Val ::ExternRef ( _ ) )
| ( Val ::FuncRef ( _ ) , Val ::FuncRef ( _ ) ) = > continue ,
_ = > fail ( ) ,
@ -278,6 +264,18 @@ pub fn differential_execution(
}
}
fn f32_equal ( a : u32 , b : u32 ) -> bool {
let a = f32 ::from_bits ( a ) ;
let b = f32 ::from_bits ( b ) ;
a = = b | | ( a . is_nan ( ) & & b . is_nan ( ) )
}
fn f64_equal ( a : u64 , b : u64 ) -> bool {
let a = f64 ::from_bits ( a ) ;
let b = f64 ::from_bits ( b ) ;
a = = b | | ( a . is_nan ( ) & & b . is_nan ( ) )
}
/// Invoke the given API calls.
pub fn make_api_calls ( api : crate ::generators ::api ::ApiCalls ) {
use crate ::generators ::api ::ApiCall ;
@ -479,3 +477,171 @@ pub fn table_ops(config: crate::generators::Config, ops: crate::generators::tabl
}
}
}
/// Configuration options for wasm-smith such that generated modules always
/// conform to certain specifications.
#[ derive(Default, Debug, Arbitrary) ]
pub struct DifferentialWasmiModuleConfig ;
impl wasm_smith ::Config for DifferentialWasmiModuleConfig {
fn allow_start_export ( & self ) -> bool {
false
}
fn min_funcs ( & self ) -> usize {
1
}
fn max_funcs ( & self ) -> usize {
1
}
fn min_memories ( & self ) -> u32 {
1
}
fn max_memories ( & self ) -> u32 {
1
}
fn max_imports ( & self ) -> usize {
0
}
fn min_exports ( & self ) -> usize {
2
}
fn max_memory_pages ( & self ) -> u32 {
1
}
fn memory_max_size_required ( & self ) -> bool {
true
}
}
/// Perform differential execution between Cranelift and wasmi, diffing the
/// resulting memory image when execution terminates. This relies on the
/// module-under-test to be instrumented to bound the execution time. Invoke
/// with a module generated by `wasm-smith` using the
/// `DiferentialWasmiModuleConfig` configuration type for best results.
///
/// May return `None` if we early-out due to a rejected fuzz config; these
/// should be rare if modules are generated appropriately.
pub fn differential_wasmi_execution ( wasm : & [ u8 ] , config : & crate ::generators ::Config ) -> Option < ( ) > {
crate ::init_fuzzing ( ) ;
// Instantiate wasmi module and instance.
let wasmi_module = wasmi ::Module ::from_buffer ( & wasm [ . . ] ) . ok ( ) ? ;
let wasmi_instance =
wasmi ::ModuleInstance ::new ( & wasmi_module , & wasmi ::ImportsBuilder ::default ( ) ) . ok ( ) ? ;
let wasmi_instance = wasmi_instance . assert_no_start ( ) ;
// TODO(paritytech/wasmi#19): wasmi does not currently canonicalize NaNs. To avoid spurious
// fuzz failures, for now let's fuzz only integer Wasm programs.
if wasmi_module . deny_floating_point ( ) . is_err ( ) {
return None ;
}
// Instantiate wasmtime module and instance.
let mut wasmtime_config = config . to_wasmtime ( ) ;
wasmtime_config . cranelift_nan_canonicalization ( true ) ;
let wasmtime_engine = Engine ::new ( & wasmtime_config ) ;
let wasmtime_store = Store ::new ( & wasmtime_engine ) ;
let wasmtime_module =
Module ::new ( & wasmtime_engine , & wasm ) . expect ( "Wasmtime can compile module" ) ;
let wasmtime_instance = Instance ::new ( & wasmtime_store , & wasmtime_module , & [ ] )
. expect ( "Wasmtime can instantiate module" ) ;
// Introspect wasmtime module to find name of an exported function and of an
// exported memory. Stop when we have one of each. (According to the config
// above, there should be at most one of each.)
let ( func_name , memory_name ) = {
let mut func_name = None ;
let mut memory_name = None ;
for e in wasmtime_module . exports ( ) {
match e . ty ( ) {
wasmtime ::ExternType ::Func ( . . ) = > func_name = Some ( e . name ( ) . to_string ( ) ) ,
wasmtime ::ExternType ::Memory ( . . ) = > memory_name = Some ( e . name ( ) . to_string ( ) ) ,
_ = > { }
}
if func_name . is_some ( ) & & memory_name . is_some ( ) {
break ;
}
}
( func_name ? , memory_name ? )
} ;
let wasmi_mem_export = wasmi_instance . export_by_name ( & memory_name [ . . ] ) . unwrap ( ) ;
let wasmi_mem = wasmi_mem_export . as_memory ( ) . unwrap ( ) ;
let wasmi_main_export = wasmi_instance . export_by_name ( & func_name [ . . ] ) . unwrap ( ) ;
let wasmi_main = wasmi_main_export . as_func ( ) . unwrap ( ) ;
let wasmi_val = wasmi ::FuncInstance ::invoke ( & wasmi_main , & [ ] , & mut wasmi ::NopExternals ) ;
let wasmtime_mem = wasmtime_instance
. get_memory ( & memory_name [ . . ] )
. expect ( "memory export is present" ) ;
let wasmtime_main = wasmtime_instance
. get_func ( & func_name [ . . ] )
. expect ( "function export is present" ) ;
let wasmtime_vals = wasmtime_main . call ( & [ ] ) ;
let wasmtime_val = wasmtime_vals . map ( | v | v . iter ( ) . next ( ) . cloned ( ) ) ;
debug ! (
"Successful execution: wasmi returned {:?}, wasmtime returned {:?}" ,
wasmi_val , wasmtime_val
) ;
let show_wat = | | {
if let Ok ( s ) = wasmprinter ::print_bytes ( & wasm [ . . ] ) {
eprintln ! ( "wat:\n{}\n" , s ) ;
}
} ;
match ( & wasmi_val , & wasmtime_val ) {
( & Ok ( Some ( wasmi ::RuntimeValue ::I32 ( a ) ) ) , & Ok ( Some ( Val ::I32 ( b ) ) ) ) if a = = b = > { }
( & Ok ( Some ( wasmi ::RuntimeValue ::F32 ( a ) ) ) , & Ok ( Some ( Val ::F32 ( b ) ) ) )
if f32_equal ( a . to_bits ( ) , b ) = > { }
( & Ok ( Some ( wasmi ::RuntimeValue ::I64 ( a ) ) ) , & Ok ( Some ( Val ::I64 ( b ) ) ) ) if a = = b = > { }
( & Ok ( Some ( wasmi ::RuntimeValue ::F64 ( a ) ) ) , & Ok ( Some ( Val ::F64 ( b ) ) ) )
if f64_equal ( a . to_bits ( ) , b ) = > { }
( & Ok ( None ) , & Ok ( None ) ) = > { }
( & Err ( _ ) , & Err ( _ ) ) = > { }
_ = > {
show_wat ( ) ;
panic ! (
"Values do not match: wasmi returned {:?}; wasmtime returned {:?}" ,
wasmi_val , wasmtime_val
) ;
}
}
if wasmi_mem . current_size ( ) . 0 ! = wasmtime_mem . size ( ) as usize {
show_wat ( ) ;
panic ! ( "resulting memories are not the same size" ) ;
}
// Wasmi memory may be stored non-contiguously; copy it out to a contiguous chunk.
let mut wasmi_buf : Vec < u8 > = vec ! [ 0 ; wasmtime_mem . data_size ( ) ] ;
wasmi_mem
. get_into ( 0 , & mut wasmi_buf [ . . ] )
. expect ( "can access wasmi memory" ) ;
let wasmtime_slice = unsafe { wasmtime_mem . data_unchecked ( ) } ;
if wasmi_buf . len ( ) > = 64 {
debug ! ( "-> First 64 bytes of wasmi heap: {:?}" , & wasmi_buf [ 0 . . 64 ] ) ;
debug ! (
"-> First 64 bytes of Wasmtime heap: {:?}" ,
& wasmtime_slice [ 0 . . 64 ]
) ;
}
if & wasmi_buf [ . . ] ! = & wasmtime_slice [ . . ] {
show_wat ( ) ;
panic ! ( "memory contents are not equal" ) ;
}
Some ( ( ) )
}