unicorn icon indicating copy to clipboard operation
unicorn copied to clipboard

RISC-V64 incorrectly returns error when calling `emu_start` with `count` = 1 at end of page

Open jxors opened this issue 11 months ago • 0 comments

When executing a single instruction at the end of a page with the following page unmapped, unicorn will return a memory error (FETCH_UNMAPPED) instead of stopping.

This can be reproduced with:

// Cargo.toml
[package]
name = "minimal-unicorn-test"
version = "0.1.0"
edition = "2021"

[dependencies]
unicorn-engine = "2.0.1"

// src/main.rs
use unicorn_engine::{unicorn_const::{Arch, Mode, Permission}, RegisterRISCV, Unicorn};

fn main() {
    let mut unicorn = Unicorn::new_with_data(Arch::RISCV, Mode::RISCV64, false)
        .expect("failed to initialize Unicorn instance");

    check(&mut unicorn, 0x6D76D7473FF0);
    check(&mut unicorn, 0x6D76D7473FFC);
}

fn check(unicorn: &mut Unicorn<'_, bool>, addr: u64) {
    unicorn.mem_map(addr & !0xfff, 4096, Permission::EXEC).unwrap();
    unicorn.mem_write(addr, &[ 0x13, 0x81, 0x00, 0x7d ]).unwrap();
    
    unicorn.reg_write(RegisterRISCV::PC, addr).unwrap();
    unicorn.reg_write(RegisterRISCV::X1, 0x1234).unwrap();
    let execution_result = unicorn.emu_start(addr, addr + 4, 0, 1);
    assert_eq!(unicorn.reg_read(RegisterRISCV::PC), Ok(addr + 4));
    assert_eq!(unicorn.reg_read(RegisterRISCV::X2), Ok(0x1234 + 2000));
    assert_eq!(execution_result, Ok(()));

    unicorn.mem_unmap(addr & !0xfff, 4096).unwrap();

    println!("Execution at 0x{addr:X} OK");
}

Note how emu_start is called with count as 1.

The first call to check places the instruction 16 bytes from the end of the page. This is successfully executed. The second call to check places the instruction exactly at the page edge. This fails with an error, but the resulting unicorn state is correct. Unicorn should return Ok(()) here instead of an error.

jxors avatar Mar 19 '24 15:03 jxors