diff options
| author | iximeow <me@iximeow.net> | 2021-01-15 14:52:11 -0800 | 
|---|---|---|
| committer | iximeow <me@iximeow.net> | 2021-01-15 14:52:11 -0800 | 
| commit | f057c712f91b215034fe84fa0f22694aaa8dffb3 (patch) | |
| tree | 6c28340154b900ba984ccd188fd7149aa4de6d75 | |
| parent | 81e9b93aab9217cf7cb508f64b19fc1c0df024b5 (diff) | |
fix several missing or invalid decodings among 0f01 opcodes
* `mwaitx`, `monitorx`, `rdpru`, and `clzero` are now supported
* swapgs is no longer decoded in protected mode
* rdpkru and wrpkru are no longer decoded if mod bits != 11
| -rw-r--r-- | CHANGELOG | 6 | ||||
| -rw-r--r-- | src/long_mode/display.rs | 8 | ||||
| -rw-r--r-- | src/long_mode/mod.rs | 36 | ||||
| -rw-r--r-- | src/protected_mode/display.rs | 8 | ||||
| -rw-r--r-- | src/protected_mode/mod.rs | 42 | ||||
| -rw-r--r-- | test/long_mode/mod.rs | 42 | ||||
| -rw-r--r-- | test/protected_mode/mod.rs | 39 | 
7 files changed, 175 insertions, 6 deletions
@@ -1,3 +1,9 @@ +## 0.1.5 +* fix several issues around 0f01* opcode decoding; +  - AMD-only `monitorx`, `mwaitx`, `clzero`, and `rdpru` are now supported +  - `swapgs` is invalid in non-64-bit modes +  - `rdpkru` and `wrpkru` were incorrectly decoded when modrm bits were not `11` +  ## 0.1.4  * [long mode only]: fix decoding of rex-prefixed modrm+sib operands selecting index 0b100 and base 0b101    - for memory operands with a base, index, and displacement either diff --git a/src/long_mode/display.rs b/src/long_mode/display.rs index e653399..c3f4413 100644 --- a/src/long_mode/display.rs +++ b/src/long_mode/display.rs @@ -513,6 +513,8 @@ const MNEMONICS: &[&'static str] = &[      "vmxoff",      "monitor",      "mwait", +    "monitorx", +    "mwaitx",      "clac",      "stac",      "encls", @@ -527,6 +529,8 @@ const MNEMONICS: &[&'static str] = &[      "enclu",      "rdpkru",      "wrpkru", +    "rdpru", +    "clzero",      "rdseed",      "rdrand",      "addps", @@ -2152,6 +2156,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color              Opcode::VMXOFF |              Opcode::MONITOR |              Opcode::MWAIT | +            Opcode::MONITORX | +            Opcode::MWAITX |              Opcode::SKINIT |              Opcode::CLGI |              Opcode::STGI | @@ -2169,6 +2175,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color              Opcode::ENCLU |              Opcode::RDPKRU |              Opcode::WRPKRU | +            Opcode::RDPRU | +            Opcode::CLZERO |              Opcode::LAR => { write!(out, "{}", colors.platform_op(self)) }              Opcode::CRC32 | diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs index d165e38..f15e2a1 100644 --- a/src/long_mode/mod.rs +++ b/src/long_mode/mod.rs @@ -1131,6 +1131,8 @@ pub enum Opcode {      VMXOFF,      MONITOR,      MWAIT, +    MONITORX, +    MWAITX,      CLAC,      STAC,      ENCLS, @@ -1146,6 +1148,9 @@ pub enum Opcode {      RDPKRU,      WRPKRU, +    RDPRU, +    CLZERO, +      RDSEED,      RDRAND, @@ -3278,6 +3283,12 @@ impl InstDecoder {                      return Err(DecodeError::InvalidOpcode);                  }              } +            Opcode::MONITORX | Opcode::MWAITX | // these are gated on the `monitorx` and `mwaitx` cpuid bits, but are AMD-only. +            Opcode::CLZERO | Opcode::RDPRU => { // again, gated on specific cpuid bits, but AMD-only. +                if !self.amd_quirks() { +                    return Err(DecodeError::InvalidOpcode); +                } +            }              other => {                  if !self.bmi1() {                      if BMI1.contains(&other) { @@ -8049,6 +8060,14 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter                  instruction.operand_count = 1;                  instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, 2, length)?;              } else if r == 5 { +                let mod_bits = modrm >> 6; +                if mod_bits != 0b11 { +                    instruction.opcode = Opcode::Invalid; +                    instruction.operands[0] = OperandSpec::Nothing; +                    instruction.operand_count = 0; +                    return Err(DecodeError::InvalidOpcode); +                } +                  let m = modrm & 7;                  match m {                      0b110 => { @@ -8084,6 +8103,23 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter                          instruction.opcode = Opcode::RDTSCP;                          instruction.operands[0] = OperandSpec::Nothing;                          instruction.operand_count = 0; +                    } else if m == 2 { +                        instruction.opcode = Opcode::MONITORX; +                        instruction.operands[0] = OperandSpec::Nothing; +                        instruction.operand_count = 0; +                    } else if m == 3 { +                        instruction.opcode = Opcode::MWAITX; +                        instruction.operands[0] = OperandSpec::Nothing; +                        instruction.operand_count = 0; +                    } else if m == 4 { +                        instruction.opcode = Opcode::CLZERO; +                        instruction.operands[0] = OperandSpec::Nothing; +                        instruction.operand_count = 0; +                    } else if m == 5 { +                        instruction.opcode = Opcode::RDPRU; +                        instruction.operands[0] = OperandSpec::RegRRR; +                        instruction.modrm_rrr = RegSpec::ecx(); +                        instruction.operand_count = 1;                      } else {                          instruction.opcode = Opcode::Invalid;                          return Err(DecodeError::InvalidOpcode); diff --git a/src/protected_mode/display.rs b/src/protected_mode/display.rs index c92737b..a0161c2 100644 --- a/src/protected_mode/display.rs +++ b/src/protected_mode/display.rs @@ -504,6 +504,8 @@ const MNEMONICS: &[&'static str] = &[      "vmxoff",      "monitor",      "mwait", +    "monitorx", +    "mwaitx",      "clac",      "stac",      "encls", @@ -518,6 +520,8 @@ const MNEMONICS: &[&'static str] = &[      "enclu",      "rdpkru",      "wrpkru", +    "rdpru", +    "clzero",      "rdseed",      "rdrand",      "addps", @@ -2162,6 +2166,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color              Opcode::VMXOFF |              Opcode::MONITOR |              Opcode::MWAIT | +            Opcode::MONITORX | +            Opcode::MWAITX |              Opcode::SKINIT |              Opcode::CLGI |              Opcode::STGI | @@ -2179,6 +2185,8 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color              Opcode::ENCLU |              Opcode::RDPKRU |              Opcode::WRPKRU | +            Opcode::RDPRU | +            Opcode::CLZERO |              Opcode::ARPL |              Opcode::LAR => { write!(out, "{}", colors.platform_op(self)) } diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs index b60ff34..9327f64 100644 --- a/src/protected_mode/mod.rs +++ b/src/protected_mode/mod.rs @@ -1036,6 +1036,8 @@ pub enum Opcode {      VMXOFF,      MONITOR,      MWAIT, +    MONITORX, +    MWAITX,      CLAC,      STAC,      ENCLS, @@ -1051,6 +1053,9 @@ pub enum Opcode {      RDPKRU,      WRPKRU, +    RDPRU, +    CLZERO, +      RDSEED,      RDRAND, @@ -3191,6 +3196,12 @@ impl InstDecoder {                      return Err(DecodeError::InvalidOpcode);                  }              } +            Opcode::MONITORX | Opcode::MWAITX | // these are gated on the `monitorx` and `mwaitx` cpuid bits, but are AMD-only. +            Opcode::CLZERO | Opcode::RDPRU => { // again, gated on specific cpuid bits, but AMD-only. +                if !self.amd_quirks() { +                    return Err(DecodeError::InvalidOpcode); +                } +            }              other => {                  if !self.bmi1() {                      if BMI1.contains(&other) { @@ -7893,6 +7904,14 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter                  instruction.operand_count = 1;                  instruction.operands[0] = read_E(&mut bytes_iter, instruction, modrm, 2, length)?;              } else if r == 5 { +                let mod_bits = modrm >> 6; +                if mod_bits != 0b11 { +                    instruction.opcode = Opcode::Invalid; +                    instruction.operands[0] = OperandSpec::Nothing; +                    instruction.operand_count = 0; +                    return Err(DecodeError::InvalidOpcode); +                } +                  let m = modrm & 7;                  match m {                      0b110 => { @@ -7921,13 +7940,30 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter                  let m = modrm & 7;                  if mod_bits == 0b11 {                      if m == 0 { -                        instruction.opcode = Opcode::SWAPGS; -                        instruction.operands[0] = OperandSpec::Nothing; -                        instruction.operand_count = 0; +                        // swapgs is not valid in modes other than 64-bit +                        instruction.opcode = Opcode::Invalid; +                        return Err(DecodeError::InvalidOpcode);                      } else if m == 1 {                          instruction.opcode = Opcode::RDTSCP;                          instruction.operands[0] = OperandSpec::Nothing;                          instruction.operand_count = 0; +                    } else if m == 2 { +                        instruction.opcode = Opcode::MONITORX; +                        instruction.operands[0] = OperandSpec::Nothing; +                        instruction.operand_count = 0; +                    } else if m == 3 { +                        instruction.opcode = Opcode::MWAITX; +                        instruction.operands[0] = OperandSpec::Nothing; +                        instruction.operand_count = 0; +                    } else if m == 4 { +                        instruction.opcode = Opcode::CLZERO; +                        instruction.operands[0] = OperandSpec::Nothing; +                        instruction.operand_count = 0; +                    } else if m == 5 { +                        instruction.opcode = Opcode::RDPRU; +                        instruction.operands[0] = OperandSpec::RegRRR; +                        instruction.modrm_rrr = RegSpec::ecx(); +                        instruction.operand_count = 1;                      } else {                          instruction.opcode = Opcode::Invalid;                          return Err(DecodeError::InvalidOpcode); diff --git a/test/long_mode/mod.rs b/test/long_mode/mod.rs index 8489822..0830c2d 100644 --- a/test/long_mode/mod.rs +++ b/test/long_mode/mod.rs @@ -831,6 +831,9 @@ fn test_ssse3() {  fn test_0f01() {      // drawn heavily from "Table A-6.  Opcode Extensions for One- and Two-byte Opcodes by Group      // Number" +    for x in &[0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f] { +        test_invalid(&[0x0f, 0x01, *x]); +    }      test_display(&[0x0f, 0x01, 0x38], "invlpg [rax]");      test_display(&[0x0f, 0x01, 0x3f], "invlpg [rdi]");      test_display(&[0x0f, 0x01, 0x40, 0xff], "sgdt [rax - 0x1]"); @@ -839,18 +842,24 @@ fn test_0f01() {      test_display(&[0x0f, 0x01, 0x51, 0xff], "lgdt [rcx - 0x1]");      test_display(&[0x0f, 0x01, 0x59, 0xff], "lidt [rcx - 0x1]");      test_display(&[0x0f, 0x01, 0x61, 0xff], "smsw [rcx - 0x1]"); +    test_invalid(&[0x0f, 0x01, 0x69, 0xff]); +    test_display(&[0x0f, 0x01, 0x71, 0xff], "lmsw [rcx - 0x1]"); +    test_display(&[0x0f, 0x01, 0x79, 0xff], "invlpg [rcx - 0x1]");      test_display(&[0x0f, 0x01, 0xc0], "enclv");      test_display(&[0x0f, 0x01, 0xc1], "vmcall");      test_display(&[0x0f, 0x01, 0xc2], "vmlaunch");      test_display(&[0x0f, 0x01, 0xc3], "vmresume");      test_display(&[0x0f, 0x01, 0xc4], "vmxoff"); -    test_invalid(&[0x0f, 0x01, 0xc5]); +    test_invalid(&[0x0f, 0x01, 0xc5]); // TODO: TME would make this `pconfig`      test_invalid(&[0x0f, 0x01, 0xc6]);      test_invalid(&[0x0f, 0x01, 0xc7]);      test_display(&[0x0f, 0x01, 0xc8], "monitor");      test_display(&[0x0f, 0x01, 0xc9], "mwait");      test_display(&[0x0f, 0x01, 0xca], "clac");      test_display(&[0x0f, 0x01, 0xcb], "stac"); +    test_invalid(&[0x0f, 0x01, 0xcc]); +    test_invalid(&[0x0f, 0x01, 0xcd]); +    test_invalid(&[0x0f, 0x01, 0xce]);      test_display(&[0x0f, 0x01, 0xcf], "encls");      test_display(&[0x0f, 0x01, 0xd0], "xgetbv");      test_display(&[0x0f, 0x01, 0xd1], "xsetbv"); @@ -868,10 +877,41 @@ fn test_0f01() {      test_display(&[0x0f, 0x01, 0xdd], "clgi");      test_display(&[0x0f, 0x01, 0xde], "skinit eax");      test_display(&[0x0f, 0x01, 0xdf], "invlpga rax, ecx"); +    test_display(&[0x4f, 0x0f, 0x01, 0xe0], "smsw r8w"); +    test_display(&[0x0f, 0x01, 0xe0], "smsw ax"); +    test_display(&[0x0f, 0x01, 0xe1], "smsw cx"); +    test_display(&[0x0f, 0x01, 0xe2], "smsw dx"); +    test_display(&[0x0f, 0x01, 0xe3], "smsw bx"); +    test_display(&[0x0f, 0x01, 0xe4], "smsw sp"); +    test_display(&[0x0f, 0x01, 0xe5], "smsw bp"); +    test_display(&[0x0f, 0x01, 0xe6], "smsw si"); +    test_display(&[0x0f, 0x01, 0xe7], "smsw di"); +    test_invalid(&[0x0f, 0x01, 0xe8]); +    test_invalid(&[0x0f, 0x01, 0xe8]); +    test_invalid(&[0x0f, 0x01, 0xe9]); +    test_invalid(&[0x0f, 0x01, 0xea]); +    test_invalid(&[0x0f, 0x01, 0xeb]); +    test_invalid(&[0x0f, 0x01, 0xec]); +    test_invalid(&[0x0f, 0x01, 0xed]);      test_display(&[0x0f, 0x01, 0xee], "rdpkru");      test_display(&[0x0f, 0x01, 0xef], "wrpkru"); +    test_display(&[0x4f, 0x0f, 0x01, 0xf0], "lmsw r8w"); +    test_display(&[0x0f, 0x01, 0xf0], "lmsw ax"); +    test_display(&[0x0f, 0x01, 0xf1], "lmsw cx"); +    test_display(&[0x0f, 0x01, 0xf2], "lmsw dx"); +    test_display(&[0x0f, 0x01, 0xf3], "lmsw bx"); +    test_display(&[0x0f, 0x01, 0xf4], "lmsw sp"); +    test_display(&[0x0f, 0x01, 0xf5], "lmsw bp"); +    test_display(&[0x0f, 0x01, 0xf6], "lmsw si"); +    test_display(&[0x0f, 0x01, 0xf7], "lmsw di");      test_display(&[0x0f, 0x01, 0xf8], "swapgs");      test_display(&[0x0f, 0x01, 0xf9], "rdtscp"); +    test_display(&[0x0f, 0x01, 0xfa], "monitorx"); +    test_display(&[0x0f, 0x01, 0xfb], "mwaitx"); +    test_display(&[0x0f, 0x01, 0xfc], "clzero"); +    test_display(&[0x0f, 0x01, 0xfd], "rdpru ecx"); +    test_invalid(&[0x0f, 0x01, 0xfe]); +    test_invalid(&[0x0f, 0x01, 0xff]);  }  #[test] diff --git a/test/protected_mode/mod.rs b/test/protected_mode/mod.rs index dd0a51f..e3c7975 100644 --- a/test/protected_mode/mod.rs +++ b/test/protected_mode/mod.rs @@ -766,18 +766,24 @@ fn test_0f01() {      test_display(&[0x0f, 0x01, 0x51, 0xff], "lgdt [ecx - 0x1]");      test_display(&[0x0f, 0x01, 0x59, 0xff], "lidt [ecx - 0x1]");      test_display(&[0x0f, 0x01, 0x61, 0xff], "smsw [ecx - 0x1]"); +    test_invalid(&[0x0f, 0x01, 0x69, 0xff]); +    test_display(&[0x0f, 0x01, 0x71, 0xff], "lmsw [ecx - 0x1]"); +    test_display(&[0x0f, 0x01, 0x79, 0xff], "invlpg [ecx - 0x1]");      test_display(&[0x0f, 0x01, 0xc0], "enclv");      test_display(&[0x0f, 0x01, 0xc1], "vmcall");      test_display(&[0x0f, 0x01, 0xc2], "vmlaunch");      test_display(&[0x0f, 0x01, 0xc3], "vmresume");      test_display(&[0x0f, 0x01, 0xc4], "vmxoff"); -    test_invalid(&[0x0f, 0x01, 0xc5]); +    test_invalid(&[0x0f, 0x01, 0xc5]); // TODO: TME would make this `pconfig`      test_invalid(&[0x0f, 0x01, 0xc6]);      test_invalid(&[0x0f, 0x01, 0xc7]);      test_display(&[0x0f, 0x01, 0xc8], "monitor");      test_display(&[0x0f, 0x01, 0xc9], "mwait");      test_display(&[0x0f, 0x01, 0xca], "clac");      test_display(&[0x0f, 0x01, 0xcb], "stac"); +    test_invalid(&[0x0f, 0x01, 0xcc]); +    test_invalid(&[0x0f, 0x01, 0xcd]); +    test_invalid(&[0x0f, 0x01, 0xce]);      test_display(&[0x0f, 0x01, 0xcf], "encls");      test_display(&[0x0f, 0x01, 0xd0], "xgetbv");      test_display(&[0x0f, 0x01, 0xd1], "xsetbv"); @@ -795,10 +801,39 @@ fn test_0f01() {      test_display(&[0x0f, 0x01, 0xdd], "clgi");      test_display(&[0x0f, 0x01, 0xde], "skinit eax");      test_display(&[0x0f, 0x01, 0xdf], "invlpga eax, ecx"); +    test_display(&[0x0f, 0x01, 0xe0], "smsw ax"); +    test_display(&[0x0f, 0x01, 0xe1], "smsw cx"); +    test_display(&[0x0f, 0x01, 0xe2], "smsw dx"); +    test_display(&[0x0f, 0x01, 0xe3], "smsw bx"); +    test_display(&[0x0f, 0x01, 0xe4], "smsw sp"); +    test_display(&[0x0f, 0x01, 0xe5], "smsw bp"); +    test_display(&[0x0f, 0x01, 0xe6], "smsw si"); +    test_display(&[0x0f, 0x01, 0xe7], "smsw di"); +    test_invalid(&[0x0f, 0x01, 0xe8]); +    test_invalid(&[0x0f, 0x01, 0xe8]); +    test_invalid(&[0x0f, 0x01, 0xe9]); +    test_invalid(&[0x0f, 0x01, 0xea]); +    test_invalid(&[0x0f, 0x01, 0xeb]); +    test_invalid(&[0x0f, 0x01, 0xec]); +    test_invalid(&[0x0f, 0x01, 0xed]);      test_display(&[0x0f, 0x01, 0xee], "rdpkru");      test_display(&[0x0f, 0x01, 0xef], "wrpkru"); -    test_display(&[0x0f, 0x01, 0xf8], "swapgs"); +    test_display(&[0x0f, 0x01, 0xf0], "lmsw ax"); +    test_display(&[0x0f, 0x01, 0xf1], "lmsw cx"); +    test_display(&[0x0f, 0x01, 0xf2], "lmsw dx"); +    test_display(&[0x0f, 0x01, 0xf3], "lmsw bx"); +    test_display(&[0x0f, 0x01, 0xf4], "lmsw sp"); +    test_display(&[0x0f, 0x01, 0xf5], "lmsw bp"); +    test_display(&[0x0f, 0x01, 0xf6], "lmsw si"); +    test_display(&[0x0f, 0x01, 0xf7], "lmsw di"); +    test_invalid(&[0x0f, 0x01, 0xf8]);      test_display(&[0x0f, 0x01, 0xf9], "rdtscp"); +    test_display(&[0x0f, 0x01, 0xfa], "monitorx"); +    test_display(&[0x0f, 0x01, 0xfb], "mwaitx"); +    test_display(&[0x0f, 0x01, 0xfc], "clzero"); +    test_display(&[0x0f, 0x01, 0xfd], "rdpru ecx"); +    test_invalid(&[0x0f, 0x01, 0xfe]); +    test_invalid(&[0x0f, 0x01, 0xff]);  }  #[test]  | 
