Skip to content

Commit 62d7dc2

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

File tree

1 file changed

+166
-0
lines changed

1 file changed

+166
-0
lines changed

tests/rust-tests/main.rs

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

0 commit comments

Comments
 (0)