zydis
zydis copied to clipboard
Instructions missing in Zydis: 64-bit RDSSPD, VMGEXIT, AltMovCr8
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.
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.
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
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).