CVE: CVE-2020-2902
Tested Versions:
- Microsoft Direct3D 9 Runtime version 10.0.17763.1
Product URL(s):
Description of the vulnerability
VirtualBox is a x86 and AMD64/Intel64 virtualization product for enterprise as well as home use. It is a solution commercially supported by Oracle, in addition to being made available as open source software. It runs on various host platforms like Windows, Linux, Mac and Solaris and also supports a large number of guest operating systems.
When we create a pixel shader using the CreatePixelShader()
method, the shader data buffer will be validated by CBaseShaderValidator::ValidateShader()
function. The lack of proper validation can result in a heap Out-of-Bounds write in the process that uses the runtime library.
The crash context with page heap enabled:
FAULTING_IP:
d3d9!CPShaderValidator30::Rule_ValidDCLInstruction+69b
00007ffa`de6311f7 4108040e or byte ptr [r14+rcx],al
EXCEPTION_RECORD: ffffffffffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 00007ffade6311f7 (d3d9!CPShaderValidator30::Rule_ValidDCLInstruction+0x000000000000069b)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 0000000000000001
Parameter[1]: 000002149f9ad161
Attempt to write to address 000002149f9ad161
CONTEXT: 0000000000000000 -- (.cxr 0x0;r)
rax=0000000000000000 rbx=000002149f7df880 rcx=000002149f9acfe0
rdx=0000003234f8f479 rsi=00007ffade6b7b20 rdi=000002149f7df900
rip=00007ffade6311f7 rsp=0000003234f8f3d0 rbp=0000000000000000
r8=000002149f9aafd8 r9=0000000000000000 r10=000002149f9cce00
r11=000002149f9cce80 r12=0000000000000004 r13=00000000000f0000
r14=0000000000000181 r15=0000000000000000
iopl=0 nv up ei pl nz na pe nc
cs=0033 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202
d3d9!CPShaderValidator30::Rule_ValidDCLInstruction+0x69b:
00007ffa`de6311f7 4108040e or byte ptr [r14+rcx],al ds:00000214`9f9ad161=??
FAULTING_THREAD: 0000000000000948
DEFAULT_BUCKET_ID: INVALID_POINTER_WRITE
PROCESS_NAME: d3d9_ShaderDefine.exe
ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.
EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%p referenced memory at 0x%p. The memory could not be %s.
EXCEPTION_PARAMETER1: 0000000000000001
EXCEPTION_PARAMETER2: 000002149f9ad161
WRITE_ADDRESS: 000002149f9ad161
FOLLOWUP_IP:
d3d9!CPShaderValidator30::Rule_ValidDCLInstruction+69b
00007ffa`de6311f7 4108040e or byte ptr [r14+rcx],al
NTGLOBALFLAG: 2000000
APPLICATION_VERIFIER_FLAGS: 0
APP: d3d9_shaderdefine.exe
ANALYSIS_VERSION: 6.3.9600.17237 (debuggers(dbg).140716-0327) amd64fre
PRIMARY_PROBLEM_CLASS: INVALID_POINTER_WRITE
BUGCHECK_STR: APPLICATION_FAULT_INVALID_POINTER_WRITE
LAST_CONTROL_TRANSFER: from 00007ffade62f8de to 00007ffade6311f7
STACK_TEXT:
00000032`34f8f3d0 00007ffa`de62f8de : 00000214`9f7df800 00000214`9f859000 00000000`ffff0300 00000214`00000000 : d3d9!CPShaderValidator30::Rule_ValidDCLInstruction+0x69b
00000032`34f8f460 00007ffa`de589afa : 00000214`9f7df880 00000214`9f859008 00000000`ffff0301 00000214`9f7df900 : d3d9!CPShaderValidator30::ApplyPerInstructionRules+0x9e
00000032`34f8f490 00007ffa`de589a04 : 00000214`9f7df880 00000000`00000014 00000000`ffff0300 00000214`9f7df880 : d3d9!CBaseShaderValidator::Instruction+0xc6
00000032`34f8f4d0 00007ffa`de589bc5 : 00000000`ffff0300 00000214`9f859004 00000000`00000000 00000032`34f8f5e0 : d3d9!CBaseShaderValidator::ValidateShader+0x34
00000032`34f8f510 00007ffa`de588c58 : 00000214`9f7ddf20 00000214`9f32f4a0 00000000`00000014 00007ffa`dd91f3a9 : d3d9!ValidatePixelShaderInternal+0x31
00000032`34f8f540 00007ff7`666210cf : 00000214`9f7ddf20 00007ffa`dd9cc500 00000214`9f32f4a0 00000214`9f7ddf20 : d3d9!CD3DBase::CreatePixelShader+0xc8
00000032`34f8f5e0 00007ff7`666212bd : 00000032`34f8f6b9 00000000`00001388 00000214`92349f10 00000000`00000000 : d3d9_ShaderDefine!shaderdefine+0xcf
00000032`34f8f610 00007ff7`66621583 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : d3d9_ShaderDefine!main+0x18d
00000032`34f8f720 00007ffb`14947974 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : d3d9_ShaderDefine!__tmainCRTStartup+0x10f
00000032`34f8f750 00007ffb`1725a271 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x14
00000032`34f8f780 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x21
STACK_COMMAND: .cxr 0x0 ; kb
SYMBOL_STACK_INDEX: 0
SYMBOL_NAME: d3d9!CPShaderValidator30::Rule_ValidDCLInstruction+69b
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: d3d9
IMAGE_NAME: d3d9.dll
DEBUG_FLR_IMAGE_TIMESTAMP: 0
FAILURE_BUCKET_ID: INVALID_POINTER_WRITE_c0000005_d3d9.dll!CPShaderValidator30::Rule_ValidDCLInstruction
BUCKET_ID: APPLICATION_FAULT_INVALID_POINTER_WRITE_d3d9!CPShaderValidator30::Rule_ValidDCLInstruction+69b
ANALYSIS_SOURCE: UM
FAILURE_ID_HASH_STRING: um:invalid_pointer_write_c0000005_d3d9.dll!cpshadervalidator30::rule_validdclinstruction
FAILURE_ID_HASH: {96c761fc-78c2-bbbb-d6cf-7c1572bff300}
Followup: MachineOwner
---------
0:000> lmvm d3d9
start end module name
00007ffa`de560000 00007ffa`de6fe000 d3d9 (pdb symbols) c:\symbols\d3d9.pdb\C52C34FEAD16F0483B42E69D4760E2191\d3d9.pdb
Loaded symbol image file: C:\WINDOWS\SYSTEM32\d3d9.dll
Image path: C:\WINDOWS\SYSTEM32\d3d9.dll
Image name: d3d9.dll
Timestamp: ***** Invalid (FC1B9E5E)
CheckSum: 001A7AF3
ImageSize: 0019E000
File version: 10.0.17763.1
Product version: 10.0.17763.1
File flags: 0 (Mask 3F)
File OS: 40004 NT Win32
File type: 2.0 Dll
File date: 00000000.00000000
Translations: 0409.04b0
CompanyName: Microsoft Corporation
ProductName: Microsoft® Windows® Operating System
InternalName: D3D9.dll
OriginalFilename: D3D9.dll
ProductVersion: 10.0.17763.1
FileVersion: 10.0.17763.1 (WinBuild.160101.0800)
FileDescription: Direct3D 9 Runtime
LegalCopyright: © Microsoft Corporation. All rights reserved.
0:000> dt ntdll!_DPH_BLOCK_INFORMATION 00000214`9f9acf90
+0x000 StartStamp : 0xabcdbbbb
+0x008 Heap : 0x00000214`91c61000 Void
+0x010 RequestedSize : 0x22
+0x018 ActualSize : 0x1000
+0x020 FreeQueue : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ]
+0x020 FreePushList : _SLIST_ENTRY
+0x020 TraceIndex : 0
+0x030 StackTrace : 0x00000214`90566420 Void
+0x038 Padding : 0
+0x03c EndStamp : 0xdcbabbbb
When validating the DCL shader instruction, we come to the function CPShaderValidator30::Rule_ValidDCLInstruction()
:
char __fastcall CPShaderValidator30::Rule_ValidDCLInstruction(struct_CBaseShaderValidator *this)
{
....
if ( !this->a37 )
{
CBaseShaderValidator::Spew(this, 2u, 0x129u, "def and dcl instructions must appear before other instructions.");
ins = this->ins;
++this->a15;
v58 = ins;
}
regNum = ins->dstParam.RegNum;
....
v13 = this->a33;
v57 = v13;
....
count = 4i64;
....
v49 = &v62;
v50 = (v57 + 12);
do
{
reg = *v50;
++v50;
value = *v49++;
reg[regNum] |= value; // regNum controlled, reg buffer only 0x22 bytes
--count;
}
while ( count );
return 1;
}
regNum
is assigned by this->ins->dstParam.RegNum
which was initialized in CBaseShaderValidator::DecodeDstParam()
:
void __fastcall CBaseShaderValidator::DecodeDstParam(CBaseShaderValidator *this, struct_dstParam *ins, unsigned int token)
{
ins->gap0 = 1;
RegNum = D3DSI_GETREGNUM_RESOLVING_CONSTANTS(token);
ins->RegNum = RegNum;
ins->dword8 = v5 & 0xF0000;
ins->dwordC = v5 & 0xF00000;
ins->dword10 = (v5 >> 24) & 0xF;
v6 = D3DSI_GETREGTYPE_RESOLVING_CONSTANTS(v5);
ins->RegType = v6;
ins->dword1C = v7 & 0x2000;
}
RegNum
is calculated from token value, which comes from the input shader buffer:
__int64 __fastcall D3DSI_GETREGNUM_RESOLVING_CONSTANTS(unsigned int a1)
{
__int64 result; // rax
result = a1 & 0x7FF;
switch ( (a1 & 0x1800 | (a1 >> 20) & 0x700) >> 8 )
{
case 0xBu:
return (result + 2048);
case 0xCu:
return (result + 4096);
case 0xDu:
result = (result + 6144);
break;
}
return result;
}
So we can control the value of ins->dstParam.RegNum
with out any validation and trigger a heap out-of-bounds write in PShaderValidator30::Rule_ValidDCLInstruction()
:
char __fastcall CPShaderValidator30::Rule_ValidDCLInstruction(struct_CBaseShaderValidator *this)
{
....
do
{
reg = *v50;
++v50;
value = *v49++;
reg[regNum] |= value; // regNum controlled, reg buffer only 0x22 bytes
--count;
}
while ( count );
return 1;
}
reg
buffer was allocated with size 0x22
:
0:000> dt ntdll!_DPH_BLOCK_INFORMATION 00000214`9f9acf90
+0x000 StartStamp : 0xabcdbbbb
+0x008 Heap : 0x00000214`91c61000 Void
+0x010 RequestedSize : 0x22
+0x018 ActualSize : 0x1000
+0x020 FreeQueue : _LIST_ENTRY [ 0x00000000`00000000 - 0x00000000`00000000 ]
+0x020 FreePushList : _SLIST_ENTRY
+0x020 TraceIndex : 0
+0x030 StackTrace : 0x00000214`90566420 Void
+0x038 Padding : 0
+0x03c EndStamp : 0xdcbabbbb
Timeline:
- 2019-12-04 Disclosed via ZDI
- 2021-07-24 Vendor patched
Vendor Response
The vendor has acknowledged the issue and released an update to address it.
The vendor advisory can be found here: https://www.oracle.com/security-alerts/cpuapr2020.html.