add rules with forbid operations

This commit is contained in:
NikVolf
2018-02-19 19:28:12 +03:00
parent bc475275c1
commit 9e92237c4d
5 changed files with 149 additions and 209 deletions

View File

@ -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")
}

View File

@ -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<elements::Module, elements::Module>
{
// 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")}
}
}

View File

@ -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;

View File

@ -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));
}
}

View File

@ -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<InstructionType, u32>,
regular: u32,
entries: HashMap<InstructionType, Metering>,
grow: u32,
}
impl Default for Set {
fn default() -> Self {
Set {
regular: 1,
entries: HashMap::new(),
grow: 0,
}
}
}
impl Set {
pub fn new(entries: HashMap<InstructionType, u32>) -> Self {
Set { entries: entries, grow: 0, }
pub fn new(regular: u32, entries: HashMap<InstructionType, Metering>) -> 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<u32, ()> {
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
}
}