unicorn-engine / unicorn

Unicorn CPU emulator framework (ARM, AArch64, M68K, Mips, Sparc, PowerPC, RiscV, S390x, TriCore, X86)
http://www.unicorn-engine.org
GNU General Public License v2.0
7.33k stars 1.31k forks source link

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

Open jxors opened 3 months ago

jxors commented 3 months ago

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.