From f6892f2ede56c3743df279d4c652e0a295421bed Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Sat, 28 Nov 2020 00:54:35 +0100 Subject: [PATCH] hypervisor: emulator: Add a negative instruction fetch unit test Signed-off-by: Samuel Ortiz --- .../src/arch/x86/emulator/instructions/mov.rs | 28 ++++++------ hypervisor/src/arch/x86/emulator/mod.rs | 44 ++++++++++++++++--- 2 files changed, 51 insertions(+), 21 deletions(-) diff --git a/hypervisor/src/arch/x86/emulator/instructions/mov.rs b/hypervisor/src/arch/x86/emulator/instructions/mov.rs index 9692cc487..620a54f72 100644 --- a/hypervisor/src/arch/x86/emulator/instructions/mov.rs +++ b/hypervisor/src/arch/x86/emulator/instructions/mov.rs @@ -268,7 +268,7 @@ mod tests { let cpu_id = 0; let insn = [0x48, 0x89, 0xd8]; let mut vmm = MockVMM::new(ip, hashmap![Register::RBX => rbx], None); - vmm.emulate_first_insn(cpu_id, &insn); + assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); let rax: u64 = vmm .cpu_state(cpu_id) @@ -288,7 +288,7 @@ mod tests { let cpu_id = 0; let insn = [0x48, 0xb8, 0x44, 0x33, 0x22, 0x11, 0x44, 0x33, 0x22, 0x11]; let mut vmm = MockVMM::new(ip, hashmap![], None); - vmm.emulate_first_insn(cpu_id, &insn); + assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); let rax: u64 = vmm .cpu_state(cpu_id) @@ -314,7 +314,7 @@ mod tests { hashmap![Register::RAX => rax], Some((rax + rax, &memory)), ); - vmm.emulate_first_insn(cpu_id, &insn); + assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); rax = vmm .cpu_state(cpu_id) @@ -334,7 +334,7 @@ mod tests { let cpu_id = 0; let insn = [0xb0, 0x11]; let mut vmm = MockVMM::new(ip, hashmap![], None); - vmm.emulate_first_insn(cpu_id, &insn); + assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); let al = vmm .cpu_state(cpu_id) @@ -354,7 +354,7 @@ mod tests { let cpu_id = 0; let insn = [0xb8, 0x11, 0x00, 0x00, 0x00]; let mut vmm = MockVMM::new(ip, hashmap![], None); - vmm.emulate_first_insn(cpu_id, &insn); + assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); let eax = vmm .cpu_state(cpu_id) @@ -374,7 +374,7 @@ mod tests { let cpu_id = 0; let insn = [0x48, 0xc7, 0xc0, 0x44, 0x33, 0x22, 0x11]; let mut vmm = MockVMM::new(ip, hashmap![], None); - vmm.emulate_first_insn(cpu_id, &insn); + assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); let rax: u64 = vmm .cpu_state(cpu_id) @@ -399,7 +399,7 @@ mod tests { hashmap![Register::RAX => rax, Register::DH => dh.into()], None, ); - vmm.emulate_first_insn(cpu_id, &insn); + assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); let mut memory: [u8; 1] = [0; 1]; vmm.read_memory(rax, &mut memory).unwrap(); @@ -422,7 +422,7 @@ mod tests { hashmap![Register::RAX => rax, Register::ESI => esi.into()], None, ); - vmm.emulate_first_insn(cpu_id, &insn); + assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); let mut memory: [u8; 4] = [0; 4]; vmm.read_memory(rax, &mut memory).unwrap(); @@ -446,7 +446,7 @@ mod tests { hashmap![Register::RAX => rax, Register::EDI => edi.into()], None, ); - vmm.emulate_first_insn(cpu_id, &insn); + assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); let mut memory: [u8; 4] = [0; 4]; vmm.read_memory(rax + displacement, &mut memory).unwrap(); @@ -471,7 +471,7 @@ mod tests { hashmap![Register::RAX => rax], Some((rax + displacement, &memory)), ); - vmm.emulate_first_insn(cpu_id, &insn); + assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); let new_eax = vmm .cpu_state(cpu_id) @@ -498,7 +498,7 @@ mod tests { hashmap![Register::RAX => rax], Some((rax + displacement, &memory)), ); - vmm.emulate_first_insn(cpu_id, &insn); + assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); let new_al = vmm .cpu_state(cpu_id) @@ -525,7 +525,7 @@ mod tests { 0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h] ]; let mut vmm = MockVMM::new(ip, hashmap![], Some((rax + displacement, &memory))); - vmm.emulate_insn(cpu_id, &insn, Some(2)); + assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok()); let rbx: u64 = vmm .cpu_state(cpu_id) @@ -554,7 +554,7 @@ mod tests { let mut vmm = MockVMM::new(ip, hashmap![], Some((rax + displacement, &memory))); // Only run the first instruction. - vmm.emulate_first_insn(cpu_id, &insn); + assert!(vmm.emulate_first_insn(cpu_id, &insn).is_ok()); assert_eq!(ip + 7 as u64, vmm.cpu_state(cpu_id).unwrap().ip()); @@ -587,7 +587,7 @@ mod tests { let mut vmm = MockVMM::new(ip, hashmap![], Some((rax + displacement, &memory))); // Run the 2 first instructions. - vmm.emulate_insn(cpu_id, &insn, Some(2)); + assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok()); assert_eq!(ip + 7 + 4 as u64, vmm.cpu_state(cpu_id).unwrap().ip()); diff --git a/hypervisor/src/arch/x86/emulator/mod.rs b/hypervisor/src/arch/x86/emulator/mod.rs index 6a54e1173..457c0114e 100644 --- a/hypervisor/src/arch/x86/emulator/mod.rs +++ b/hypervisor/src/arch/x86/emulator/mod.rs @@ -650,21 +650,26 @@ mod mock_vmm { vmm } - pub fn emulate_insn(&mut self, cpu_id: usize, insn: &[u8], num_insn: Option) { + pub fn emulate_insn( + &mut self, + cpu_id: usize, + insn: &[u8], + num_insn: Option, + ) -> MockResult { let ip = self.cpu_state(cpu_id).unwrap().ip(); let mut emulator = Emulator::new(self); - let new_state = emulator - .emulate_insn_stream(cpu_id, &insn, num_insn) - .unwrap(); + let new_state = emulator.emulate_insn_stream(cpu_id, &insn, num_insn)?; if num_insn.is_none() { assert_eq!(ip + insn.len() as u64, new_state.ip()); } self.set_cpu_state(cpu_id, new_state).unwrap(); + + Ok(()) } - pub fn emulate_first_insn(&mut self, cpu_id: usize, insn: &[u8]) { + pub fn emulate_first_insn(&mut self, cpu_id: usize, insn: &[u8]) -> MockResult { self.emulate_insn(cpu_id, insn, Some(1)) } } @@ -760,7 +765,7 @@ mod tests { ]; let mut vmm = MockVMM::new(ip, hashmap![], Some((ip, &memory))); - vmm.emulate_insn(cpu_id, &insn, Some(2)); + assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok()); let rax: u64 = vmm .cpu_state(cpu_id) @@ -797,7 +802,7 @@ mod tests { ]; let mut vmm = MockVMM::new(ip, hashmap![], Some((ip, &memory))); - vmm.emulate_insn(cpu_id, &insn, Some(2)); + assert!(vmm.emulate_insn(cpu_id, &insn, Some(2)).is_ok()); let rbx: u64 = vmm .cpu_state(cpu_id) @@ -808,4 +813,29 @@ mod tests { Ok(()) } + + #[test] + // Emulate truncated instruction stream, which should cause a fetch. + // + // mov rax, 0x1000 + // Test with a first instruction truncated and a bad fetched instruction. + // Verify that the instruction emulation returns an error. + fn test_fetch_bad_insn() -> MockResult { + let ip: u64 = 0x1000; + let cpu_id = 0; + let memory = [ + // Code at IP + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, + ]; + let insn = [ + // First instruction is truncated + 0x48, 0xc7, 0xc0, 0x00, // mov rax, 0x1000 -- Missing bytes: 0x00, 0x10, 0x00, 0x00, + ]; + + let mut vmm = MockVMM::new(ip, hashmap![], Some((ip, &memory))); + assert!(vmm.emulate_first_insn(cpu_id, &insn).is_err()); + + Ok(()) + } }