diff --git a/gas/src/main.rs b/gas/src/main.rs index 782e116..c125143 100644 --- a/gas/src/main.rs +++ b/gas/src/main.rs @@ -16,7 +16,9 @@ fn main() { // Loading module let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed"); - let result = wasm_utils::inject_gas_counter(module, &Default::default()); + let result = wasm_utils::inject_gas_counter( + module, &Default::default() + ).expect("Failed to inject gas. Some forbidden opcodes?"); parity_wasm::serialize_to_file(&args[2], result).expect("Module serialization to succeed") } diff --git a/src/gas.rs b/src/gas.rs index fcac023..ed88124 100644 --- a/src/gas.rs +++ b/src/gas.rs @@ -65,7 +65,7 @@ pub fn inject_counter( opcodes: &mut elements::Opcodes, rules: &rules::Set, gas_func: u32, -) { +) -> Result<(), ()> { use parity_wasm::elements::Opcode::*; let mut stack: Vec<(usize, usize)> = Vec::new(); @@ -83,7 +83,7 @@ pub fn inject_counter( let opcode = &opcodes.elements()[cursor]; match *opcode { Block(_) | If(_) | Loop(_) => { - InjectAction::Spawn(rules.process(opcode)) + InjectAction::Spawn(rules.process(opcode)?) }, Else => { InjectAction::IncrementSpawn @@ -92,7 +92,7 @@ pub fn inject_counter( InjectAction::Increment }, _ => { - InjectAction::Continue(rules.process(opcode)) + InjectAction::Continue(rules.process(opcode)?) } } }; @@ -125,9 +125,17 @@ pub fn inject_counter( }, } } + + Ok(()) } -pub fn inject_gas_counter(module: elements::Module, rules: &rules::Set) -> elements::Module { +/// Injects gas counter. +/// +/// Can only fail if encounters operation forbidden by gas rules, +/// in this case it returns error with the original module. +pub fn inject_gas_counter(module: elements::Module, rules: &rules::Set) + -> Result +{ // Injecting gas counting external let mut mbuilder = builder::from_module(module); let import_sig = mbuilder.push_signature( @@ -153,6 +161,7 @@ pub fn inject_gas_counter(module: elements::Module, rules: &rules::Set) -> eleme let gas_func = module.import_count(elements::ImportCountType::Function) as u32 - 1; let total_func = module.functions_space() as u32; let mut need_grow_counter = false; + let mut error = false; // Updating calling addresses (all calls to function index >= `gas_func` should be incremented) for section in module.sections_mut() { @@ -160,7 +169,10 @@ pub fn inject_gas_counter(module: elements::Module, rules: &rules::Set) -> eleme &mut elements::Section::Code(ref mut code_section) => { for ref mut func_body in code_section.bodies_mut() { update_call_index(func_body.code_mut(), gas_func); - inject_counter(func_body.code_mut(), rules, gas_func); + if let Err(_) = inject_counter(func_body.code_mut(), rules, gas_func) { + error = true; + break; + } if rules.grow_cost() > 0 { if inject_grow_counter(func_body.code_mut(), total_func) > 0 { need_grow_counter = true; @@ -190,7 +202,9 @@ pub fn inject_gas_counter(module: elements::Module, rules: &rules::Set) -> eleme } } - if need_grow_counter { add_grow_counter(module, rules, gas_func) } else { module } + if error { return Err(module); } + + if need_grow_counter { Ok(add_grow_counter(module, rules, gas_func)) } else { Ok(module) } } #[cfg(test)] @@ -224,7 +238,7 @@ mod tests { .build() .build(); - let injected_module = inject_gas_counter(module, &rules::Set::default().with_grow_cost(10000)); + let injected_module = inject_gas_counter(module, &rules::Set::default().with_grow_cost(10000)).unwrap(); assert_eq!( &vec![ @@ -280,7 +294,7 @@ mod tests { .build() .build(); - let injected_module = inject_gas_counter(module, &rules::Set::default()); + let injected_module = inject_gas_counter(module, &rules::Set::default()).unwrap(); assert_eq!( &vec![ @@ -322,7 +336,7 @@ mod tests { .build() .build(); - let injected_module = inject_gas_counter(module, &Default::default()); + let injected_module = inject_gas_counter(module, &Default::default()).unwrap(); assert_eq!( &vec![ @@ -364,7 +378,7 @@ mod tests { .build() .build(); - let injected_module = inject_gas_counter(module, &Default::default()); + let injected_module = inject_gas_counter(module, &Default::default()).unwrap(); assert_eq!( &vec![ @@ -417,7 +431,7 @@ mod tests { .build() .build(); - let injected_module = inject_gas_counter(module, &Default::default()); + let injected_module = inject_gas_counter(module, &Default::default()).unwrap(); assert_eq!( &vec![ @@ -479,7 +493,7 @@ mod tests { .build() .build(); - let injected_module = inject_gas_counter(module, &Default::default()); + let injected_module = inject_gas_counter(module, &Default::default()).unwrap(); assert_eq!( &vec![ @@ -507,4 +521,33 @@ mod tests { ); } + + #[test] + fn forbidden() { + use parity_wasm::elements::Opcode::*; + + let module = builder::module() + .global() + .value_type().i32() + .build() + .function() + .signature().param().i32().build() + .body() + .with_opcodes(elements::Opcodes::new( + vec![ + F32Const(555555), + End + ] + )) + .build() + .build() + .build(); + + let rules = rules::Set::default().with_forbidden_floats(); + + if let Err(_) = inject_gas_counter(module, &rules) { } + else { panic!("Should be error because of the forbidden operation")} + + } + } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 015afaa..adce5c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,6 @@ mod symbols; mod logger; mod ext; mod pack; -mod nondeterminism_check; mod runtime_type; pub use optimizer::{optimize, Error as OptimizerError}; @@ -24,5 +23,4 @@ pub use gas::inject_gas_counter; pub use logger::init_log; pub use ext::{externalize, externalize_mem, underscore_funcs, ununderscore_funcs, shrink_unknown_stack}; pub use pack::pack_instance; -pub use nondeterminism_check::is_deterministic; pub use runtime_type::inject_runtime_type; diff --git a/src/nondeterminism_check.rs b/src/nondeterminism_check.rs deleted file mode 100644 index eae6f1a..0000000 --- a/src/nondeterminism_check.rs +++ /dev/null @@ -1,138 +0,0 @@ -use parity_wasm::{elements}; -use parity_wasm::elements::{ Section, Opcode }; -use parity_wasm::elements::Opcode::*; - -fn have_nondeterministic_opcodes (opcodes: &[Opcode]) -> bool { - for opcode in opcodes { - match *opcode { - F32Abs | - F32Neg | - F32Ceil | - F32Floor | - F32Trunc | - F32Nearest | - F32Sqrt | - F32Add | - F32Sub | - F32Mul | - F32Div | - F32Min | - F32Max | - F32Copysign | - F64Abs | - F64Neg | - F64Ceil | - F64Floor | - F64Trunc | - F64Nearest | - F64Sqrt | - F64Add | - F64Sub | - F64Mul | - F64Div | - F64Min | - F64Max | - F64Copysign | - I32TruncSF32 | - I32TruncUF32 | - I32TruncSF64 | - I32TruncUF64 | - I64TruncSF32 | - I64TruncUF32 | - I64TruncSF64 | - I64TruncUF64 | - F32ConvertSI32 | - F32ConvertUI32 | - F32ConvertSI64 | - F32ConvertUI64 | - F32DemoteF64 | - F64ConvertSI32 | - F64ConvertUI32 | - F64ConvertSI64 | - F64ConvertUI64 | - F64PromoteF32 | - I32ReinterpretF32 | - I64ReinterpretF64 | - F32ReinterpretI32 | - F64ReinterpretI64 | - F32Eq | - F32Ne | - F32Lt | - F32Gt | - F32Le | - F32Ge | - F64Eq | - F64Ne | - F64Lt | - F64Gt | - F64Le | - F64Ge - => return true, - _ => continue - } - } - false -} - - - - -pub fn is_deterministic(module: &elements::Module) -> bool { - for section in module.sections() { - match *section { - Section::Code(ref cs) => { - for body in cs.bodies() { - if have_nondeterministic_opcodes(body.code().elements()) { - return false; - } - } - }, - _ => continue - } - } - true -} - -#[cfg(test)] -mod tests { - use parity_wasm::{builder, elements}; - use super::*; - - #[test] - fn nondeterminism_found() { - let module = builder::module() - .function().signature().return_type().f32().build() - .body() - .with_opcodes(elements::Opcodes::new( - vec![ - elements::Opcode::F32Const(1), // unrelated to this test matter - elements::Opcode::F32Const(1), // unrelated to this test matter - elements::Opcode::F32Add, - elements::Opcode::End - ] - )) - .build() - .build() - .build(); - assert_eq!(false, is_deterministic(&module)); - } - - #[test] - fn nondeterminism_not() { - let module = builder::module() - .function().signature().return_type().i32().build() - .body() - .with_opcodes(elements::Opcodes::new( - vec![ - elements::Opcode::I32Const(1), - elements::Opcode::I32Const(1), - elements::Opcode::I32Add, - elements::Opcode::End - ] - )) - .build() - .build() - .build(); - assert_eq!(true, is_deterministic(&module)); - } -} diff --git a/src/rules.rs b/src/rules.rs index a4d739a..31e7cf3 100644 --- a/src/rules.rs +++ b/src/rules.rs @@ -3,6 +3,13 @@ use parity_wasm::elements; pub struct UnknownInstruction; +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum Metering { + Regular, + Forbidden, + Fixed(u32), +} + #[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] pub enum InstructionType { Bit, @@ -12,13 +19,15 @@ pub enum InstructionType { Load, Store, Const, + FloatConst, Local, Global, ControlFlow, IntegerComparsion, FloatComparsion, - Numeric, + Float, Conversion, + FloatConversion, Reinterpretation, Unreachable, Nop, @@ -43,8 +52,9 @@ impl ::std::str::FromStr for InstructionType { "flow" => Ok(InstructionType::ControlFlow), "integer_comp" => Ok(InstructionType::IntegerComparsion), "float_comp" => Ok(InstructionType::FloatComparsion), - "numeric" => Ok(InstructionType::Numeric), + "float" => Ok(InstructionType::Float), "conversion" => Ok(InstructionType::Conversion), + "float_conversion" => Ok(InstructionType::FloatConversion), "reinterpret" => Ok(InstructionType::Reinterpretation), "unreachable" => Ok(InstructionType::Unreachable), "nop" => Ok(InstructionType::Nop), @@ -112,8 +122,9 @@ impl InstructionType { I32Const(_) => InstructionType::Const, I64Const(_) => InstructionType::Const, - F32Const(_) => InstructionType::Const, - F64Const(_) => InstructionType::Const, + + F32Const(_) => InstructionType::FloatConst, + F64Const(_) => InstructionType::FloatConst, I32Eqz => InstructionType::IntegerComparsion, I32Eq => InstructionType::IntegerComparsion, @@ -191,56 +202,57 @@ impl InstructionType { I64Rotl => InstructionType::Bit, I64Rotr => InstructionType::Bit, - F32Abs => InstructionType::Numeric, - F32Neg => InstructionType::Numeric, - F32Ceil => InstructionType::Numeric, - F32Floor => InstructionType::Numeric, - F32Trunc => InstructionType::Numeric, - F32Nearest => InstructionType::Numeric, - F32Sqrt => InstructionType::Numeric, - F32Add => InstructionType::Numeric, - F32Sub => InstructionType::Numeric, - F32Mul => InstructionType::Numeric, - F32Div => InstructionType::Numeric, - F32Min => InstructionType::Numeric, - F32Max => InstructionType::Numeric, - F32Copysign => InstructionType::Numeric, - F64Abs => InstructionType::Numeric, - F64Neg => InstructionType::Numeric, - F64Ceil => InstructionType::Numeric, - F64Floor => InstructionType::Numeric, - F64Trunc => InstructionType::Numeric, - F64Nearest => InstructionType::Numeric, - F64Sqrt => InstructionType::Numeric, - F64Add => InstructionType::Numeric, - F64Sub => InstructionType::Numeric, - F64Mul => InstructionType::Numeric, - F64Div => InstructionType::Numeric, - F64Min => InstructionType::Numeric, - F64Max => InstructionType::Numeric, - F64Copysign => InstructionType::Numeric, + F32Abs => InstructionType::Float, + F32Neg => InstructionType::Float, + F32Ceil => InstructionType::Float, + F32Floor => InstructionType::Float, + F32Trunc => InstructionType::Float, + F32Nearest => InstructionType::Float, + F32Sqrt => InstructionType::Float, + F32Add => InstructionType::Float, + F32Sub => InstructionType::Float, + F32Mul => InstructionType::Float, + F32Div => InstructionType::Float, + F32Min => InstructionType::Float, + F32Max => InstructionType::Float, + F32Copysign => InstructionType::Float, + F64Abs => InstructionType::Float, + F64Neg => InstructionType::Float, + F64Ceil => InstructionType::Float, + F64Floor => InstructionType::Float, + F64Trunc => InstructionType::Float, + F64Nearest => InstructionType::Float, + F64Sqrt => InstructionType::Float, + F64Add => InstructionType::Float, + F64Sub => InstructionType::Float, + F64Mul => InstructionType::Float, + F64Div => InstructionType::Float, + F64Min => InstructionType::Float, + F64Max => InstructionType::Float, + F64Copysign => InstructionType::Float, I32WrapI64 => InstructionType::Conversion, - I32TruncSF32 => InstructionType::Conversion, - I32TruncUF32 => InstructionType::Conversion, - I32TruncSF64 => InstructionType::Conversion, - I32TruncUF64 => InstructionType::Conversion, I64ExtendSI32 => InstructionType::Conversion, I64ExtendUI32 => InstructionType::Conversion, - I64TruncSF32 => InstructionType::Conversion, - I64TruncUF32 => InstructionType::Conversion, - I64TruncSF64 => InstructionType::Conversion, - I64TruncUF64 => InstructionType::Conversion, - F32ConvertSI32 => InstructionType::Conversion, - F32ConvertUI32 => InstructionType::Conversion, - F32ConvertSI64 => InstructionType::Conversion, - F32ConvertUI64 => InstructionType::Conversion, - F32DemoteF64 => InstructionType::Conversion, - F64ConvertSI32 => InstructionType::Conversion, - F64ConvertUI32 => InstructionType::Conversion, - F64ConvertSI64 => InstructionType::Conversion, - F64ConvertUI64 => InstructionType::Conversion, - F64PromoteF32 => InstructionType::Conversion, + + I32TruncSF32 => InstructionType::FloatConversion, + I32TruncUF32 => InstructionType::FloatConversion, + I32TruncSF64 => InstructionType::FloatConversion, + I32TruncUF64 => InstructionType::FloatConversion, + I64TruncSF32 => InstructionType::FloatConversion, + I64TruncUF32 => InstructionType::FloatConversion, + I64TruncSF64 => InstructionType::FloatConversion, + I64TruncUF64 => InstructionType::FloatConversion, + F32ConvertSI32 => InstructionType::FloatConversion, + F32ConvertUI32 => InstructionType::FloatConversion, + F32ConvertSI64 => InstructionType::FloatConversion, + F32ConvertUI64 => InstructionType::FloatConversion, + F32DemoteF64 => InstructionType::FloatConversion, + F64ConvertSI32 => InstructionType::FloatConversion, + F64ConvertUI32 => InstructionType::FloatConversion, + F64ConvertSI64 => InstructionType::FloatConversion, + F64ConvertUI64 => InstructionType::FloatConversion, + F64PromoteF32 => InstructionType::FloatConversion, I32ReinterpretF32 => InstructionType::Reinterpretation, I64ReinterpretF64 => InstructionType::Reinterpretation, @@ -250,19 +262,34 @@ impl InstructionType { } } -#[derive(Debug, Default)] +#[derive(Debug)] pub struct Set { - entries: HashMap, + regular: u32, + entries: HashMap, grow: u32, } +impl Default for Set { + fn default() -> Self { + Set { + regular: 1, + entries: HashMap::new(), + grow: 0, + } + } +} + impl Set { - pub fn new(entries: HashMap) -> Self { - Set { entries: entries, grow: 0, } + pub fn new(regular: u32, entries: HashMap) -> Self { + Set { regular: regular, entries: entries, grow: 0, } } - pub fn process(&self, opcode: &elements::Opcode) -> u32 { - self.entries.get(&InstructionType::op(opcode)).map(|x| *x).unwrap_or(1) + pub fn process(&self, opcode: &elements::Opcode) -> Result { + match self.entries.get(&InstructionType::op(opcode)).map(|x| *x) { + None | Some(Metering::Regular) => Ok(self.regular), + Some(Metering::Forbidden) => Err(()), + Some(Metering::Fixed(val)) => Ok(val), + } } pub fn grow_cost(&self) -> u32 { @@ -273,4 +300,12 @@ impl Set { self.grow = val; self } + + pub fn with_forbidden_floats(mut self) -> Self { + self.entries.insert(InstructionType::Float, Metering::Forbidden); + self.entries.insert(InstructionType::FloatComparsion, Metering::Forbidden); + self.entries.insert(InstructionType::FloatConst, Metering::Forbidden); + self.entries.insert(InstructionType::FloatConversion, Metering::Forbidden); + self + } } \ No newline at end of file