Skip to content

Potential soundness issue in revm-interpreter 35.0.1 #3687

@varies-rust

Description

@varies-rust

Hello, and thank you for maintaining this crate.

I found a sanitizer/Miri failure reachable through public crate APIs using safe Rust code. I may be missing crate-specific preconditions, but the behavior looks worth checking because safe callers should not be able to trigger undefined behavior.

Summary

  • Crate: revm-interpreter
  • Version tested: 35.0.1
  • API paths with similar failures observed:
    • ExtBytecode::read_u8/read_u16/read_slice
    • ExtBytecode::read_offset_u16
    • ExtBytecode::opcode/pc
    • ExtBytecode::relative_jump/absolute_jump
    • SharedMemory::set
    • SharedMemory::set_data
    • SharedMemory::resize

Observed diagnostics

Miri undefined behavior: error: Undefined Behavior: memory access failed: attempting to access 2 bytes, but got alloc88868 which is only 1 byte from the end of the allocation
Unsafe precondition violation: unsafe precondition(s) violated: slice::get_unchecked_mut requires that the range is within the slice

Reproduction

The snippets below are minimal readable reproducers. Each PoC also includes the source location most relevant to the reported failure.

PoC 1: test_revm_interpreter10::generated_test_10

Relevant source location:

  • src/interpreter/ext_bytecode.rs:167-168: read_u16 performs an unchecked pointer read at the current instruction pointer.
  • src/interpreter/ext_bytecode.rs:182-188: read_offset_u16 applies an unchecked offset before reading two bytes.

Readable equivalent PoC:

use revm_interpreter::interpreter::ExtBytecode;
use revm_interpreter::interpreter_types::{Immediates, Jumps};

#[test]
fn poc() {
    let mut bytecode = ExtBytecode::default();

    bytecode.relative_jump(isize::MAX);
    let _ = bytecode.read_offset_u16(isize::MAX);
}

Observed diagnostic:

error: Undefined Behavior: memory access failed: attempting to access 2 bytes, but got alloc88868 which is only 1 byte from the end of the allocation

PoC 2: test_revm_interpreter202::generated_test_202

Relevant source location:

  • src/interpreter/shared_memory.rs:531: internal set_data receives unchecked offsets and length.
  • src/interpreter/shared_memory.rs:543-545: it uses unchecked slice ranges derived from those values.

Readable equivalent PoC:

use revm_interpreter::interpreter_types::MemoryTr;

#[test]
fn poc() {
    let mut memory = revm_interpreter::SharedMemory::new();

    memory.set(usize::MAX, &[0xff; 8]);
    let _ = memory.resize(0);
    memory.set_data(usize::MAX, 0, usize::MAX, &[0xff; 8]);
}

Observed diagnostic:

unsafe precondition(s) violated: slice::get_unchecked_mut requires that the range is within the slice

Source review and suggested fix

Brief reasoning:

  • Several public trait methods perform unchecked reads from the instruction pointer.
  • If those methods are safe to call from outside the crate, callers can trigger out-of-bounds or misaligned access through ordinary safe code.

Suggested fix:

  • Make the unchecked readers internal or unsafe, and add bounds checks or fallible accessors for the public API.

Thanks again for taking a look.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions