zydis icon indicating copy to clipboard operation
zydis copied to clipboard

Instructions missing in Zydis: 64-bit RDSSPD, VMGEXIT, AltMovCr8

Open tremalrik opened this issue 3 years ago • 3 comments

Doing a bunch of tests with Zydis, I've identified instructions that it doesn't decode properly:

  • RDSSPD in 64-bit mode. "ZydisInfo -64 f3 0f 1e c8" returns "nop eax,ecx" and not the expected "rdsspd eax". This is surprising, since the closely related instruction RDSSPQ works fine ("ZydisInfo -64 f3 48 0f 1e c8" returns the expected "rdsspq rax")
  • VMGEXIT (AMD system instruction). "ZydisInfo -64 f3 0f 01 d9" returns "vmmcall" rather than the expected "vmgexit". Given that older AMD processors will, according to the AMD APM, treat the instruction as vmmcall, it might be a suitable candidate for a ZydisDecoderMode flag, similar to how wbinvd/wbnoinvd is handled.
  • On AMD processors with the "AltMovCr8" feature, it is possible to prefix a MOV to/from CR0 with a LOCK prefix; this will modify the instruction into a MOV to/from CR8. As such, e.g. F0 0F 20 00 should probably be decoded as "mov eax,cr8"/"mov rax,cr8" rather than the ILLEGAL_LOCK error that Zydis is currently returning.

tremalrik avatar Apr 27 '21 23:04 tremalrik

RDSSPD

Fixed with https://github.com/zyantific/zydis/commit/25193db008e8799ff59fd655c2a26b2ffd79d40d

In the 64-bit mode branch the definition for REX.w == 0 was missing.

VMGEXIT

Will add a decoder mode for this one :-)

AltMovCr8

Oh, this one I was not aware of. I will leave this issue open and follow your suggestion to add a new decoder-mode. It requires some further adjustments to the code that validates the LOCK prefix and the register constraints as well as a new filter, so might take a while.

flobernd avatar Apr 28 '21 06:04 flobernd

For AltMovCr8, I decided to do a few tests on an AMD EPYC instance. Given how this feature works - using the LOCK prefix to get access to a register that one would normally use the 64-bit-only REX.R bit to access - it seemed to me natural to ask whether the processor just treated LOCK as an alias for REX.R for the MOV CRx opcodes. This doesn't turn out to be the case - placing both LOCK and REX.R on a MOV CRx opcode results in an #UD on the EPYC. A few tests and their results (from user-mode, where valid variants produce #GP and invalid variants produce #UD):

  • mov rax, cr0 : #GP
  • mov rax, cr1 : #UD
  • mov rax, cr2 : #GP
  • mov rax, cr8 : #GP
  • mov r8, cr8 : #GP : no surprises so far
  • lock mov rax, cr0 : #GP : AltMovCr8 shows up
  • lock mov r8, cr0 : #GP : For AltMovCr8, LOCK + a REX prefix without REX.R is valid
  • lock mov rax, cr2 : #UD : (LOCK is not simply ignored for these opcodes)
  • lock mov rax, cr8 : #UD : For AltMovCr8, LOCK + REX.R is not valid
  • lock mov rax, cr10 : #UD

tremalrik avatar Sep 10 '21 09:09 tremalrik

I have been testing this on baremetal Ryzen 9 5900HS, and can confirm @tremalrik's data: #UD's as described and no exceptions where he had #GP (since it was running in ring 0).

jfhs avatar Feb 05 '22 02:02 jfhs