Skip to content

Commit

Permalink
Feat: add usize casting strict check (#49)
Browse files Browse the repository at this point in the history
* Refactore usize casting

* Added error: UsizeOverflow

* pop_u256! - tiny refactoring

* Fix fn func call - in_offset usize casting

* Optimize array reallocation for: fn return_value
  • Loading branch information
mrLSD authored Jul 3, 2024
1 parent a201e15 commit 75940f1
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 101 deletions.
10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ keywords.workspace = true
edition.workspace = true

[workspace.dependencies]
evm = { version = "0.43", path = "." }
evm-core = { version = "0.43", path = "core", default-features = false }
evm-gasometer = { version = "0.43", path = "gasometer", default-features = false }
evm-runtime = { version = "0.43", path = "runtime", default-features = false }
evm = { version = "0.44", path = "." }
evm-core = { version = "0.44", path = "core", default-features = false }
evm-gasometer = { version = "0.44", path = "gasometer", default-features = false }
evm-runtime = { version = "0.44", path = "runtime", default-features = false }
primitive-types = { version = "0.12", default-features = false }
auto_impl = "1.0"
sha3 = { version = "0.10", default-features = false }
Expand Down Expand Up @@ -87,7 +87,7 @@ create-fixed = []
print-debug = ["evm-gasometer/print-debug"]

[workspace.package]
version = "0.43.0"
version = "0.44.0"
license = "Apache-2.0"
authors = ["Aurora Labs <[email protected]>", "Wei Tang <[email protected]>", "Parity Technologies <[email protected]>"]
description = "Portable Ethereum Virtual Machine implementation written in pure Rust."
Expand Down
2 changes: 2 additions & 0 deletions core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ impl From<ExitError> for ExitReason {
pub enum ExitFatal {
/// The operation is not supported.
NotSupported,
/// `usize` casting overflow
UsizeOverflow,
/// The trap (interrupt) is unhandled.
UnhandledInterrupt,
/// The environment explicitly set call errors as fatal error.
Expand Down
17 changes: 1 addition & 16 deletions core/src/eval/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,21 +65,6 @@ macro_rules! push_u256 {
)
}

/// Pops top element of the stack and converts it to `usize`.
///
/// The top element **must** be not greater than `usize::MAX`.
/// This non-local invariant is enforced by gas metering infrastructure.
macro_rules! pop_usize {
( $machine:expr, $( $x:ident ),* ) => (
$(
let $x = match $machine.stack.pop() {
Ok(value) => value.as_usize(),
Err(e) => return Control::Exit(e.into()),
};
)*
);
}

macro_rules! op1_u256_fn {
( $machine:expr, $op:path ) => {{
pop_u256!($machine, op1);
Expand Down Expand Up @@ -149,7 +134,7 @@ macro_rules! op3_u256_fn {
macro_rules! as_usize_or_fail {
( $v:expr ) => {{
if $v > crate::utils::USIZE_MAX {
return Control::Exit(ExitFatal::NotSupported.into());
return Control::Exit(ExitFatal::UsizeOverflow.into());
}

$v.as_usize()
Expand Down
58 changes: 30 additions & 28 deletions core/src/eval/misc.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::Control;

use crate::utils::USIZE_MAX;
use crate::{ExitError, ExitRevert, ExitSucceed, Machine};
use crate::{ExitError, ExitFatal, ExitRevert, ExitSucceed, Machine};
use core::cmp::min;
use primitive_types::{H256, U256};

Expand All @@ -13,18 +14,16 @@ pub fn codesize(state: &mut Machine) -> Control {

#[inline]
pub fn codecopy(state: &mut Machine) -> Control {
pop_u256!(state, memory_offset);
pop_u256!(state, code_offset);
pop_usize!(state, len);
pop_u256!(state, memory_offset, code_offset, len);

// If `len` is zero then nothing happens, regardless of the
// value of the other parameters. In particular, `memory_offset`
// might be larger than `usize::MAX`, hence why we check this first.
if len == 0 {
if len == U256::zero() {
return Control::Continue(1);
}

let memory_offset = memory_offset.as_usize();
let len = as_usize_or_fail!(len);
let memory_offset = as_usize_or_fail!(memory_offset);

try_or_fail!(state.memory.resize_offset(memory_offset, len));
match state
Expand Down Expand Up @@ -66,15 +65,15 @@ pub fn calldatasize(state: &mut Machine) -> Control {

#[inline]
pub fn calldatacopy(state: &mut Machine) -> Control {
pop_u256!(state, memory_offset);
pop_u256!(state, data_offset);
pop_usize!(state, len);
pop_u256!(state, memory_offset, data_offset, len);

// See comment on `codecopy` about the `len == 0` case.
if len == 0 {
if len == U256::zero() {
return Control::Continue(1);
}
let memory_offset = memory_offset.as_usize();
let len = as_usize_or_fail!(len);
let memory_offset = as_usize_or_fail!(memory_offset);

try_or_fail!(state.memory.resize_offset(memory_offset, len));

match state
Expand All @@ -94,7 +93,8 @@ pub fn pop(state: &mut Machine) -> Control {

#[inline]
pub fn mload(state: &mut Machine) -> Control {
pop_usize!(state, index);
pop_u256!(state, index);
let index = as_usize_or_fail!(index);
try_or_fail!(state.memory.resize_offset(index, 32));
let value = state.memory.get_h256(index);
push_h256!(state, value);
Expand All @@ -103,7 +103,8 @@ pub fn mload(state: &mut Machine) -> Control {

#[inline]
pub fn mstore(state: &mut Machine) -> Control {
pop_usize!(state, index);
pop_u256!(state, index);
let index = as_usize_or_fail!(index);
pop_h256!(state, value);
try_or_fail!(state.memory.resize_offset(index, 32));
match state.memory.set(index, &value[..], Some(32)) {
Expand All @@ -114,8 +115,8 @@ pub fn mstore(state: &mut Machine) -> Control {

#[inline]
pub fn mstore8(state: &mut Machine) -> Control {
pop_usize!(state, index);
pop_u256!(state, value);
pop_u256!(state, index, value);
let index = as_usize_or_fail!(index);
try_or_fail!(state.memory.resize_offset(index, 1));
let value = (value.low_u32() & 0xff) as u8;
match state.memory.set(index, &[value], Some(1)) {
Expand All @@ -138,8 +139,7 @@ pub fn jump(state: &mut Machine) -> Control {

#[inline]
pub fn jumpi(state: &mut Machine) -> Control {
pop_u256!(state, dest);
pop_u256!(state, value);
pop_u256!(state, dest, value);

if value == U256::zero() {
Control::Continue(1)
Expand Down Expand Up @@ -237,22 +237,24 @@ pub fn swap(state: &mut Machine, n: usize) -> Control {

#[inline]
pub fn ret(state: &mut Machine) -> Control {
pop_u256!(state, start);
pop_usize!(state, len);
if len > 0 {
try_or_fail!(state.memory.resize_offset(start.as_usize(), len));
pop_u256!(state, start, len);
if len > U256::zero() {
let start = as_usize_or_fail!(start);
let len = as_usize_or_fail!(len);
try_or_fail!(state.memory.resize_offset(start, len));
}
state.return_range = start..(start + U256::from(len));
state.return_range = start..(start + len);
Control::Exit(ExitSucceed::Returned.into())
}

#[inline]
pub fn revert(state: &mut Machine) -> Control {
pop_u256!(state, start);
pop_usize!(state, len);
if len > 0 {
try_or_fail!(state.memory.resize_offset(start.as_usize(), len));
pop_u256!(state, start, len);
if len > U256::zero() {
let start = as_usize_or_fail!(start);
let len = as_usize_or_fail!(len);
try_or_fail!(state.memory.resize_offset(start, len));
}
state.return_range = start..(start + U256::from(len));
state.return_range = start..(start + len);
Control::Exit(ExitRevert::Reverted.into())
}
6 changes: 4 additions & 2 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,10 @@ impl Machine {
self.return_range.start.as_usize(),
usize::MAX - self.return_range.start.as_usize(),
);
while ret.len() < (self.return_range.end - self.return_range.start).as_usize() {
ret.push(0);

let new_len = (self.return_range.end - self.return_range.start).as_usize();
if ret.len() < new_len {
ret.resize(new_len, 0);
}
ret
} else {
Expand Down
17 changes: 1 addition & 16 deletions runtime/src/eval/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,25 +55,10 @@ macro_rules! push_u256 {
)
}

/// Pops top element of the stack and converts it to `usize`.
///
/// The top element **must** be not greater than `usize::MAX`.
/// This non-local invariant is enforced by gas metering infrastructure.
macro_rules! pop_usize {
( $machine:expr, $( $x:ident ),* ) => (
$(
let $x = match $machine.machine.stack_mut().pop() {
Ok(value) => value.as_usize(),
Err(e) => return Control::Exit(e.into()),
};
)*
);
}

macro_rules! as_usize_or_fail {
( $v:expr ) => {{
if $v > crate::utils::USIZE_MAX {
return Control::Exit(ExitFatal::NotSupported.into());
return Control::Exit(ExitFatal::UsizeOverflow.into());
}

$v.as_usize()
Expand Down
66 changes: 32 additions & 34 deletions runtime/src/eval/system.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
use super::Control;
use crate::prelude::*;
use crate::{
CallScheme, Capture, Context, CreateScheme, ExitError, ExitSucceed, Handler, Runtime, Transfer,
CallScheme, Capture, Context, CreateScheme, ExitError, ExitFatal, ExitSucceed, Handler,
Runtime, Transfer,
};
use core::cmp::max;
use evm_core::utils::{U64_MAX, USIZE_MAX};
use primitive_types::{H256, U256};
use sha3::{Digest, Keccak256};

pub fn sha3<H: Handler>(runtime: &mut Runtime) -> Control<H> {
pop_u256!(runtime, from);
pop_usize!(runtime, len);
pop_u256!(runtime, from, len);

// Cast to `usize` after length checking to avoid overflow
let from = if len == 0 {
let from = if len == U256::zero() {
usize::MAX
} else {
from.as_usize()
as_usize_or_fail!(from)
};
let len = as_usize_or_fail!(len);

try_or_fail!(runtime.machine.memory_mut().resize_offset(from, len));
let data = if len == 0 {
Expand Down Expand Up @@ -144,16 +145,15 @@ pub fn extcodehash<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H>

pub fn extcodecopy<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
pop_h256!(runtime, address);
pop_u256!(runtime, memory_offset);
pop_u256!(runtime, code_offset);
pop_usize!(runtime, len);
pop_u256!(runtime, memory_offset, code_offset, len);

if len == 0 {
if len == U256::zero() {
return Control::Continue;
}
let len = as_usize_or_fail!(len);

// Cast to `usize` after length checking to avoid overflow
let memory_offset = memory_offset.as_usize();
let memory_offset = as_usize_or_fail!(memory_offset);

try_or_fail!(runtime
.machine
Expand All @@ -180,24 +180,23 @@ pub fn returndatasize<H: Handler>(runtime: &mut Runtime) -> Control<H> {
}

pub fn returndatacopy<H: Handler>(runtime: &mut Runtime) -> Control<H> {
pop_u256!(runtime, memory_offset);
pop_u256!(runtime, data_offset);
pop_usize!(runtime, len);
pop_u256!(runtime, memory_offset, data_offset, len);

// If `len` is zero then nothing happens to the memory, regardless
// of the value of `memory_offset`. In particular, the value taken
// from the stack might be larger than `usize::MAX`, hence why the
// `as_usize` cast is not always safe. But because the value does
// not matter when `len == 0` we can safely set it equal to zero instead.
let memory_offset = if len == 0 {
let memory_offset = if len == U256::zero() {
0
} else {
// SAFETY: this cast is safe because if `len > 0` then gas cost of memory
// would have already been taken into account at this point. It is impossible
// to have a memory offset greater than `usize::MAX` for any gas limit less
// than `u64::MAX` (and gas limits higher than this are disallowed in general).
memory_offset.as_usize()
as_usize_or_fail!(memory_offset)
};
let len = as_usize_or_fail!(len);

try_or_fail!(runtime
.machine
Expand Down Expand Up @@ -333,10 +332,10 @@ pub fn tstore<H: Handler>(runtime: &mut Runtime, handler: &mut H) -> Control<H>
/// EIP-5656: MCOPY - Memory copying instruction
pub fn mcopy<H: Handler>(runtime: &mut Runtime, _handler: &mut H) -> Control<H> {
pop_u256!(runtime, dst, src, len);
let len = as_usize_or_fail!(len, ExitError::OutOfGas);
if len == 0 {
if len == U256::zero() {
return Control::Continue;
}
let len = as_usize_or_fail!(len, ExitError::OutOfGas);
let dst = as_usize_or_fail!(dst, ExitError::OutOfGas);
let src = as_usize_or_fail!(src, ExitError::OutOfGas);

Expand All @@ -361,15 +360,15 @@ pub fn gas<H: Handler>(runtime: &mut Runtime, handler: &H) -> Control<H> {
}

pub fn log<H: Handler>(runtime: &mut Runtime, n: u8, handler: &mut H) -> Control<H> {
pop_u256!(runtime, offset);
pop_usize!(runtime, len);
pop_u256!(runtime, offset, len);

// Cast to `usize` after length checking to avoid overflow
let offset = if len == 0 {
let offset = if len == U256::zero() {
usize::MAX
} else {
offset.as_usize()
as_usize_or_fail!(offset)
};
let len = as_usize_or_fail!(len);

try_or_fail!(runtime.machine.memory_mut().resize_offset(offset, len));
let data = if len == 0 {
Expand Down Expand Up @@ -419,16 +418,15 @@ pub fn selfdestruct<H: Handler>(runtime: &mut Runtime, handler: &mut H) -> Contr
pub fn create<H: Handler>(runtime: &mut Runtime, is_create2: bool, handler: &mut H) -> Control<H> {
runtime.return_data_buffer = Vec::new();

pop_u256!(runtime, value);
pop_u256!(runtime, code_offset);
pop_usize!(runtime, len);
pop_u256!(runtime, value, code_offset, len);

// Cast to `usize` after length checking to avoid overflow
let code_offset = if len == 0 {
let code_offset = if len == U256::zero() {
usize::MAX
} else {
code_offset.as_usize()
as_usize_or_fail!(code_offset)
};
let len = as_usize_or_fail!(len);

try_or_fail!(runtime.machine.memory_mut().resize_offset(code_offset, len));
let code = if len == 0 {
Expand Down Expand Up @@ -481,23 +479,23 @@ pub fn call<H: Handler>(runtime: &mut Runtime, scheme: CallScheme, handler: &mut
CallScheme::DelegateCall | CallScheme::StaticCall => U256::zero(),
};

pop_u256!(runtime, in_offset);
pop_usize!(runtime, in_len);
pop_u256!(runtime, out_offset);
pop_usize!(runtime, out_len);
pop_u256!(runtime, in_offset, in_len);
pop_u256!(runtime, out_offset, out_len);

// Cast to `usize` after length checking to avoid overflow
let in_offset = if in_len == 0 {
let in_offset = if in_len == U256::zero() {
usize::MAX
} else {
in_offset.as_usize()
as_usize_or_fail!(in_offset)
};
let in_len = as_usize_or_fail!(in_len);
// Cast to `usize` after length checking to avoid overflow
let out_offset = if out_len == 0 {
let out_offset = if out_len == U256::zero() {
usize::MAX
} else {
out_offset.as_usize()
as_usize_or_fail!(out_offset)
};
let out_len = as_usize_or_fail!(out_len);

try_or_fail!(runtime
.machine
Expand Down

0 comments on commit 75940f1

Please sign in to comment.