Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SECURITY] Range end out of bounds in CODECOPY opcode #1145

Open
pventuzelo opened this issue Nov 12, 2024 · 0 comments
Open

[SECURITY] Range end out of bounds in CODECOPY opcode #1145

pventuzelo opened this issue Nov 12, 2024 · 0 comments

Comments

@pventuzelo
Copy link

Our team (@FuzzingLabs) discovered a bug in the op_codecopy function, which lead to a range end out of bounds.

Root cause

    pub fn op_codecopy(
        &mut self,
        current_call_frame: &mut CallFrame,
    ) -> Result<OpcodeSuccess, VMError> {
        let dest_offset: usize = current_call_frame
            .stack
            .pop()?
            .try_into()
            .map_err(|_| VMError::VeryLargeNumber)?;
        let offset: usize = current_call_frame
            .stack
            .pop()?
            .try_into()
            .map_err(|_| VMError::VeryLargeNumber)?;
        let size: usize = current_call_frame
            .stack
            .pop()?
            .try_into()
            .map_err(|_| VMError::VeryLargeNumber)?;

        let minimum_word_size = (size + WORD_SIZE - 1) / WORD_SIZE;

        let memory_expansion_cost = current_call_frame
            .memory
            .expansion_cost(dest_offset + size)?;

        let gas_cost = gas_cost::CODECOPY_STATIC
            + gas_cost::CODECOPY_DYNAMIC_BASE * minimum_word_size
            + memory_expansion_cost;

        self.increase_consumed_gas(current_call_frame, gas_cost)?;

        let code = current_call_frame.bytecode.slice(offset..offset + size);

        current_call_frame.memory.store_bytes(dest_offset, &code);

        Ok(OpcodeSuccess::Continue)
    }

let code = current_call_frame.bytecode.slice(offset..offset + size); Because of the lack of checks, we can trigger a range end out of bounds.

Step to reproduce

Payload

[58, 50, 50, 51, 57]

GASPRICE
ORIGIN
ORIGIN
CALLER
CODECOPY

Add to [test](https://github.com/lambdaclass/lambda_ethereum_rust/blob/main/crates/vm/levm/tests/tests.rs) :

#[test]
fn test_end_of_range_swap() {
    let mut vm = new_vm_with_bytecode(Bytes::copy_from_slice(&[58, 50, 50, 51, 57]));
    let mut current_call_frame = vm.call_frames.pop().unwrap();
    vm.execute(&mut current_call_frame);
}

Backtrace

---- tests::test_end_of_range_swap stdout ----
thread 'tests::test_end_of_range_swap' panicked at /home/.../.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytes-1.8.0/src/bytes.rs:305:9:
range end out of bounds: 200 <= 5
stack backtrace:
   0: rust_begin_unwind
             at /rustc/59e2c01c2217a01546222e4d9ff4e6695ee8a1db/library/std/src/panicking.rs:658:5
   1: core::panicking::panic_fmt
             at /rustc/59e2c01c2217a01546222e4d9ff4e6695ee8a1db/library/core/src/panicking.rs:74:14
   2: bytes::bytes::Bytes::slice
             at /home/mhoste/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytes-1.8.0/src/bytes.rs:305:9
   3: ethereum_rust_levm::opcode_handlers::environment::<impl ethereum_rust_levm::vm::VM>::op_codecopy
             at ./src/opcode_handlers/environment.rs:238:20
   4: ethereum_rust_levm::vm::VM::execute
             at ./src/vm.rs:236:37
   5: lib::tests::test_end_of_range_swap
             at ./tests/tests.rs:89:5
   6: lib::tests::test_end_of_range_swap::{{closure}}
             at ./tests/tests.rs:86:28
   7: core::ops::function::FnOnce::call_once
             at /rustc/59e2c01c2217a01546222e4d9ff4e6695ee8a1db/library/core/src/ops/function.rs:250:5
   8: core::ops::function::FnOnce::call_once
             at /rustc/59e2c01c2217a01546222e4d9ff4e6695ee8a1db/library/core/src/ops/function.rs:250:5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: No status
Development

No branches or pull requests

1 participant