mirror of
https://github.com/pezkuwichain/wasm-instrument.git
synced 2026-07-18 18:05:41 +00:00
Make the rules passed to gas metering injection generic
This commit is contained in:
+1
-1
@@ -17,7 +17,7 @@ fn main() {
|
|||||||
let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed");
|
let module = parity_wasm::deserialize_file(&args[1]).expect("Module deserialization to succeed");
|
||||||
|
|
||||||
let result = utils::inject_gas_counter(
|
let result = utils::inject_gas_counter(
|
||||||
module, &Default::default(), "env"
|
module, &utils::rules::Set::default(), "env"
|
||||||
).expect("Failed to inject gas. Some forbidden opcodes?");
|
).expect("Failed to inject gas. Some forbidden opcodes?");
|
||||||
|
|
||||||
parity_wasm::serialize_to_file(&args[2], result).expect("Module serialization to succeed")
|
parity_wasm::serialize_to_file(&args[2], result).expect("Module serialization to succeed")
|
||||||
|
|||||||
+26
-12
@@ -12,7 +12,7 @@ use std::mem;
|
|||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
use parity_wasm::{elements, builder};
|
use parity_wasm::{elements, builder};
|
||||||
use rules;
|
use rules::Rules;
|
||||||
|
|
||||||
pub fn update_call_index(instructions: &mut elements::Instructions, inserted_index: u32) {
|
pub fn update_call_index(instructions: &mut elements::Instructions, inserted_index: u32) {
|
||||||
use parity_wasm::elements::Instruction::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
@@ -234,8 +234,18 @@ fn inject_grow_counter(instructions: &mut elements::Instructions, grow_counter_f
|
|||||||
counter
|
counter
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_grow_counter(module: elements::Module, rules: &rules::Set, gas_func: u32) -> elements::Module {
|
fn add_grow_counter<R: Rules>(
|
||||||
|
module: elements::Module,
|
||||||
|
rules: &R,
|
||||||
|
gas_func: u32
|
||||||
|
) -> elements::Module {
|
||||||
use parity_wasm::elements::Instruction::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
|
use crate::rules::MemoryGrowCost;
|
||||||
|
|
||||||
|
let cost = match rules.memory_grow_cost() {
|
||||||
|
None => return module,
|
||||||
|
Some(MemoryGrowCost::Linear(val)) => val.get(),
|
||||||
|
};
|
||||||
|
|
||||||
let mut b = builder::from_module(module);
|
let mut b = builder::from_module(module);
|
||||||
b.push_function(
|
b.push_function(
|
||||||
@@ -245,7 +255,7 @@ fn add_grow_counter(module: elements::Module, rules: &rules::Set, gas_func: u32)
|
|||||||
.with_instructions(elements::Instructions::new(vec![
|
.with_instructions(elements::Instructions::new(vec![
|
||||||
GetLocal(0),
|
GetLocal(0),
|
||||||
GetLocal(0),
|
GetLocal(0),
|
||||||
I32Const(rules.grow_cost() as i32),
|
I32Const(cost as i32),
|
||||||
I32Mul,
|
I32Mul,
|
||||||
// todo: there should be strong guarantee that it does not return anything on stack?
|
// todo: there should be strong guarantee that it does not return anything on stack?
|
||||||
Call(gas_func),
|
Call(gas_func),
|
||||||
@@ -259,9 +269,9 @@ fn add_grow_counter(module: elements::Module, rules: &rules::Set, gas_func: u32)
|
|||||||
b.build()
|
b.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn determine_metered_blocks(
|
pub(crate) fn determine_metered_blocks<R: Rules>(
|
||||||
instructions: &elements::Instructions,
|
instructions: &elements::Instructions,
|
||||||
rules: &rules::Set,
|
rules: &R,
|
||||||
) -> Result<Vec<MeteredBlock>, ()> {
|
) -> Result<Vec<MeteredBlock>, ()> {
|
||||||
use parity_wasm::elements::Instruction::*;
|
use parity_wasm::elements::Instruction::*;
|
||||||
|
|
||||||
@@ -272,7 +282,7 @@ pub(crate) fn determine_metered_blocks(
|
|||||||
|
|
||||||
for cursor in 0..instructions.elements().len() {
|
for cursor in 0..instructions.elements().len() {
|
||||||
let instruction = &instructions.elements()[cursor];
|
let instruction = &instructions.elements()[cursor];
|
||||||
let instruction_cost = rules.process(instruction)?;
|
let instruction_cost = rules.instruction_cost(instruction).ok_or(())?;
|
||||||
match instruction {
|
match instruction {
|
||||||
Block(_) => {
|
Block(_) => {
|
||||||
counter.increment(instruction_cost)?;
|
counter.increment(instruction_cost)?;
|
||||||
@@ -333,9 +343,9 @@ pub(crate) fn determine_metered_blocks(
|
|||||||
Ok(counter.finalized_blocks)
|
Ok(counter.finalized_blocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inject_counter(
|
pub fn inject_counter<R: Rules>(
|
||||||
instructions: &mut elements::Instructions,
|
instructions: &mut elements::Instructions,
|
||||||
rules: &rules::Set,
|
rules: &R,
|
||||||
gas_func: u32,
|
gas_func: u32,
|
||||||
) -> Result<(), ()> {
|
) -> Result<(), ()> {
|
||||||
let blocks = determine_metered_blocks(instructions, rules)?;
|
let blocks = determine_metered_blocks(instructions, rules)?;
|
||||||
@@ -420,7 +430,11 @@ fn insert_metering_calls(
|
|||||||
///
|
///
|
||||||
/// The function fails if the module contains any operation forbidden by gas rule set, returning
|
/// The function fails if the module contains any operation forbidden by gas rule set, returning
|
||||||
/// the original module as an Err.
|
/// the original module as an Err.
|
||||||
pub fn inject_gas_counter(module: elements::Module, rules: &rules::Set, gas_module_name: &str)
|
pub fn inject_gas_counter<R: Rules>(
|
||||||
|
module: elements::Module,
|
||||||
|
rules: &R,
|
||||||
|
gas_module_name: &str,
|
||||||
|
)
|
||||||
-> Result<elements::Module, elements::Module>
|
-> Result<elements::Module, elements::Module>
|
||||||
{
|
{
|
||||||
// Injecting gas counting external
|
// Injecting gas counting external
|
||||||
@@ -460,7 +474,7 @@ pub fn inject_gas_counter(module: elements::Module, rules: &rules::Set, gas_modu
|
|||||||
error = true;
|
error = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if rules.grow_cost() > 0 && inject_grow_counter(func_body.code_mut(), total_func) > 0 {
|
if rules.memory_grow_cost().is_some() && inject_grow_counter(func_body.code_mut(), total_func) > 0 {
|
||||||
need_grow_counter = true;
|
need_grow_counter = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -636,7 +650,7 @@ mod tests {
|
|||||||
.build()
|
.build()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let injected_module = inject_gas_counter(module, &Default::default(), "env").unwrap();
|
let injected_module = inject_gas_counter(module, &rules::Set::default(), "env").unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
get_function_body(&injected_module, 1).unwrap(),
|
get_function_body(&injected_module, 1).unwrap(),
|
||||||
@@ -705,7 +719,7 @@ mod tests {
|
|||||||
let input_module = parse_wat($input);
|
let input_module = parse_wat($input);
|
||||||
let expected_module = parse_wat($expected);
|
let expected_module = parse_wat($expected);
|
||||||
|
|
||||||
let injected_module = inject_gas_counter(input_module, &Default::default(), "env")
|
let injected_module = inject_gas_counter(input_module, &rules::Set::default(), "env")
|
||||||
.expect("inject_gas_counter call failed");
|
.expect("inject_gas_counter call failed");
|
||||||
|
|
||||||
let actual_func_body = get_function_body(&injected_module, 0)
|
let actual_func_body = get_function_body(&injected_module, 0)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
use super::MeteredBlock;
|
use super::MeteredBlock;
|
||||||
use rules::Set as RuleSet;
|
use rules::Set as RuleSet;
|
||||||
|
use rules::Rules;
|
||||||
use parity_wasm::elements::{FuncBody, Instruction};
|
use parity_wasm::elements::{FuncBody, Instruction};
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -167,7 +168,7 @@ fn build_control_flow_graph(
|
|||||||
graph.increment_charged_cost(active_node_id, next_metered_block.cost);
|
graph.increment_charged_cost(active_node_id, next_metered_block.cost);
|
||||||
}
|
}
|
||||||
|
|
||||||
let instruction_cost = rules.process(instruction)?;
|
let instruction_cost = rules.instruction_cost(instruction).ok_or(())?;
|
||||||
match instruction {
|
match instruction {
|
||||||
Instruction::Block(_) => {
|
Instruction::Block(_) => {
|
||||||
graph.increment_actual_cost(active_node_id, instruction_cost);
|
graph.increment_actual_cost(active_node_id, instruction_cost);
|
||||||
|
|||||||
+47
-8
@@ -3,10 +3,39 @@ use std::collections::{HashMap as Map};
|
|||||||
#[cfg(not(features = "std"))]
|
#[cfg(not(features = "std"))]
|
||||||
use std::collections::{BTreeMap as Map};
|
use std::collections::{BTreeMap as Map};
|
||||||
|
|
||||||
|
use std::num::NonZeroU32;
|
||||||
|
|
||||||
use parity_wasm::elements;
|
use parity_wasm::elements;
|
||||||
|
|
||||||
pub struct UnknownInstruction;
|
pub struct UnknownInstruction;
|
||||||
|
|
||||||
|
/// An interface that describes instruction costs.
|
||||||
|
pub trait Rules {
|
||||||
|
/// Returns the cost for the passed `instruction`.
|
||||||
|
///
|
||||||
|
/// Returning `None` makes the gas instrumention end with an error. This is meant
|
||||||
|
/// as a way to have a partial rule set where any instruction that is not specifed
|
||||||
|
/// is considered as forbidden.
|
||||||
|
fn instruction_cost(&self, instruction: &elements::Instruction) -> Option<u32>;
|
||||||
|
|
||||||
|
/// Returns the costs for growing the memory using the `memory.grow` instruction.
|
||||||
|
///
|
||||||
|
/// Please note that these costs are in addition to the costs specified by `instruction_cost`
|
||||||
|
/// for the `memory.grow` instruction. Specifying `None` leads to no additional charge.
|
||||||
|
/// Those are meant as dynamic costs which take the amount of pages that the memory is
|
||||||
|
/// grown by into consideration. This is not possible using `instruction_cost` because
|
||||||
|
/// those costs depend on the stack and must be injected as code into the function calling
|
||||||
|
/// `memory.grow`. Therefore returning `Some` comes with a performance cost.
|
||||||
|
fn memory_grow_cost(&self) -> Option<MemoryGrowCost>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dynamic costs for memory growth.
|
||||||
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
|
pub enum MemoryGrowCost {
|
||||||
|
/// Charge the specified amount for each page that the memory is grown by.
|
||||||
|
Linear(NonZeroU32),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
|
||||||
pub enum Metering {
|
pub enum Metering {
|
||||||
Regular,
|
Regular,
|
||||||
@@ -288,14 +317,6 @@ impl Set {
|
|||||||
Set { regular, entries, grow: 0 }
|
Set { regular, entries, grow: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process(&self, instruction: &elements::Instruction) -> Result<u32, ()> {
|
|
||||||
match self.entries.get(&InstructionType::op(instruction)).cloned() {
|
|
||||||
None | Some(Metering::Regular) => Ok(self.regular),
|
|
||||||
Some(Metering::Forbidden) => Err(()),
|
|
||||||
Some(Metering::Fixed(val)) => Ok(val),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn grow_cost(&self) -> u32 {
|
pub fn grow_cost(&self) -> u32 {
|
||||||
self.grow
|
self.grow
|
||||||
}
|
}
|
||||||
@@ -313,3 +334,21 @@ impl Set {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Rules for Set {
|
||||||
|
fn instruction_cost(&self, instruction: &elements::Instruction) -> Option<u32> {
|
||||||
|
match self.entries.get(&InstructionType::op(instruction)) {
|
||||||
|
None | Some(Metering::Regular) => Some(self.regular),
|
||||||
|
Some(Metering::Fixed(val)) => Some(*val),
|
||||||
|
Some(Metering::Forbidden) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn memory_grow_cost(&self) -> Option<MemoryGrowCost> {
|
||||||
|
if let Some(val) = NonZeroU32::new(self.grow) {
|
||||||
|
Some(MemoryGrowCost::Linear(val))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user