Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 75 additions & 16 deletions rust_crate/src/evm/host_functions/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,34 @@ where
return_data_size
}

fn validate_returndata_copy_bounds(
return_data_len: usize,
data_offset: u32,
length: u32,
) -> HostFunctionResult<(usize, usize)> {
let data_offset_usize = data_offset as usize;
let length_usize = length as usize;
let data_end = data_offset_usize.checked_add(length_usize).ok_or_else(|| {
crate::evm::error::out_of_bounds_error_with_function(
data_offset,
length,
"return_data_copy: return data offset overflow",
"return_data_copy",
)
})?;

if data_end > return_data_len {
return Err(crate::evm::error::out_of_bounds_error_with_function(
data_offset,
length,
"return_data_copy: return data out of bounds",
"return_data_copy",
));
}

Ok((data_offset_usize, data_end))
}

/// Copy return data from the last call to memory
/// Copies the return data from the last external call to the specified memory location
///
Expand Down Expand Up @@ -184,26 +212,57 @@ where

// Get the return data from the evmhost
let return_data = evmhost.return_data_copy();
let data_offset_usize = data_offset as usize;
let data_offset_u32 = data_offset as u32;
let (data_offset_usize, data_end) =
validate_returndata_copy_bounds(return_data.len(), data_offset_u32, length_u32)?;

// Prepare buffer for copying
let mut buffer = vec![0u8; length_u32 as usize];
// Write the buffer to memory
memory.write_bytes(result_offset_u32, &return_data[data_offset_usize..data_end])?;

// Copy from return data with bounds checking
let available_bytes = if data_offset_usize < return_data.len() {
return_data.len() - data_offset_usize
} else {
0
};
Ok(())
}

#[cfg(test)]
mod tests {
use super::validate_returndata_copy_bounds;

let copy_len = std::cmp::min(available_bytes, length_u32 as usize);
if copy_len > 0 {
buffer[..copy_len]
.copy_from_slice(&return_data[data_offset_usize..data_offset_usize + copy_len]);
#[test]
fn test_validate_returndata_copy_bounds_exact_end_ok() {
let (start, end) =
validate_returndata_copy_bounds(4, 2, 2).expect("bounds should be valid");
assert_eq!((start, end), (2, 4));
}

// Write the buffer to memory
memory.write_bytes(result_offset_u32, &buffer)?;
#[test]
fn test_validate_returndata_copy_bounds_oob_rejected() {
let err = validate_returndata_copy_bounds(4, 3, 2).expect_err("must fail");
assert_eq!(err.category(), "memory");
assert!(
err.message().contains("out of bounds"),
"unexpected error: {}",
err
);
}

Ok(())
#[test]
fn test_validate_returndata_copy_bounds_overflow_rejected() {
let err = validate_returndata_copy_bounds(4, u32::MAX, 2).expect_err("must fail");
assert_eq!(err.category(), "memory");
assert!(
err.message().contains("offset overflow"),
"unexpected error: {}",
err
);
}

#[test]
fn test_validate_returndata_copy_bounds_zero_length_past_end_rejected() {
let err = validate_returndata_copy_bounds(4, 5, 0).expect_err("must fail");
assert_eq!(err.category(), "memory");
assert!(
err.message().contains("out of bounds"),
"unexpected error: {}",
err
);
}
}
28 changes: 28 additions & 0 deletions tests/evm_asm/returndatacopy_oob.easm
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// RETURNDATACOPY out-of-bounds must fail per EIP-211.
// Build a 1-byte return buffer via identity precompile (0x04),
// then request offset=1, size=1 (1 + 1 > 1) to trigger failure.

// input[0] = 0x42
PUSH1 0x42
PUSH1 0x00
MSTORE8

// CALL identity precompile with 1-byte input and 32-byte output area.
// Stack order for CALL: gas, addr, value, argsOffset, argsSize, retOffset, retSize
PUSH1 0x20
PUSH1 0x80
PUSH1 0x01
PUSH1 0x00
PUSH1 0x00
PUSH1 0x04
PUSH4 0x100000
CALL
POP

// RETURNDATACOPY(dst=0, offset=1, size=1) -> out-of-bounds
PUSH1 0x01
PUSH1 0x01
PUSH1 0x00
RETURNDATACOPY

STOP
8 changes: 8 additions & 0 deletions tests/evm_asm/returndatacopy_oob.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
status: invalid memory access
error_code: 9
stack: []
memory: ''
storage: {}
transient_storage: {}
return: ''
events: []
Loading