When I run the following assembly sequence in a kernel mode of Windows 7 x64 running in a VMWare Workstation virtual machine:
xor eax, eax
mov ax, gs
mov gs, ax ; this instruction
that last mov gs, ax
instruction immediately causes that VM to crash (or possibly bug-check) with the following pop-up message:
A fault has occurred causing a virtual CPU to enter the shutdown state. If this fault had occurred outside of a virtual machine, it would have caused the physical machine to restart. The shutdown state can be reached by incorrectly configuring the virtual machine, a bug in the guest operating system, or a problem in VMWare Workstation.
Does re-loading gs
register as such cause the issue in the kernel, or is it a virtualization issue?
I don't see anything unusual about that mov
instruction in the Intel manual.
PS. By the way, replacing gs
register with fs
does not cause this fault.
Edit: To answer the question about the state of the segment descriptor in GDT. Here it is:
0: kd> r gs
gs=002b
0: kd> dg 28
P Si Gr Pr Lo
Sel Base Limit Type l ze an es ng Flags
---- ----------------- ----------------- ---------- - -- -- -- -- --------
0028 00000000`00000000 00000000`ffffffff Data RW Ac 3 Bg Pg P Nl 00000cf3
I'm not sure why the move mov gs,ax
would cause Windows to immediately triple fault, but it would soon enough cause it to crash. In the 64-bit Windows kernel the GS segment is used as a pointer to access the current CPU's Processor Control Region (PCR). Each CPU has a different GS base value pointing to a different PCR. Your mov ax,gs
mov gs,ax
sequence actually breaks this because it loads an incorrect value for the GS base into the descriptor cache.
The GDT doesn't actually contain the correct base for the GS register. Since the GDT can only hold 32-bit addresses, it's not actually used to load the GS base. Instead the IA32_GS_BASE and IA32_KERNEL_GS_BASE MSRs, the later in combination with the SWAPGS instruction, are used to set a 64-bit base address for the GS segment. The selector value stored in the GS register is just a dummy value.
So your mov gs,ax
instruction loads the dummy 32-bit base value stored in the GDT, not the 64-bit value stored in IA32_GS_BASE. This means that the base address of the GS segment is set to 0 and not to the address of the PCR for the current CPU. After loading this incorrect GS base it's only a matter of time before the Windows kernel tries to use the GS register to access the PCR (with an instruction like mov rax, gs:[10]
) and ends up reading what is probably unmapped memory causing an unexpected kernel page fault and crashing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With