Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why this mov gs instruction causes a fault in VMWare Workstation guest running Windows 7 OS?

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

enter image description here

that last mov gs, ax instruction immediately causes that VM to crash (or possibly bug-check) with the following pop-up message:

enter image description here

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
like image 326
c00000fd Avatar asked Jan 27 '23 20:01

c00000fd


1 Answers

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.

like image 133
Ross Ridge Avatar answered Jan 30 '23 09:01

Ross Ridge