Skip to content

Commit 94caf73

Browse files
committed
Heap safety and memory leak fixes for CDirect3DEvents9
g_pActiveShader Reference Counting (UAF) GetRealVertexBuffer & GetRealIndexBuffer: Per-frame leak during rendering SetVertexDeclaration: Leak on vertex declaration changes DiscoverReadableDepthFormat: Initialization leak
1 parent 2566ac6 commit 94caf73

File tree

5 files changed

+138
-60
lines changed

5 files changed

+138
-60
lines changed

Client/core/DXHook/CDirect3DEvents9.cpp

Lines changed: 134 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,35 @@ static EDiagnosticDebugType ms_DiagnosticDebug = EDiagnosticDebug::NONE;
3232
// To reuse shader setups between calls to DrawIndexedPrimitive
3333
CShaderItem* g_pActiveShader = NULL;
3434

35+
namespace
36+
{
37+
struct SResolvedShaderState
38+
{
39+
CShaderInstance* pInstance = nullptr;
40+
CEffectWrap* pEffectWrap = nullptr;
41+
ID3DXEffect* pEffect = nullptr;
42+
};
43+
44+
bool TryResolveShaderState(CShaderItem* pShaderItem, SResolvedShaderState& outState)
45+
{
46+
if (!pShaderItem)
47+
return false;
48+
49+
CShaderInstance* pInstance = pShaderItem->m_pShaderInstance;
50+
if (!pInstance)
51+
return false;
52+
53+
CEffectWrap* pEffectWrap = pInstance->m_pEffectWrap;
54+
if (!pEffectWrap)
55+
return false;
56+
57+
outState.pInstance = pInstance;
58+
outState.pEffectWrap = pEffectWrap;
59+
outState.pEffect = pEffectWrap->m_pD3DEffect;
60+
return true;
61+
}
62+
}
63+
3564
bool CDirect3DEvents9::IsDeviceOperational(IDirect3DDevice9* pDevice, bool* pbTemporarilyLost, HRESULT* pHrCooperativeLevel)
3665
{
3766
if (pHrCooperativeLevel)
@@ -375,31 +404,37 @@ HRESULT CDirect3DEvents9::DrawPrimitiveShader(IDirect3DDevice9* pDevice, D3DPRIM
375404
else
376405
{
377406
// Yes shader for this texture
378-
CShaderInstance* pShaderInstance = pShaderItem->m_pShaderInstance;
407+
SResolvedShaderState shaderState;
408+
if (!TryResolveShaderState(pShaderItem, shaderState) || !shaderState.pEffect)
409+
{
410+
if (!bIsLayer)
411+
return DrawPrimitiveGuarded(pDevice, PrimitiveType, StartVertex, PrimitiveCount);
412+
return D3D_OK;
413+
}
414+
415+
CShaderInstance* pShaderInstance = shaderState.pInstance;
416+
CEffectWrap* pEffectWrap = shaderState.pEffectWrap;
417+
ID3DXEffect* pD3DEffect = shaderState.pEffect;
379418

380419
// Apply custom parameters
381420
pShaderInstance->ApplyShaderParameters();
382421
// Apply common parameters
383-
pShaderInstance->m_pEffectWrap->ApplyCommonHandles();
422+
pEffectWrap->ApplyCommonHandles();
384423
// Apply mapped parameters
385-
pShaderInstance->m_pEffectWrap->ApplyMappedHandles();
424+
pEffectWrap->ApplyMappedHandles();
386425

387426
// Remember vertex shader if original draw was using it
388427
IDirect3DVertexShader9* pOriginalVertexShader = NULL;
389428
pDevice->GetVertexShader(&pOriginalVertexShader);
390429

391430
// Do shader passes
392-
ID3DXEffect* pD3DEffect = pShaderInstance->m_pEffectWrap->m_pD3DEffect;
393-
bool bEffectDeviceTemporarilyLost = false;
394-
bool bEffectDeviceOperational = true;
395-
if (pD3DEffect)
431+
bool bEffectDeviceTemporarilyLost = false;
432+
bool bEffectDeviceOperational = true;
433+
IDirect3DDevice9* pEffectDevice = nullptr;
434+
if (SUCCEEDED(pD3DEffect->GetDevice(&pEffectDevice)) && pEffectDevice)
396435
{
397-
IDirect3DDevice9* pEffectDevice = nullptr;
398-
if (SUCCEEDED(pD3DEffect->GetDevice(&pEffectDevice)) && pEffectDevice)
399-
{
400-
bEffectDeviceOperational = IsDeviceOperational(pEffectDevice, &bEffectDeviceTemporarilyLost);
401-
SAFE_RELEASE(pEffectDevice);
402-
}
436+
bEffectDeviceOperational = IsDeviceOperational(pEffectDevice, &bEffectDeviceTemporarilyLost);
437+
SAFE_RELEASE(pEffectDevice);
403438
}
404439

405440
if (!bEffectDeviceOperational)
@@ -412,7 +447,7 @@ HRESULT CDirect3DEvents9::DrawPrimitiveShader(IDirect3DDevice9* pDevice, D3DPRIM
412447

413448
DWORD dwFlags = D3DXFX_DONOTSAVESHADERSTATE; // D3DXFX_DONOTSAVE(SHADER|SAMPLER)STATE
414449
uint uiNumPasses = 0;
415-
HRESULT hrBegin = pShaderInstance->m_pEffectWrap->Begin(&uiNumPasses, dwFlags);
450+
HRESULT hrBegin = pEffectWrap->Begin(&uiNumPasses, dwFlags);
416451
if (FAILED(hrBegin) || uiNumPasses == 0)
417452
{
418453
if (FAILED(hrBegin) && hrBegin != D3DERR_DEVICELOST && hrBegin != D3DERR_DEVICENOTRESET)
@@ -460,7 +495,7 @@ HRESULT CDirect3DEvents9::DrawPrimitiveShader(IDirect3DDevice9* pDevice, D3DPRIM
460495
bCompletedAnyPass = true;
461496
}
462497

463-
HRESULT hrEnd = pShaderInstance->m_pEffectWrap->End(bEffectDeviceOperational && !bEncounteredDeviceLoss);
498+
HRESULT hrEnd = pEffectWrap->End(bEffectDeviceOperational && !bEncounteredDeviceLoss);
464499
if (FAILED(hrEnd) && hrEnd != D3DERR_DEVICELOST && hrEnd != D3DERR_DEVICENOTRESET)
465500
WriteDebugEvent(SString("DrawPrimitiveShader: End failed %08x", hrEnd));
466501

@@ -601,23 +636,30 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
601636
// See if we should use the previously setup shader
602637
if (g_pActiveShader)
603638
{
639+
SResolvedShaderState activeState;
640+
if (!TryResolveShaderState(g_pActiveShader, activeState) || !activeState.pEffect)
641+
{
642+
CloseActiveShader(false);
643+
if (!bIsLayer)
644+
return DrawIndexedPrimitiveGuarded(pDevice, PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
645+
return D3D_OK;
646+
}
647+
604648
dassert(pShaderItem == g_pActiveShader);
605649
g_pDeviceState->FrameStats.iNumShadersReuseSetup++;
606650

607651
// Transfer any state changes to the active shader, but ensure the device still accepts work
608-
CShaderInstance* pShaderInstance = g_pActiveShader->m_pShaderInstance;
609-
ID3DXEffect* pActiveEffect = pShaderInstance->m_pEffectWrap->m_pD3DEffect;
652+
CShaderInstance* pShaderInstance = activeState.pInstance;
653+
CEffectWrap* pActiveEffectWrap = activeState.pEffectWrap;
654+
ID3DXEffect* pActiveEffect = activeState.pEffect;
610655

611656
bool bDeviceTemporarilyLost = false;
612657
bool bDeviceOperational = true;
613-
if (pActiveEffect)
658+
IDirect3DDevice9* pEffectDevice = nullptr;
659+
if (SUCCEEDED(pActiveEffect->GetDevice(&pEffectDevice)) && pEffectDevice)
614660
{
615-
IDirect3DDevice9* pEffectDevice = nullptr;
616-
if (SUCCEEDED(pActiveEffect->GetDevice(&pEffectDevice)) && pEffectDevice)
617-
{
618-
bDeviceOperational = IsDeviceOperational(pEffectDevice, &bDeviceTemporarilyLost);
619-
SAFE_RELEASE(pEffectDevice);
620-
}
661+
bDeviceOperational = IsDeviceOperational(pEffectDevice, &bDeviceTemporarilyLost);
662+
SAFE_RELEASE(pEffectDevice);
621663
}
622664

623665
if (!bDeviceOperational)
@@ -626,11 +668,11 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
626668
return D3D_OK;
627669
}
628670

629-
bool bChanged = pShaderInstance->m_pEffectWrap->ApplyCommonHandles();
630-
bChanged |= pShaderInstance->m_pEffectWrap->ApplyMappedHandles();
671+
bool bChanged = pActiveEffectWrap->ApplyCommonHandles();
672+
bChanged |= pActiveEffectWrap->ApplyMappedHandles();
631673
if (bChanged)
632674
{
633-
HRESULT hrCommit = pShaderInstance->m_pEffectWrap->m_pD3DEffect->CommitChanges();
675+
HRESULT hrCommit = pActiveEffect->CommitChanges();
634676
if (FAILED(hrCommit))
635677
{
636678
if (hrCommit != D3DERR_DEVICELOST && hrCommit != D3DERR_DEVICENOTRESET)
@@ -645,11 +687,21 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
645687
g_pDeviceState->FrameStats.iNumShadersFullSetup++;
646688

647689
// Yes shader for this texture
648-
CShaderInstance* pShaderInstance = pShaderItem->m_pShaderInstance;
690+
SResolvedShaderState shaderState;
691+
if (!TryResolveShaderState(pShaderItem, shaderState) || !shaderState.pEffect)
692+
{
693+
if (!bIsLayer)
694+
return DrawIndexedPrimitiveGuarded(pDevice, PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
695+
return D3D_OK;
696+
}
697+
698+
CShaderInstance* pShaderInstance = shaderState.pInstance;
699+
CEffectWrap* pEffectWrap = shaderState.pEffectWrap;
700+
ID3DXEffect* pD3DEffect = shaderState.pEffect;
649701

650702
// Add normal stream if shader wants it
651703
CAdditionalVertexStreamManager* pAdditionalStreamManager = CAdditionalVertexStreamManager::GetExistingSingleton();
652-
if (pShaderInstance->m_pEffectWrap->m_pEffectTemplate->m_bRequiresNormals && pAdditionalStreamManager)
704+
if (pEffectWrap->m_pEffectTemplate->m_bRequiresNormals && pAdditionalStreamManager)
653705
{
654706
// Find/create/set additional vertex stream
655707
pAdditionalStreamManager->MaybeSetAdditionalVertexStream(PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
@@ -658,26 +710,22 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
658710
// Apply custom parameters
659711
pShaderInstance->ApplyShaderParameters();
660712
// Apply common parameters
661-
pShaderInstance->m_pEffectWrap->ApplyCommonHandles();
713+
pEffectWrap->ApplyCommonHandles();
662714
// Apply mapped parameters
663-
pShaderInstance->m_pEffectWrap->ApplyMappedHandles();
715+
pEffectWrap->ApplyMappedHandles();
664716

665717
// Remember vertex shader if original draw was using it
666718
IDirect3DVertexShader9* pOriginalVertexShader = NULL;
667719
pDevice->GetVertexShader(&pOriginalVertexShader);
668720

669721
// Do shader passes
670-
ID3DXEffect* pD3DEffect = pShaderInstance->m_pEffectWrap->m_pD3DEffect;
671-
bool bEffectDeviceTemporarilyLost = false;
672-
bool bEffectDeviceOperational = true;
673-
if (pD3DEffect)
722+
bool bEffectDeviceTemporarilyLost = false;
723+
bool bEffectDeviceOperational = true;
724+
IDirect3DDevice9* pEffectDevice = nullptr;
725+
if (SUCCEEDED(pD3DEffect->GetDevice(&pEffectDevice)) && pEffectDevice)
674726
{
675-
IDirect3DDevice9* pEffectDevice = nullptr;
676-
if (SUCCEEDED(pD3DEffect->GetDevice(&pEffectDevice)) && pEffectDevice)
677-
{
678-
bEffectDeviceOperational = IsDeviceOperational(pEffectDevice, &bEffectDeviceTemporarilyLost);
679-
SAFE_RELEASE(pEffectDevice);
680-
}
727+
bEffectDeviceOperational = IsDeviceOperational(pEffectDevice, &bEffectDeviceTemporarilyLost);
728+
SAFE_RELEASE(pEffectDevice);
681729
}
682730

683731
if (!bEffectDeviceOperational)
@@ -692,7 +740,7 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
692740

693741
DWORD dwFlags = D3DXFX_DONOTSAVESHADERSTATE; // D3DXFX_DONOTSAVE(SHADER|SAMPLER)STATE
694742
uint uiNumPasses = 0;
695-
HRESULT hrBegin = pShaderInstance->m_pEffectWrap->Begin(&uiNumPasses, dwFlags);
743+
HRESULT hrBegin = pEffectWrap->Begin(&uiNumPasses, dwFlags);
696744
if (FAILED(hrBegin) || uiNumPasses == 0)
697745
{
698746
if (FAILED(hrBegin) && hrBegin != D3DERR_DEVICELOST && hrBegin != D3DERR_DEVICENOTRESET)
@@ -733,6 +781,7 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
733781
{
734782
// Make this the active shader for possible reuse
735783
dassert(dwFlags == D3DXFX_DONOTSAVESHADERSTATE);
784+
pShaderItem->AddRef();
736785
g_pActiveShader = pShaderItem;
737786
SAFE_RELEASE(pOriginalVertexShader);
738787
return D3D_OK;
@@ -752,7 +801,7 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
752801
bCompletedAnyPass = true;
753802
}
754803

755-
HRESULT hrEnd = pShaderInstance->m_pEffectWrap->End(bEffectDeviceOperational && !bEncounteredDeviceLoss);
804+
HRESULT hrEnd = pEffectWrap->End(bEffectDeviceOperational && !bEncounteredDeviceLoss);
756805
if (FAILED(hrEnd) && hrEnd != D3DERR_DEVICELOST && hrEnd != D3DERR_DEVICENOTRESET)
757806
WriteDebugEvent(SString("DrawIndexedPrimitiveShader: End failed %08x", hrEnd));
758807

@@ -785,10 +834,15 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
785834
/////////////////////////////////////////////////////////////
786835
void CDirect3DEvents9::CloseActiveShader(bool bDeviceOperational)
787836
{
788-
if (!g_pActiveShader)
837+
CShaderItem* pShaderItem = g_pActiveShader;
838+
g_pActiveShader = nullptr;
839+
if (!pShaderItem)
789840
return;
790841

791-
ID3DXEffect* pD3DEffect = g_pActiveShader->m_pShaderInstance->m_pEffectWrap->m_pD3DEffect;
842+
SResolvedShaderState shaderState;
843+
bool bHasShaderState = TryResolveShaderState(pShaderItem, shaderState);
844+
845+
ID3DXEffect* pD3DEffect = bHasShaderState ? shaderState.pEffect : nullptr;
792846
IDirect3DDevice9* pDevice = g_pGraphics ? g_pGraphics->GetDevice() : nullptr;
793847

794848
bool bAllowDeviceWork = bDeviceOperational;
@@ -807,8 +861,8 @@ void CDirect3DEvents9::CloseActiveShader(bool bDeviceOperational)
807861
}
808862

809863
// When the device is lost we intentionally skip touching the GPU beyond the required End call; the effect will be reset later.
810-
g_pActiveShader->m_pShaderInstance->m_pEffectWrap->End(bAllowDeviceWork);
811-
g_pActiveShader = nullptr;
864+
if (bHasShaderState)
865+
shaderState.pEffectWrap->End(bAllowDeviceWork);
812866

813867
if (CAdditionalVertexStreamManager* pAdditionalStreamManager = CAdditionalVertexStreamManager::GetExistingSingleton())
814868
pAdditionalStreamManager->MaybeUnsetAdditionalVertexStream();
@@ -819,6 +873,8 @@ void CDirect3DEvents9::CloseActiveShader(bool bDeviceOperational)
819873
pDevice->SetVertexShader(nullptr);
820874
pDevice->SetPixelShader(nullptr);
821875
}
876+
877+
pShaderItem->Release();
822878
}
823879

824880
/////////////////////////////////////////////////////////////
@@ -1923,7 +1979,10 @@ IDirect3DVertexBuffer9* CDirect3DEvents9::GetRealVertexBuffer(IDirect3DVertexBuf
19231979

19241980
// If so, use the original vertex buffer
19251981
if (pProxy)
1982+
{
19261983
pStreamData = pProxy->GetOriginal();
1984+
SAFE_RELEASE(pProxy);
1985+
}
19271986
}
19281987

19291988
return pStreamData;
@@ -1946,7 +2005,10 @@ IDirect3DIndexBuffer9* CDirect3DEvents9::GetRealIndexBuffer(IDirect3DIndexBuffer
19462005

19472006
// If so, use the original index buffer
19482007
if (pProxy)
2008+
{
19492009
pIndexData = pProxy->GetOriginal();
2010+
SAFE_RELEASE(pProxy);
2011+
}
19502012
}
19512013
return pIndexData;
19522014
}
@@ -1968,7 +2030,10 @@ IDirect3DBaseTexture9* CDirect3DEvents9::GetRealTexture(IDirect3DBaseTexture9* p
19682030

19692031
// If so, use the original texture
19702032
if (pProxy)
2033+
{
19712034
pTexture = pProxy->GetOriginal();
2035+
SAFE_RELEASE(pProxy);
2036+
}
19722037
}
19732038
return pTexture;
19742039
}
@@ -2034,6 +2099,8 @@ HRESULT CDirect3DEvents9::SetVertexDeclaration(IDirect3DDevice9* pDevice, IDirec
20342099
CProxyDirect3DDevice9::SD3DVertexDeclState* pInfo = MapFind(g_pProxyDevice->m_VertexDeclMap, pProxy);
20352100
if (pInfo)
20362101
g_pDeviceState->VertexDeclState = *pInfo;
2102+
2103+
SAFE_RELEASE(pProxy);
20372104
}
20382105
}
20392106

@@ -2052,26 +2119,33 @@ ERenderFormat CDirect3DEvents9::DiscoverReadableDepthFormat(IDirect3DDevice9* pD
20522119
IDirect3D9* pD3D = NULL;
20532120
pDevice->GetDirect3D(&pD3D);
20542121

2055-
// Formats to check for
2056-
ERenderFormat checkList[] = {RFORMAT_INTZ, RFORMAT_DF24, RFORMAT_DF16, RFORMAT_RAWZ};
2122+
ERenderFormat discoveredFormat = RFORMAT_UNKNOWN;
20572123

2058-
D3DDISPLAYMODE displayMode;
2059-
if (pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode) == D3D_OK)
2124+
if (pD3D)
20602125
{
2061-
for (uint i = 0; i < NUMELMS(checkList); i++)
2126+
// Formats to check for
2127+
ERenderFormat checkList[] = {RFORMAT_INTZ, RFORMAT_DF24, RFORMAT_DF16, RFORMAT_RAWZ};
2128+
2129+
D3DDISPLAYMODE displayMode;
2130+
if (pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode) == D3D_OK)
20622131
{
2063-
D3DFORMAT DepthFormat = (D3DFORMAT)checkList[i];
2132+
for (uint i = 0; i < NUMELMS(checkList); i++)
2133+
{
2134+
D3DFORMAT DepthFormat = (D3DFORMAT)checkList[i];
20642135

2065-
// Can use this format?
2066-
if (D3D_OK != pD3D->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, displayMode.Format, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, DepthFormat))
2067-
continue;
2136+
// Can use this format?
2137+
if (D3D_OK != pD3D->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, displayMode.Format, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, DepthFormat))
2138+
continue;
20682139

2069-
// Don't check for compatibility with multisampling, as we turn AA off when using readable depth buffer
2140+
// Don't check for compatibility with multisampling, as we turn AA off when using readable depth buffer
20702141

2071-
// Found a working format
2072-
return checkList[i];
2142+
// Found a working format
2143+
discoveredFormat = checkList[i];
2144+
break;
2145+
}
20732146
}
20742147
}
20752148

2076-
return RFORMAT_UNKNOWN;
2149+
SAFE_RELEASE(pD3D);
2150+
return discoveredFormat;
20772151
}

Client/core/DXHook/CProxyDirect3DIndexBuffer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ HRESULT CProxyDirect3DIndexBuffer::QueryInterface(REFIID riid, void** ppvObj)
6565
if (riid == CProxyDirect3DIndexBuffer_GUID)
6666
{
6767
*ppvObj = this;
68+
AddRef();
6869
return S_OK;
6970
}
7071

Client/core/DXHook/CProxyDirect3DTexture.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ HRESULT CProxyDirect3DTexture::QueryInterface(REFIID riid, void** ppvObj)
6363
if (riid == CProxyDirect3DTexture_GUID)
6464
{
6565
*ppvObj = this;
66+
AddRef();
6667
return S_OK;
6768
}
6869

Client/core/DXHook/CProxyDirect3DVertexBuffer.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ HRESULT CProxyDirect3DVertexBuffer::QueryInterface(REFIID riid, void** ppvObj)
7979
if (riid == CProxyDirect3DVertexBuffer_GUID)
8080
{
8181
*ppvObj = this;
82+
AddRef();
8283
return S_OK;
8384
}
8485

0 commit comments

Comments
 (0)