Skip to content

Commit

Permalink
Config option to allow permissive errors (#100)
Browse files Browse the repository at this point in the history
# Summary

Fixes #56 

# Checklist

- [x] Code is formatted by Rustfmt.
- [x] Documentation has been updated if necessary.
  • Loading branch information
2xic committed Sep 28, 2023
1 parent 3f8212c commit 355558a
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 10 deletions.
3 changes: 3 additions & 0 deletions src/constant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,6 @@ pub const DEFAULT_WATCHDOG_POLL_LOOP_ITERATIONS: usize = 100;

/// The value that solidity uses to type tag `string` in ABI encoding.
pub const SOLIDITY_STRING_POINTER: usize = 0x20;

// The default value for whether to allow permissive errors or not
pub const DEFAULT_PERMISSIVE_ERRORS: bool = false;
35 changes: 32 additions & 3 deletions src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ use crate::{
DEFAULT_CONDITIONAL_JUMP_PER_TARGET_FORK_LIMIT,
DEFAULT_ITERATIONS_PER_OPCODE,
DEFAULT_MEMORY_SINGLE_OPERATION_MAX_BYTES,
DEFAULT_PERMISSIVE_ERRORS,
DEFAULT_VALUE_SIZE_LIMIT,
},
disassembly::{ExecutionThread, InstructionStream},
error::{
self,
container::Locatable,
execution::{Error, Errors, LocatedError, Result},
},
Expand Down Expand Up @@ -176,9 +178,23 @@ impl VM {
.consume_gas(instruction.min_gas_cost());
}
Err(payload) => {
// If execution errored, add the error to the collection of them and then kill
// the current thread to continue.
self.errors.add(payload);
// If execution errored and we are not in permissive error mode, add the error
// to the collection of them and then kill the current
// thread to continue. If we are in permissive error mode we
// will only collect critical errors.
match payload.payload {
error::execution::Error::InvalidOffsetForJump { .. }
| error::execution::Error::InvalidJumpTarget { .. }
| error::execution::Error::NonExistentJumpTarget { .. }
| error::execution::Error::NoConcreteJumpDestination => {
if !self.config.permissive_errors {
self.errors.add(payload);
}
}
_ => {
self.errors.add(payload);
}
}
self.kill_current_thread();
}
}
Expand Down Expand Up @@ -564,6 +580,10 @@ pub struct Config {
///
/// Defaults to [`DEFAULT_MEMORY_SINGLE_OPERATION_MAX_BYTES`].
pub single_memory_operation_size_limit: usize,

/// Whether to continue execution when non critical errors happen during
/// execution.
pub permissive_errors: bool,
}

impl Config {
Expand Down Expand Up @@ -601,6 +621,13 @@ impl Config {
self.single_memory_operation_size_limit = value;
self
}

/// Sets the permissive errors configuration parameter to `value`.
#[must_use]
pub fn with_permissive_errors(mut self, value: bool) -> Self {
self.permissive_errors = value;
self
}
}

impl Default for Config {
Expand All @@ -610,12 +637,14 @@ impl Default for Config {
let maximum_forks_per_fork_target = DEFAULT_CONDITIONAL_JUMP_PER_TARGET_FORK_LIMIT;
let value_size_limit = DEFAULT_VALUE_SIZE_LIMIT;
let single_memory_operation_size_limit = DEFAULT_MEMORY_SINGLE_OPERATION_MAX_BYTES;
let permissive_errors = DEFAULT_PERMISSIVE_ERRORS;
Self {
gas_limit,
maximum_iterations_per_opcode,
maximum_forks_per_fork_target,
value_size_limit,
single_memory_operation_size_limit,
permissive_errors,
}
}
}
Expand Down
21 changes: 14 additions & 7 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ pub fn new_analyzer_from_bytecode(
watchdog: DynWatchdog,
) -> anyhow::Result<InitialAnalyzer> {
// Generally unsafe but fine for ASCII so we do it here.
let code = code.into();
let no_0x_prefix = &code[2..];
let bytecode = hex::decode(no_0x_prefix).map_err(|_| anyhow!("Could not decode hex"))?;
let bytecode = get_bytecode_from_string(code)?;

let contract = Contract::new(
bytecode,
Expand Down Expand Up @@ -104,10 +102,19 @@ pub fn new_contract_from_file(path: impl Into<String>, chain: Chain) -> anyhow::
.map_err(|_| anyhow!("Could not parse compiled contract."))?;

// Generally unsafe but fine for ASCII.
let bytecode_string = contract_rep.deployed_bytecode.object;
let no_0x_prefix = &bytecode_string[2..];

let bytecode = hex::decode(no_0x_prefix).map_err(|_| anyhow!("Could not decode hex"))?;
let bytecode = get_bytecode_from_string(contract_rep.deployed_bytecode.object)?;

Ok(Contract::new(bytecode, chain))
}

pub fn get_bytecode_from_string(code: impl Into<String>) -> anyhow::Result<Vec<u8>> {
let bytecode_string = code.into();
// Remove the 0x if it is present
let no_0x_prefix = match bytecode_string.strip_prefix("0x") {
Some(no_0x_prefix) => no_0x_prefix,
None => &bytecode_string,
};

let bytecode = hex::decode(no_0x_prefix).map_err(|_| anyhow!("Could not decode hex"))?;
Ok(bytecode)
}
80 changes: 80 additions & 0 deletions tests/curve_v1_adapter_2_assets.rs

Large diffs are not rendered by default.

0 comments on commit 355558a

Please sign in to comment.