From 213edc11e7d464f90e2b52bd0391f003e4dacb7d Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Fri, 29 Mar 2024 16:38:07 -0700 Subject: [PATCH 1/6] Fuzzing: upgrade to latest deps and fix build. Fixes #7. --- fuzz/Cargo.toml | 6 +- fuzz/fuzz_targets/differential.rs | 206 +++++++++++++++--------------- fuzz/fuzz_targets/opt_diff.rs | 119 +++++++++-------- src/fuzzing.rs | 93 +++++--------- 4 files changed, 196 insertions(+), 228 deletions(-) diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index b5e8748..4da48f2 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -11,11 +11,11 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" -wasm-smith = "0.8" +wasm-smith = "0.202.0" env_logger = "0.9" log = "0.4" -wasmparser = "0.95" -wasmtime = "7.0" +wasmparser = "0.202.0" +wasmtime = "19.0" [dependencies.waffle] path = ".." diff --git a/fuzz/fuzz_targets/differential.rs b/fuzz/fuzz_targets/differential.rs index b72af67..4f86ec3 100644 --- a/fuzz/fuzz_targets/differential.rs +++ b/fuzz/fuzz_targets/differential.rs @@ -4,114 +4,110 @@ use std::sync::atomic::{AtomicU64, Ordering}; use waffle::{FrontendOptions, Module}; -fuzz_target!( - |module: wasm_smith::ConfiguredModule| { - let _ = env_logger::try_init(); - log::debug!("original module: {:?}", module.module); +fuzz_target!(|module: waffle::fuzzing::ArbitraryModule| { + let module = module.0; + let _ = env_logger::try_init(); + log::debug!("original module: {:?}", module); - let orig_bytes = module.module.to_bytes(); + let orig_bytes = module.to_bytes(); - if waffle::fuzzing::reject(&orig_bytes[..]) { - log::debug!("Discarding fuzz run. Body:\n{:?}", module); - return; - } else { - log::info!("body: {:?}", module); - } - - let mut config = wasmtime::Config::default(); - config.consume_fuel(true); - let engine = wasmtime::Engine::new(&config).unwrap(); - let orig_module = - wasmtime::Module::new(&engine, &orig_bytes[..]).expect("failed to parse original wasm"); - let mut orig_store = wasmtime::Store::new(&engine, ()); - orig_store.out_of_fuel_trap(); - orig_store.add_fuel(10000).unwrap(); - let orig_instance = wasmtime::Instance::new(&mut orig_store, &orig_module, &[]); - let orig_instance = match orig_instance { - Ok(orig_instance) => orig_instance, - Err(e) => { - log::info!("cannot run start on orig intsance ({:?}); discarding", e); - return; - } - }; - - let mut parsed_module = - Module::from_wasm_bytes(&orig_bytes[..], &FrontendOptions::default()).unwrap(); - parsed_module.expand_all_funcs().unwrap(); - parsed_module.per_func_body(|body| body.optimize()); - let roundtrip_bytes = parsed_module.to_wasm_bytes().unwrap(); - - if let Ok(filename) = std::env::var("FUZZ_DUMP_WASM") { - std::fs::write(format!("{}_orig.wasm", filename), &orig_bytes[..]).unwrap(); - std::fs::write(format!("{}_roundtrip.wasm", filename), &roundtrip_bytes[..]).unwrap(); - } - - let total = TOTAL.fetch_add(1, Ordering::Relaxed); - - let roundtrip_module = wasmtime::Module::new(&engine, &roundtrip_bytes[..]) - .expect("failed to parse roundtripped wasm"); - let mut roundtrip_store = wasmtime::Store::new(&engine, ()); - roundtrip_store.out_of_fuel_trap(); - // After roundtrip, fuel consumption rate may differ. That's fine; - // what matters is that it terminated above without a trap (hence - // halts in a reasonable time). - roundtrip_store.add_fuel(u64::MAX).unwrap(); - let roundtrip_instance = - wasmtime::Instance::new(&mut roundtrip_store, &roundtrip_module, &[]) - .expect("cannot instantiate roundtripped wasm"); - - // Ensure exports are equal. - - let a_globals: Vec<_> = orig_instance - .exports(&mut orig_store) - .filter_map(|e| e.into_global()) - .collect(); - let a_globals: Vec = a_globals - .into_iter() - .map(|g| g.get(&mut orig_store)) - .collect(); - let a_mems: Vec = orig_instance - .exports(&mut orig_store) - .filter_map(|e| e.into_memory()) - .collect(); - - let b_globals: Vec<_> = roundtrip_instance - .exports(&mut roundtrip_store) - .filter_map(|e| e.into_global()) - .collect(); - let b_globals: Vec = b_globals - .into_iter() - .map(|g| g.get(&mut roundtrip_store)) - .collect(); - let b_mems: Vec = roundtrip_instance - .exports(&mut roundtrip_store) - .filter_map(|e| e.into_memory()) - .collect(); - - log::info!("a_globals = {:?}", a_globals); - log::info!("b_globals = {:?}", b_globals); - - assert_eq!(a_globals.len(), b_globals.len()); - for (a, b) in a_globals.into_iter().zip(b_globals.into_iter()) { - match (a, b) { - (wasmtime::Val::I32(a), wasmtime::Val::I32(b)) => assert_eq!(a, b), - (wasmtime::Val::I64(a), wasmtime::Val::I64(b)) => assert_eq!(a, b), - (wasmtime::Val::F32(a), wasmtime::Val::F32(b)) => assert_eq!(a, b), - (wasmtime::Val::F64(a), wasmtime::Val::F64(b)) => assert_eq!(a, b), - _ => panic!("mismatching types"), - } - } - - assert_eq!(a_mems.len(), b_mems.len()); - for (a, b) in a_mems.into_iter().zip(b_mems.into_iter()) { - let a_data = a.data(&orig_store); - let b_data = b.data(&roundtrip_store); - assert_eq!(a_data, b_data); - } - - success(total); + if waffle::fuzzing::reject(&orig_bytes[..]) { + log::debug!("Discarding fuzz run. Body:\n{:?}", module); + return; + } else { + log::info!("body: {:?}", module); } -); + + let mut config = wasmtime::Config::default(); + config.consume_fuel(true); + let engine = wasmtime::Engine::new(&config).unwrap(); + let orig_module = + wasmtime::Module::new(&engine, &orig_bytes[..]).expect("failed to parse original wasm"); + let mut orig_store = wasmtime::Store::new(&engine, ()); + orig_store.set_fuel(10000).unwrap(); + let orig_instance = wasmtime::Instance::new(&mut orig_store, &orig_module, &[]); + let orig_instance = match orig_instance { + Ok(orig_instance) => orig_instance, + Err(e) => { + log::info!("cannot run start on orig intsance ({:?}); discarding", e); + return; + } + }; + + let mut parsed_module = + Module::from_wasm_bytes(&orig_bytes[..], &FrontendOptions::default()).unwrap(); + parsed_module.expand_all_funcs().unwrap(); + parsed_module.per_func_body(|body| body.optimize()); + let roundtrip_bytes = parsed_module.to_wasm_bytes().unwrap(); + + if let Ok(filename) = std::env::var("FUZZ_DUMP_WASM") { + std::fs::write(format!("{}_orig.wasm", filename), &orig_bytes[..]).unwrap(); + std::fs::write(format!("{}_roundtrip.wasm", filename), &roundtrip_bytes[..]).unwrap(); + } + + let total = TOTAL.fetch_add(1, Ordering::Relaxed); + + let roundtrip_module = wasmtime::Module::new(&engine, &roundtrip_bytes[..]) + .expect("failed to parse roundtripped wasm"); + let mut roundtrip_store = wasmtime::Store::new(&engine, ()); + // After roundtrip, fuel consumption rate may differ. That's fine; + // what matters is that it terminated above without a trap (hence + // halts in a reasonable time). + roundtrip_store.set_fuel(u64::MAX).unwrap(); + let roundtrip_instance = wasmtime::Instance::new(&mut roundtrip_store, &roundtrip_module, &[]) + .expect("cannot instantiate roundtripped wasm"); + + // Ensure exports are equal. + + let a_globals: Vec<_> = orig_instance + .exports(&mut orig_store) + .filter_map(|e| e.into_global()) + .collect(); + let a_globals: Vec = a_globals + .into_iter() + .map(|g| g.get(&mut orig_store)) + .collect(); + let a_mems: Vec = orig_instance + .exports(&mut orig_store) + .filter_map(|e| e.into_memory()) + .collect(); + + let b_globals: Vec<_> = roundtrip_instance + .exports(&mut roundtrip_store) + .filter_map(|e| e.into_global()) + .collect(); + let b_globals: Vec = b_globals + .into_iter() + .map(|g| g.get(&mut roundtrip_store)) + .collect(); + let b_mems: Vec = roundtrip_instance + .exports(&mut roundtrip_store) + .filter_map(|e| e.into_memory()) + .collect(); + + log::info!("a_globals = {:?}", a_globals); + log::info!("b_globals = {:?}", b_globals); + + assert_eq!(a_globals.len(), b_globals.len()); + for (a, b) in a_globals.into_iter().zip(b_globals.into_iter()) { + match (a, b) { + (wasmtime::Val::I32(a), wasmtime::Val::I32(b)) => assert_eq!(a, b), + (wasmtime::Val::I64(a), wasmtime::Val::I64(b)) => assert_eq!(a, b), + (wasmtime::Val::F32(a), wasmtime::Val::F32(b)) => assert_eq!(a, b), + (wasmtime::Val::F64(a), wasmtime::Val::F64(b)) => assert_eq!(a, b), + _ => panic!("mismatching types"), + } + } + + assert_eq!(a_mems.len(), b_mems.len()); + for (a, b) in a_mems.into_iter().zip(b_mems.into_iter()) { + let a_data = a.data(&orig_store); + let b_data = b.data(&roundtrip_store); + assert_eq!(a_data, b_data); + } + + success(total); +}); static TOTAL: AtomicU64 = AtomicU64::new(0); static SUCCESS: AtomicU64 = AtomicU64::new(0); diff --git a/fuzz/fuzz_targets/opt_diff.rs b/fuzz/fuzz_targets/opt_diff.rs index cee3e11..524bbc8 100644 --- a/fuzz/fuzz_targets/opt_diff.rs +++ b/fuzz/fuzz_targets/opt_diff.rs @@ -3,66 +3,65 @@ use libfuzzer_sys::fuzz_target; use waffle::{FrontendOptions, InterpContext, InterpResult, Module}; -fuzz_target!( - |module: wasm_smith::ConfiguredModule| { - let _ = env_logger::try_init(); - log::debug!("original module: {:?}", module.module); +fuzz_target!(|module: waffle::fuzzing::ArbitraryModule| { + let module = module.0; + let _ = env_logger::try_init(); + log::debug!("original module: {:?}", module); - let orig_bytes = module.module.to_bytes(); + let orig_bytes = module.to_bytes(); - if waffle::fuzzing::reject(&orig_bytes[..]) { - log::debug!("Discarding fuzz run. Body:\n{:?}", module); - return; - } else { - log::info!("body: {:?}", module); - } - - let mut parsed_module = - Module::from_wasm_bytes(&orig_bytes[..], &FrontendOptions::default()).unwrap(); - parsed_module.expand_all_funcs().unwrap(); - - let start = parsed_module.start_func.unwrap(); - - let mut orig_ctx = match InterpContext::new(&parsed_module) { - Ok(ctx) => ctx, - Err(e) => { - log::trace!("Rejecting due to instantiation error: {:?}", e); - return; - } - }; - orig_ctx.fuel = 10000; - - match orig_ctx.call(&parsed_module, start, &[]) { - InterpResult::OutOfFuel => { - // Silently reject. - log::trace!("Rejecting due to timeout in orig"); - return; - } - InterpResult::Trap(..) => { - // Silently reject. - log::trace!("Rejecting due to trap in orig"); - return; - } - InterpResult::Ok(_) => {} - ret => panic!("Bad result: {:?}", ret), - } - - let mut opt_module = parsed_module.clone(); - opt_module.per_func_body(|body| body.optimize()); - opt_module.per_func_body(|body| body.convert_to_max_ssa(None)); - - let mut opt_ctx = InterpContext::new(&opt_module).unwrap(); - // Allow a little leeway for opts to not actually optimize. - opt_ctx.fuel = 20000; - opt_ctx.call(&opt_module, start, &[]).ok().unwrap(); - - log::trace!( - "Orig ran in {} fuel; opt ran in {} fuel", - 10000 - orig_ctx.fuel, - 20000 - opt_ctx.fuel - ); - - assert_eq!(orig_ctx.memories, opt_ctx.memories); - assert_eq!(orig_ctx.globals, opt_ctx.globals); + if waffle::fuzzing::reject(&orig_bytes[..]) { + log::debug!("Discarding fuzz run. Body:\n{:?}", module); + return; + } else { + log::info!("body: {:?}", module); } -); + + let mut parsed_module = + Module::from_wasm_bytes(&orig_bytes[..], &FrontendOptions::default()).unwrap(); + parsed_module.expand_all_funcs().unwrap(); + + let start = parsed_module.start_func.unwrap(); + + let mut orig_ctx = match InterpContext::new(&parsed_module) { + Ok(ctx) => ctx, + Err(e) => { + log::trace!("Rejecting due to instantiation error: {:?}", e); + return; + } + }; + orig_ctx.fuel = 10000; + + match orig_ctx.call(&parsed_module, start, &[]) { + InterpResult::OutOfFuel => { + // Silently reject. + log::trace!("Rejecting due to timeout in orig"); + return; + } + InterpResult::Trap(..) => { + // Silently reject. + log::trace!("Rejecting due to trap in orig"); + return; + } + InterpResult::Ok(_) => {} + ret => panic!("Bad result: {:?}", ret), + } + + let mut opt_module = parsed_module.clone(); + opt_module.per_func_body(|body| body.optimize()); + opt_module.per_func_body(|body| body.convert_to_max_ssa(None)); + + let mut opt_ctx = InterpContext::new(&opt_module).unwrap(); + // Allow a little leeway for opts to not actually optimize. + opt_ctx.fuel = 20000; + opt_ctx.call(&opt_module, start, &[]).ok().unwrap(); + + log::trace!( + "Orig ran in {} fuel; opt ran in {} fuel", + 10000 - orig_ctx.fuel, + 20000 - opt_ctx.fuel + ); + + assert_eq!(orig_ctx.memories, opt_ctx.memories); + assert_eq!(orig_ctx.globals, opt_ctx.globals); +}); diff --git a/src/fuzzing.rs b/src/fuzzing.rs index 3b8e486..4b8a8b9 100644 --- a/src/fuzzing.rs +++ b/src/fuzzing.rs @@ -23,10 +23,10 @@ pub fn reject(bytes: &[u8]) -> bool { wasmparser::Payload::StartSection { .. } => { has_start = true; } - wasmparser::Payload::ExportSection(mut reader) => { - for _ in 0..reader.get_count() { - let e = reader.read().unwrap(); - match &e.kind { + wasmparser::Payload::ExportSection(reader) => { + for export in reader { + let export = export.unwrap(); + match &export.kind { &wasmparser::ExternalKind::Global => { num_globals += 1; } @@ -34,10 +34,10 @@ pub fn reject(bytes: &[u8]) -> bool { } } } - wasmparser::Payload::MemorySection(mut reader) => { - for _ in 0..reader.get_count() { - let m = reader.read().unwrap(); - if m.maximum.is_none() || m.maximum.unwrap() > 100 { + wasmparser::Payload::MemorySection(reader) => { + for mem in reader { + let mem = mem.unwrap(); + if mem.maximum.is_none() || mem.maximum.unwrap() > 100 { return true; } } @@ -53,59 +53,32 @@ pub fn reject(bytes: &[u8]) -> bool { false } +pub fn fuzzing_config() -> wasm_smith::Config { + wasm_smith::Config { + min_funcs: 1, + max_funcs: 1, + min_memories: 1, + max_memories: 1, + min_globals: 10, + max_globals: 10, + min_tables: 0, + max_tables: 0, + min_imports: 0, + max_imports: 0, + min_exports: 12, + max_exports: 12, + allow_start_export: true, + canonicalize_nans: true, + max_memory32_pages: 1, + ..Default::default() + } +} + #[derive(Debug)] -pub struct Config; +pub struct ArbitraryModule(pub wasm_smith::Module); -impl<'a> arbitrary::Arbitrary<'a> for Config { - fn arbitrary(_u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - Ok(Config) - } -} - -impl wasm_smith::Config for Config { - fn min_funcs(&self) -> usize { - 1 - } - fn max_funcs(&self) -> usize { - 1 - } - fn min_memories(&self) -> u32 { - 1 - } - fn max_memories(&self) -> usize { - 1 - } - fn min_globals(&self) -> usize { - 10 - } - fn max_globals(&self) -> usize { - 10 - } - fn min_tables(&self) -> u32 { - 0 - } - fn max_tables(&self) -> usize { - 0 - } - fn min_imports(&self) -> usize { - 0 - } - fn max_imports(&self) -> usize { - 0 - } - fn min_exports(&self) -> usize { - 12 - } - fn max_exports(&self) -> usize { - 12 - } - fn allow_start_export(&self) -> bool { - true - } - fn canonicalize_nans(&self) -> bool { - true - } - fn max_memory_pages(&self, _is_64: bool) -> u64 { - 1 +impl<'a> arbitrary::Arbitrary<'a> for ArbitraryModule { + fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result { + Ok(Self(wasm_smith::Module::new(fuzzing_config(), u)?)) } } From 43da50792256cda83232d10a287974ccd053af89 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Mon, 1 Apr 2024 14:59:29 -0700 Subject: [PATCH 2/6] v128 store-lane ops do not leave a result on the stack. --- src/op_traits.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/op_traits.rs b/src/op_traits.rs index 8d2fadc..9cb1a08 100644 --- a/src/op_traits.rs +++ b/src/op_traits.rs @@ -706,10 +706,10 @@ pub fn op_outputs( Operator::V128Load16Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])), Operator::V128Load32Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])), Operator::V128Load64Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])), - Operator::V128Store8Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])), - Operator::V128Store16Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])), - Operator::V128Store32Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])), - Operator::V128Store64Lane { .. } => Ok(Cow::Borrowed(&[Type::V128])), + Operator::V128Store8Lane { .. } => Ok(Cow::Borrowed(&[])), + Operator::V128Store16Lane { .. } => Ok(Cow::Borrowed(&[])), + Operator::V128Store32Lane { .. } => Ok(Cow::Borrowed(&[])), + Operator::V128Store64Lane { .. } => Ok(Cow::Borrowed(&[])), Operator::V128Const { .. } => Ok(Cow::Borrowed(&[Type::V128])), Operator::I8x16Shuffle { .. } => Ok(Cow::Borrowed(&[Type::V128])), From fbb0a34a25f3e847ea0d98b112c59fc359b5be2c Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Mon, 1 Apr 2024 14:59:39 -0700 Subject: [PATCH 3/6] Version bump to 0.0.26. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 74bf443..58c5a0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "waffle" -version = "0.0.25" +version = "0.0.26" description = "Wasm Analysis Framework For Lightweight Experiments" authors = ["Chris Fallin "] license = "Apache-2.0 WITH LLVM-exception" From 4eaf0ced01d9018b03781bda819107cf2c65de6e Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Tue, 2 Apr 2024 13:20:28 -0700 Subject: [PATCH 4/6] Support RefIsNull operator. --- src/backend/mod.rs | 1 + src/frontend.rs | 1 + src/op_traits.rs | 4 ++++ src/ops.rs | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/backend/mod.rs b/src/backend/mod.rs index 296046b..8685668 100644 --- a/src/backend/mod.rs +++ b/src/backend/mod.rs @@ -913,6 +913,7 @@ impl<'a> WasmFuncBackend<'a> { Operator::CallRef { sig_index } => { Some(wasm_encoder::Instruction::CallRef(sig_index.index() as u32)) } + Operator::RefIsNull => Some(wasm_encoder::Instruction::RefIsNull), Operator::RefFunc { func_index } => { Some(wasm_encoder::Instruction::RefFunc(func_index.index() as u32)) } diff --git a/src/frontend.rs b/src/frontend.rs index e28af19..ec5c1e0 100644 --- a/src/frontend.rs +++ b/src/frontend.rs @@ -1420,6 +1420,7 @@ impl<'a, 'b> FunctionBodyBuilder<'a, 'b> { | wasmparser::Operator::F32x4DemoteF64x2Zero | wasmparser::Operator::F64x2PromoteLowF32x4 | wasmparser::Operator::CallRef { .. } + | wasmparser::Operator::RefIsNull | wasmparser::Operator::RefFunc { .. } => { self.emit(Operator::try_from(&op).unwrap(), loc)? } diff --git a/src/op_traits.rs b/src/op_traits.rs index 9cb1a08..56c4bbc 100644 --- a/src/op_traits.rs +++ b/src/op_traits.rs @@ -482,6 +482,7 @@ pub fn op_inputs( params.push(Type::TypedFuncRef(true, sig_index.index() as u32)); Ok(params.into()) } + Operator::RefIsNull => Ok(vec![op_stack.last().unwrap().0].into()), Operator::RefFunc { .. } => Ok(Cow::Borrowed(&[])), } } @@ -945,6 +946,7 @@ pub fn op_outputs( Operator::CallRef { sig_index } => { Ok(Vec::from(module.signatures[*sig_index].returns.clone()).into()) } + Operator::RefIsNull => Ok(Cow::Borrowed(&[Type::I32])), Operator::RefFunc { func_index } => { let ty = module.funcs[*func_index].sig(); Ok(vec![Type::TypedFuncRef(true, ty.index() as u32)].into()) @@ -1415,6 +1417,7 @@ impl Operator { Operator::F64x2PromoteLowF32x4 => &[], Operator::CallRef { .. } => &[All], + Operator::RefIsNull => &[], Operator::RefFunc { .. } => &[], } } @@ -1906,6 +1909,7 @@ impl std::fmt::Display for Operator { Operator::F64x2PromoteLowF32x4 => write!(f, "f64x2promotelowf32x4")?, Operator::CallRef { sig_index } => write!(f, "call_ref<{}>", sig_index)?, + Operator::RefIsNull => write!(f, "ref_is_null")?, Operator::RefFunc { func_index } => write!(f, "ref_func<{}>", func_index)?, } diff --git a/src/ops.rs b/src/ops.rs index b5634c1..2ba7c66 100644 --- a/src/ops.rs +++ b/src/ops.rs @@ -634,6 +634,7 @@ pub enum Operator { CallRef { sig_index: Signature, }, + RefIsNull, RefFunc { func_index: Func, }, @@ -1269,6 +1270,7 @@ impl<'a, 'b> std::convert::TryFrom<&'b wasmparser::Operator<'a>> for Operator { &wasmparser::Operator::CallRef { type_index } => Ok(Operator::CallRef { sig_index: Signature::from(type_index), }), + &wasmparser::Operator::RefIsNull => Ok(Operator::RefIsNull), &wasmparser::Operator::RefFunc { function_index } => Ok(Operator::RefFunc { func_index: Func::from(function_index), }), From 7b297b5e4743f81bec2a857e3d3eadd5f570fa01 Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Tue, 2 Apr 2024 13:21:07 -0700 Subject: [PATCH 5/6] Add test for nullable funcrefs. --- wasm_tests/non-nullable-funcrefs.wat | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 wasm_tests/non-nullable-funcrefs.wat diff --git a/wasm_tests/non-nullable-funcrefs.wat b/wasm_tests/non-nullable-funcrefs.wat new file mode 100644 index 0000000..f5978f8 --- /dev/null +++ b/wasm_tests/non-nullable-funcrefs.wat @@ -0,0 +1,8 @@ +(module + (type $t (func (param i32) (result i32))) + (func $f1 (param i32) (result i32) local.get 0) + (func $f2 (param i32) (result i32) local.get 0) + (table $t 1 1 (ref null $t)) + (elem $t (i32.const 0) (ref null $t) + (item (ref.func $f1)) + (item (ref.func $f2)))) From ab0cc59c16a56275fc50f3d7bcbd8d09f1d9ae6a Mon Sep 17 00:00:00 2001 From: Chris Fallin Date: Tue, 2 Apr 2024 13:20:38 -0700 Subject: [PATCH 6/6] Version 0.0.27. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 58c5a0a..328b95e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "waffle" -version = "0.0.26" +version = "0.0.27" description = "Wasm Analysis Framework For Lightweight Experiments" authors = ["Chris Fallin "] license = "Apache-2.0 WITH LLVM-exception"