aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2026-04-17 02:22:24 +0000
committeriximeow <me@iximeow.net>2026-05-25 00:59:27 +0000
commit470ddb9a0329a8f1823674bca2c108e012ca2780 (patch)
treec5ccc556fdde40ad263caff9dbdb6427597d0a5b
parenta049351c5d512710f557ffb45ee6391fc86a3dc6 (diff)
more precise about 0f0d prefetch/nop
-rw-r--r--CHANGELOG1
-rw-r--r--src/long_mode/mod.rs14
-rw-r--r--src/protected_mode/mod.rs18
-rw-r--r--src/real_mode/mod.rs18
-rw-r--r--test/long_mode/mod.rs2
-rw-r--r--test/protected_mode/mod.rs2
-rw-r--r--test/real_mode/mod.rs2
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");