mirror of
https://github.com/fluencelabs/wasm-utils
synced 2025-06-30 15:01:39 +00:00
add rules with forbid operations
This commit is contained in:
@ -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")
|
||||
}
|
||||
|
67
src/gas.rs
67
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<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")}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
147
src/rules.rs
147
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<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
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user