From 470ddb9a0329a8f1823674bca2c108e012ca2780 Mon Sep 17 00:00:00 2001 From: iximeow Date: Fri, 17 Apr 2026 02:22:24 +0000 Subject: more precise about 0f0d prefetch/nop --- CHANGELOG | 1 + src/long_mode/mod.rs | 14 +++++++------- src/protected_mode/mod.rs | 18 +++++++----------- src/real_mode/mod.rs | 18 +++++++----------- test/long_mode/mod.rs | 2 ++ test/protected_mode/mod.rs | 2 ++ test/real_mode/mod.rs | 2 ++ 7 files changed, 28 insertions(+), 29 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 540a5bf..1b82028 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -18,6 +18,7 @@ zero-extended to 64 bits for storage. writing to "eax" in this way implies the 32->64 bit zero-extend, whereas writing to "ax" does not imply any zero-extension. mov reg-to-seg is unchanged and uses a 16-bit form for source GPR. +* reject 0f0d prefetch/nop with a register operand, which was incorrectly decoded before. ## 2.0.0 diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs index 4288eb9..5e8dafa 100644 --- a/src/long_mode/mod.rs +++ b/src/long_mode/mod.rs @@ -7223,8 +7223,6 @@ fn read_operands< OperandCase::ModRM_0x0f0d => { let r = instruction.regs[0].num & 0b111; - let bank = bank_from_prefixes_64(SizeCode::vq, instruction.prefixes); - match r { 1 => { instruction.opcode = Opcode::PREFETCHW; @@ -7233,12 +7231,14 @@ fn read_operands< instruction.opcode = Opcode::NOP; } } - instruction.operands[0] = mem_oper; - if instruction.operands[0] != OperandSpec::RegMMM { - instruction.mem_size = 64; - } else { - instruction.regs[1].bank = bank; + if mem_oper == OperandSpec::RegMMM { + // *found* this from running `0f0dc0` under KVM on a Zen 5 system. this is + // consistent with the register number being used to pick kinds of prefetch. + return Err(DecodeError::InvalidOperand); } + + instruction.operands[0] = mem_oper; + instruction.mem_size = 64; instruction.operand_count = 1; } OperandCase::ModRM_0x0f0f => { diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs index 89c5dfd..eeb2942 100644 --- a/src/protected_mode/mod.rs +++ b/src/protected_mode/mod.rs @@ -7087,12 +7087,6 @@ fn read_operands< OperandCase::ModRM_0x0f0d => { let r = instruction.regs[0].num & 0b111; - let bank = if instruction.prefixes.operand_size() { - RegisterBank::W - } else { - RegisterBank::D - }; - match r { 1 => { instruction.opcode = Opcode::PREFETCHW; @@ -7101,12 +7095,14 @@ fn read_operands< instruction.opcode = Opcode::NOP; } } - instruction.operands[0] = mem_oper; - if instruction.operands[0] != OperandSpec::RegMMM { - instruction.mem_size = 64; - } else { - instruction.regs[1].bank = bank; + if mem_oper == OperandSpec::RegMMM { + // *found* this from running `0f0dc0` under KVM on a Zen 5 system. this is + // consistent with the register number being used to pick kinds of prefetch. + return Err(DecodeError::InvalidOperand); } + + instruction.operands[0] = mem_oper; + instruction.mem_size = 64; instruction.operand_count = 1; } OperandCase::ModRM_0x0f0f => { diff --git a/src/real_mode/mod.rs b/src/real_mode/mod.rs index 559cac3..cab3fb1 100644 --- a/src/real_mode/mod.rs +++ b/src/real_mode/mod.rs @@ -7126,12 +7126,6 @@ fn read_operands< OperandCase::ModRM_0x0f0d => { let r = instruction.regs[0].num & 0b111; - let bank = if !instruction.prefixes.operand_size() { - RegisterBank::W - } else { - RegisterBank::D - }; - match r { 1 => { instruction.opcode = Opcode::PREFETCHW; @@ -7140,12 +7134,14 @@ fn read_operands< instruction.opcode = Opcode::NOP; } } - instruction.operands[0] = mem_oper; - if instruction.operands[0] != OperandSpec::RegMMM { - instruction.mem_size = 64; - } else { - instruction.regs[1].bank = bank; + if mem_oper == OperandSpec::RegMMM { + // *found* this from running `0f0dc0` under KVM on a Zen 5 system. this is + // consistent with the register number being used to pick kinds of prefetch. + return Err(DecodeError::InvalidOperand); } + + instruction.operands[0] = mem_oper; + instruction.mem_size = 64; instruction.operand_count = 1; } OperandCase::ModRM_0x0f0f => { diff --git a/test/long_mode/mod.rs b/test/long_mode/mod.rs index 0b6dcc6..6d12c8d 100644 --- a/test/long_mode/mod.rs +++ b/test/long_mode/mod.rs @@ -2852,6 +2852,8 @@ fn test_adx() { #[test] fn test_prefetchw() { test_display(&[0x0f, 0x0d, 0x08], "prefetchw zmmword [rax]"); + test_display(&[0x0f, 0x0d, 0x00], "nop zmmword [rax]"); + test_invalid(&[0x0f, 0x0d, 0xc0]); } #[test] diff --git a/test/protected_mode/mod.rs b/test/protected_mode/mod.rs index d5533cc..800c5ec 100644 --- a/test/protected_mode/mod.rs +++ b/test/protected_mode/mod.rs @@ -2500,6 +2500,8 @@ fn test_adx() { #[test] fn test_prefetchw() { test_display(&[0x0f, 0x0d, 0x08], "prefetchw zmmword [eax]"); + test_display(&[0x0f, 0x0d, 0x00], "nop zmmword [eax]"); + test_invalid(&[0x0f, 0x0d, 0xc0]); } #[test] diff --git a/test/real_mode/mod.rs b/test/real_mode/mod.rs index af8bfaf..b866aa0 100644 --- a/test/real_mode/mod.rs +++ b/test/real_mode/mod.rs @@ -170,6 +170,8 @@ fn test_real_mode() { test_display(&[0x0f, 0x06], "clts"); test_display(&[0x0f, 0x07], "sysret"); test_display(&[0x0f, 0x0d, 0x08], "prefetchw zmmword [bx + si * 1]"); + test_display(&[0x0f, 0x0d, 0x00], "nop zmmword [bx + si * 1]"); + test_invalid(&[0x0f, 0x0d, 0xc0]); test_display(&[0x0f, 0x0f, 0x38, 0x8e], "pfpnacc mm7, qword [bx + si * 1]"); test_display(&[0x0f, 0x0f, 0xc6, 0xb7], "pmulhrw mm0, mm6"); test_display(&[0x0f, 0x0f, 0xe0, 0x8a], "pfnacc mm4, mm0"); -- cgit v1.1