Skip to content

Commit 761b0c9

Browse files
rust-tests: handling of RISC-V memory access errors
1 parent 5329930 commit 761b0c9

File tree

1 file changed

+174
-0
lines changed

1 file changed

+174
-0
lines changed

tests/rust-tests/main.rs

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,180 @@ fn emulate_riscv64_invalid_insn_interrupt() {
775775
);
776776
}
777777

778+
#[test]
779+
fn emulate_riscv64_mem_error_hook() {
780+
let riscv_code: Vec<u8> = vec![
781+
0x17, 0x45, 0x01, 0x00, // auipc a0,0x14
782+
0x13, 0x05, 0x85, 0x00, // addi a0,a0,8 # 14008
783+
0x03, 0x25, 0x05, 0x00, // lw a0,0(a0)
784+
];
785+
786+
struct Data {
787+
hook_calls: usize,
788+
call: Option<HookCall>,
789+
}
790+
#[derive(Debug, PartialEq)]
791+
struct HookCall {
792+
typ: MemType,
793+
addr: u64,
794+
size: usize,
795+
}
796+
797+
let mut emu = unicorn_engine::Unicorn::new_with_data(
798+
Arch::RISCV,
799+
Mode::RISCV64,
800+
Data {
801+
hook_calls: 0,
802+
call: None,
803+
},
804+
)
805+
.expect("failed to initialize unicorn instance");
806+
807+
// Attempt to write to memory before mapping it.
808+
assert_eq!(
809+
emu.mem_write(0x1000, &riscv_code),
810+
(Err(uc_error::WRITE_UNMAPPED))
811+
);
812+
813+
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
814+
assert_eq!(emu.mem_write(0x1000, &riscv_code), Ok(()));
815+
assert_eq!(
816+
emu.mem_read_as_vec(0x1000, riscv_code.len()),
817+
Ok(riscv_code.clone())
818+
);
819+
820+
emu.ctl_tlb_type(unicorn_engine::TlbType::VIRTUAL)
821+
.expect("failed to select virtual TLB");
822+
emu.add_tlb_hook(0, !0, |_, vaddr, _| {
823+
if vaddr < 0x4000 {
824+
// The first page is identity-mapped.
825+
Some(TlbEntry {
826+
paddr: vaddr,
827+
perms: Permission::ALL,
828+
})
829+
} else {
830+
// All other memory is unmapped
831+
None
832+
}
833+
})
834+
.expect("failed to add TLB hook");
835+
836+
emu.add_mem_hook(HookType::MEM_INVALID, 0, !0, |emu, typ, addr, size, _| {
837+
let data = emu.get_data_mut();
838+
data.hook_calls += 1;
839+
data.call = Some(HookCall { typ, addr, size });
840+
false
841+
})
842+
.expect("failed to add memory hook");
843+
844+
assert_eq!(
845+
emu.emu_start(
846+
0x1000,
847+
(0x1000 + riscv_code.len()) as u64,
848+
10 * SECOND_SCALE,
849+
1000
850+
),
851+
Ok(())
852+
);
853+
854+
assert_eq!(
855+
emu.get_data().hook_calls,
856+
1,
857+
"interrupt hook should have been called exactly once"
858+
);
859+
assert_eq!(
860+
emu.get_data().call,
861+
Some(HookCall {
862+
typ: MemType::READ_PROT,
863+
addr: !0,
864+
size: 8,
865+
}),
866+
"wrong hook call for read from unmapped memory"
867+
);
868+
}
869+
870+
#[test]
871+
fn emulate_riscv64_mem_error_interrupt() {
872+
let riscv_code: Vec<u8> = vec![
873+
0x17, 0x45, 0x01, 0x00, // auipc a0,0x14
874+
0x13, 0x05, 0x85, 0x00, // addi a0,a0,8 # 14008
875+
0x03, 0x25, 0x05, 0x00, // lw a0,0(a0)
876+
];
877+
878+
struct Data {
879+
hook_calls: usize,
880+
mcause: Option<u32>,
881+
}
882+
883+
let mut emu = unicorn_engine::Unicorn::new_with_data(
884+
Arch::RISCV,
885+
Mode::RISCV64,
886+
Data {
887+
hook_calls: 0,
888+
mcause: None,
889+
},
890+
)
891+
.expect("failed to initialize unicorn instance");
892+
893+
// Attempt to write to memory before mapping it.
894+
assert_eq!(
895+
emu.mem_write(0x1000, &riscv_code),
896+
(Err(uc_error::WRITE_UNMAPPED))
897+
);
898+
899+
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
900+
assert_eq!(emu.mem_write(0x1000, &riscv_code), Ok(()));
901+
assert_eq!(
902+
emu.mem_read_as_vec(0x1000, riscv_code.len()),
903+
Ok(riscv_code.clone())
904+
);
905+
906+
emu.ctl_tlb_type(unicorn_engine::TlbType::VIRTUAL)
907+
.expect("failed to select virtual TLB");
908+
emu.add_tlb_hook(0, !0, |_, vaddr, _| {
909+
if vaddr < 0x4000 {
910+
// The first page is identity-mapped.
911+
Some(TlbEntry {
912+
paddr: vaddr,
913+
perms: Permission::ALL,
914+
})
915+
} else {
916+
// All other memory is unmapped
917+
None
918+
}
919+
})
920+
.expect("failed to add TLB hook");
921+
922+
emu.add_intr_hook(|emu, mcause| {
923+
let data = emu.get_data_mut();
924+
data.hook_calls += 1;
925+
data.mcause = Some(mcause);
926+
emu.emu_stop().expect("failed to stop");
927+
})
928+
.expect("failed to add interrupt hook");
929+
930+
assert_eq!(
931+
emu.emu_start(
932+
0x1000,
933+
(0x1000 + riscv_code.len()) as u64,
934+
10 * SECOND_SCALE,
935+
1000
936+
),
937+
Ok(())
938+
);
939+
940+
assert_eq!(
941+
emu.get_data().hook_calls,
942+
1,
943+
"interrupt hook should have been called exactly once"
944+
);
945+
assert_eq!(
946+
emu.get_data().mcause,
947+
Some(13_u32),
948+
"wrong mcause value for load page fault"
949+
);
950+
}
951+
778952
#[test]
779953
fn emulate_riscv64_ecall_interrupt() {
780954
let riscv_code: Vec<u8> = vec![0x73, 0x00, 0x00, 0x00]; // ecall

0 commit comments

Comments
 (0)