diff options
| author | iximeow <me@iximeow.net> | 2026-04-23 07:44:24 +0000 |
|---|---|---|
| committer | iximeow <me@iximeow.net> | 2026-04-23 07:44:24 +0000 |
| commit | b9271576823ecbffc014f3809d8df6b08e049489 (patch) | |
| tree | 50e4f8e4969b3dafc556dba300355f36f597d18c | |
| parent | d23dc85b291e75b90b0acf8c7a51a6b3a6becee9 (diff) | |
start verifying vex-encoded instruction behavior
| -rw-r--r-- | src/long_mode/behavior.rs | 55 | ||||
| -rw-r--r-- | test/long_mode/behavior.rs | 60 |
2 files changed, 91 insertions, 24 deletions
diff --git a/src/long_mode/behavior.rs b/src/long_mode/behavior.rs index 56cb270..a5e5757 100644 --- a/src/long_mode/behavior.rs +++ b/src/long_mode/behavior.rs @@ -975,6 +975,10 @@ pub enum ComplexOp { BTS, /// TODO: document + SYSCALL, + SYSRET, + + /// TODO: document SWAPGS, RDFSBASE, WRFSBASE, @@ -2594,7 +2598,8 @@ fn opcode2behavior(opc: &Opcode) -> Option<BehaviorDigest> { .set_pl0(), INVD => BehaviorDigest::empty() .set_pl0(), - SYSRET => { panic!("todo: sysret"); }, + SYSRET => BehaviorDigest::empty() + .set_pl0(), CLTS => BehaviorDigest::empty() .set_implicit_ops(CLTS_IDX) .set_pl0(), @@ -2903,7 +2908,7 @@ fn opcode2behavior(opc: &Opcode) -> Option<BehaviorDigest> { XORPS => GENERAL_RW_R, XORPD => GENERAL_RW_R, - VMOVDDUP => { panic!("todo: vmovddup"); }, + VMOVDDUP => GENERAL_W_R, VPSHUFLW => { panic!("todo: vpshuflw"); }, VPSHUFHW => { panic!("todo: vpshufhw"); }, VHADDPS => GENERAL_W_R_R, @@ -3053,12 +3058,12 @@ fn opcode2behavior(opc: &Opcode) -> Option<BehaviorDigest> { VMOVD => GENERAL_W_R, VMOVDQA => { panic!("todo: vmovdqa"); }, VMOVDQU => { panic!("todo: vmovdqu"); }, - VMOVHLPS => { panic!("todo: vmovhlps"); }, - VMOVHPD => { panic!("todo: vmovhpd"); }, - VMOVHPS => { panic!("todo: vmovhps"); }, - VMOVLHPS => { panic!("todo: vmovlhps"); }, - VMOVLPD => { panic!("todo: vmovlpd"); }, - VMOVLPS => { panic!("todo: vmovlps"); }, + VMOVHLPS => GENERAL_W_R_R, + VMOVHPD => GENERAL_W_R, + VMOVHPS => GENERAL_W_R, + VMOVLHPS => GENERAL_W_R_R, + VMOVLPD => GENERAL_W_R, + VMOVLPS => GENERAL_W_R, VMOVMSKPD => { panic!("todo: vmovmskpd"); }, VMOVMSKPS => { panic!("todo: vmovmskps"); }, VMOVNTDQ => { panic!("todo: vmovntdq"); }, @@ -3066,12 +3071,12 @@ fn opcode2behavior(opc: &Opcode) -> Option<BehaviorDigest> { VMOVNTPD => { panic!("todo: vmovntpd"); }, VMOVNTPS => { panic!("todo: vmovntps"); }, VMOVQ => GENERAL_W_R, - VMOVSS => { panic!("todo: vmovss"); }, - VMOVSD => { panic!("todo: vmovsd"); }, + VMOVSS => GENERAL_W_R_R, + VMOVSD => GENERAL_W_R_R, VMOVSHDUP => GENERAL_W_R, VMOVSLDUP => GENERAL_W_R, - VMOVUPD => { panic!("todo: vmovupd"); }, - VMOVUPS => { panic!("todo: vmovups"); }, + VMOVUPD => GENERAL_W_R, + VMOVUPS => GENERAL_W_R, VMPSADBW => { panic!("todo: vmpsadbw"); }, VMULPD => GENERAL_W_R_R, VMULPS => GENERAL_W_R_R, @@ -3126,10 +3131,10 @@ fn opcode2behavior(opc: &Opcode) -> Option<BehaviorDigest> { VPERM2F128 => { panic!("todo: vperm2f128"); }, VPERM2I128 => { panic!("todo: vperm2i128"); }, VPERMD => { panic!("todo: vpermd"); }, - VPERMILPD => { panic!("todo: vpermilpd"); }, - VPERMILPS => { panic!("todo: vpermilps"); }, - VPERMPD => { panic!("todo: vpermpd"); }, - VPERMPS => { panic!("todo: vpermps"); }, + VPERMILPD => GENERAL_W_R_R, + VPERMILPS => GENERAL_W_R_R, + VPERMPD => GENERAL_W_R_R, + VPERMPS => GENERAL_W_R_R, VPERMQ => { panic!("todo: vpermq"); }, VPEXTRB => { panic!("todo: vpextrb"); }, VPEXTRD => { panic!("todo: vpextrd"); }, @@ -3139,14 +3144,14 @@ fn opcode2behavior(opc: &Opcode) -> Option<BehaviorDigest> { VPGATHERDQ => { panic!("todo: vpgatherdq"); }, VPGATHERQD => { panic!("todo: vpgatherqd"); }, VPGATHERQQ => { panic!("todo: vpgatherqq"); }, - VPHADDD => { panic!("todo: vphaddd"); }, - VPHADDSW => { panic!("todo: vphaddsw"); }, - VPHADDW => { panic!("todo: vphaddw"); }, + VPHADDD => GENERAL_W_R_R, + VPHADDSW => GENERAL_W_R_R, + VPHADDW => GENERAL_W_R_R, VPMADDUBSW => GENERAL_W_R_R, VPHMINPOSUW => { panic!("todo: vphminposuw"); }, - VPHSUBD => { panic!("todo: vphsubd"); }, - VPHSUBSW => { panic!("todo: vphsubsw"); }, - VPHSUBW => { panic!("todo: vphsubw"); }, + VPHSUBD => GENERAL_W_R_R, + VPHSUBSW => GENERAL_W_R_R, + VPHSUBW => GENERAL_W_R_R, VPINSRB => { panic!("todo: vpinsrb"); }, VPINSRD => { panic!("todo: vpinsrd"); }, VPINSRQ => { panic!("todo: vpinsrq"); }, @@ -3247,8 +3252,10 @@ fn opcode2behavior(opc: &Opcode) -> Option<BehaviorDigest> { VSUBPS => { panic!("todo: vsubps"); }, VSUBSD => { panic!("todo: vsubsd"); }, VSUBSS => { panic!("todo: vsubss"); }, - VTESTPD => { panic!("todo: vtestpd"); }, - VTESTPS => { panic!("todo: vtestps"); }, + VTESTPD => GENERAL_R_R + .set_flags_access(Access::Write), + VTESTPS => GENERAL_R_R + .set_flags_access(Access::Write), VUNPCKHPD => { panic!("todo: vunpckhpd"); }, VUNPCKHPS => { panic!("todo: vunpckhps"); }, VUNPCKLPD => { panic!("todo: vunpcklpd"); }, diff --git a/test/long_mode/behavior.rs b/test/long_mode/behavior.rs index 15e3660..19f6336 100644 --- a/test/long_mode/behavior.rs +++ b/test/long_mode/behavior.rs @@ -1520,6 +1520,66 @@ mod kvm { } } + #[test] + fn behavior_verify_kvm_vex() { + use yaxpeax_arch::{Decoder, U8Reader}; + use yaxpeax_x86::long_mode::Instruction; + + let mut vm = create_test_vm(); + vm.set_single_step(true).expect("can enable single-step"); + + // TODO: happen to be testing on a zen 5 system, so i picked a zen 5 decoder. + let decoder = long_mode::uarch::amd::zen5(); + let mut buf = Instruction::default(); + let initial_regs = vm.get_regs().unwrap(); + + #[allow(non_snake_case)] + for opcode in 0x00..=u8::MAX { + for prefix_bits in 0x00..0x400u16 { + let mmmmm = prefix_bits & 0b11111; + let prefix_1 = (0xe0 | mmmmm) as u8; + + let pp = (prefix_bits >> 5) & 0b11; + let W = (prefix_bits >> 7) & 1; + let L = (prefix_bits >> 8) & 1; + let prefix_2 = (0x78 | (W << 7) | (L << 2) | pp) as u8; + + let operands = (prefix_bits >> 9) & 0b1; + static OPC_BYTE_TABLE: [u8; 2] = [0xc1, 0x01]; + + let bytes: [u8; 5] = [0xc4, prefix_1, prefix_2, opcode, OPC_BYTE_TABLE[operands as usize]]; + let mut reader = U8Reader::new(&bytes); + if decoder.decode_into(&mut buf, &mut reader).is_ok() { + // two byte instructions were covered by `verify_kvm`, novel instructions are three + // bytes (or longer..?) + use yaxpeax_arch::LengthedInstruction; + let inst_len = 0.wrapping_offset(buf.len()) as usize; + if inst_len != bytes.len() { + continue; + } + + if not_generic(&buf) { + continue; + } + + let mut printed = false; + eprint!("checking behavior of "); + for b in bytes.iter() { + if printed { + eprint!(" "); + } + eprint!("{:02x}", b); + printed = true; + } + eprintln!(": {}", buf); + + vm.set_regs(&initial_regs).unwrap(); + check_behavior(&mut vm, &bytes[..inst_len]).expect("behavior check is ok"); + } + } + } + } + // use the generic test harness for a handful of instructions that don't get covered in the // general enumeration above #[test] |
