Skip to content

Commit 4505174

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 9f2a170 commit 4505174

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)
@@ -377,31 +406,37 @@ HRESULT CDirect3DEvents9::DrawPrimitiveShader(IDirect3DDevice9* pDevice, D3DPRIM
377406
else
378407
{
379408
// Yes shader for this texture
380-
CShaderInstance* pShaderInstance = pShaderItem->m_pShaderInstance;
409+
SResolvedShaderState shaderState;
410+
if (!TryResolveShaderState(pShaderItem, shaderState) || !shaderState.pEffect)
411+
{
412+
if (!bIsLayer)
413+
return DrawPrimitiveGuarded(pDevice, PrimitiveType, StartVertex, PrimitiveCount);
414+
return D3D_OK;
415+
}
416+
417+
CShaderInstance* pShaderInstance = shaderState.pInstance;
418+
CEffectWrap* pEffectWrap = shaderState.pEffectWrap;
419+
ID3DXEffect* pD3DEffect = shaderState.pEffect;
381420

382421
// Apply custom parameters
383422
pShaderInstance->ApplyShaderParameters();
384423
// Apply common parameters
385-
pShaderInstance->m_pEffectWrap->ApplyCommonHandles();
424+
pEffectWrap->ApplyCommonHandles();
386425
// Apply mapped parameters
387-
pShaderInstance->m_pEffectWrap->ApplyMappedHandles();
426+
pEffectWrap->ApplyMappedHandles();
388427

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

393432
// Do shader passes
394-
ID3DXEffect* pD3DEffect = pShaderInstance->m_pEffectWrap->m_pD3DEffect;
395-
bool bEffectDeviceTemporarilyLost = false;
396-
bool bEffectDeviceOperational = true;
397-
if (pD3DEffect)
433+
bool bEffectDeviceTemporarilyLost = false;
434+
bool bEffectDeviceOperational = true;
435+
IDirect3DDevice9* pEffectDevice = nullptr;
436+
if (SUCCEEDED(pD3DEffect->GetDevice(&pEffectDevice)) && pEffectDevice)
398437
{
399-
IDirect3DDevice9* pEffectDevice = nullptr;
400-
if (SUCCEEDED(pD3DEffect->GetDevice(&pEffectDevice)) && pEffectDevice)
401-
{
402-
bEffectDeviceOperational = IsDeviceOperational(pEffectDevice, &bEffectDeviceTemporarilyLost);
403-
SAFE_RELEASE(pEffectDevice);
404-
}
438+
bEffectDeviceOperational = IsDeviceOperational(pEffectDevice, &bEffectDeviceTemporarilyLost);
439+
SAFE_RELEASE(pEffectDevice);
405440
}
406441

407442
if (!bEffectDeviceOperational)
@@ -414,7 +449,7 @@ HRESULT CDirect3DEvents9::DrawPrimitiveShader(IDirect3DDevice9* pDevice, D3DPRIM
414449

415450
DWORD dwFlags = D3DXFX_DONOTSAVESHADERSTATE; // D3DXFX_DONOTSAVE(SHADER|SAMPLER)STATE
416451
uint uiNumPasses = 0;
417-
HRESULT hrBegin = pShaderInstance->m_pEffectWrap->Begin(&uiNumPasses, dwFlags);
452+
HRESULT hrBegin = pEffectWrap->Begin(&uiNumPasses, dwFlags);
418453
if (FAILED(hrBegin) || uiNumPasses == 0)
419454
{
420455
if (FAILED(hrBegin) && hrBegin != D3DERR_DEVICELOST && hrBegin != D3DERR_DEVICENOTRESET)
@@ -462,7 +497,7 @@ HRESULT CDirect3DEvents9::DrawPrimitiveShader(IDirect3DDevice9* pDevice, D3DPRIM
462497
bCompletedAnyPass = true;
463498
}
464499

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

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

609653
// Transfer any state changes to the active shader, but ensure the device still accepts work
610-
CShaderInstance* pShaderInstance = g_pActiveShader->m_pShaderInstance;
611-
ID3DXEffect* pActiveEffect = pShaderInstance->m_pEffectWrap->m_pD3DEffect;
654+
CShaderInstance* pShaderInstance = activeState.pInstance;
655+
CEffectWrap* pActiveEffectWrap = activeState.pEffectWrap;
656+
ID3DXEffect* pActiveEffect = activeState.pEffect;
612657

613658
bool bDeviceTemporarilyLost = false;
614659
bool bDeviceOperational = true;
615-
if (pActiveEffect)
660+
IDirect3DDevice9* pEffectDevice = nullptr;
661+
if (SUCCEEDED(pActiveEffect->GetDevice(&pEffectDevice)) && pEffectDevice)
616662
{
617-
IDirect3DDevice9* pEffectDevice = nullptr;
618-
if (SUCCEEDED(pActiveEffect->GetDevice(&pEffectDevice)) && pEffectDevice)
619-
{
620-
bDeviceOperational = IsDeviceOperational(pEffectDevice, &bDeviceTemporarilyLost);
621-
SAFE_RELEASE(pEffectDevice);
622-
}
663+
bDeviceOperational = IsDeviceOperational(pEffectDevice, &bDeviceTemporarilyLost);
664+
SAFE_RELEASE(pEffectDevice);
623665
}
624666

625667
if (!bDeviceOperational)
@@ -628,11 +670,11 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
628670
return D3D_OK;
629671
}
630672

631-
bool bChanged = pShaderInstance->m_pEffectWrap->ApplyCommonHandles();
632-
bChanged |= pShaderInstance->m_pEffectWrap->ApplyMappedHandles();
673+
bool bChanged = pActiveEffectWrap->ApplyCommonHandles();
674+
bChanged |= pActiveEffectWrap->ApplyMappedHandles();
633675
if (bChanged)
634676
{
635-
HRESULT hrCommit = pShaderInstance->m_pEffectWrap->m_pD3DEffect->CommitChanges();
677+
HRESULT hrCommit = pActiveEffect->CommitChanges();
636678
if (FAILED(hrCommit))
637679
{
638680
if (hrCommit != D3DERR_DEVICELOST && hrCommit != D3DERR_DEVICENOTRESET)
@@ -647,11 +689,21 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
647689
g_pDeviceState->FrameStats.iNumShadersFullSetup++;
648690

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

652704
// Add normal stream if shader wants it
653705
CAdditionalVertexStreamManager* pAdditionalStreamManager = CAdditionalVertexStreamManager::GetExistingSingleton();
654-
if (pShaderInstance->m_pEffectWrap->m_pEffectTemplate->m_bRequiresNormals && pAdditionalStreamManager)
706+
if (pEffectWrap->m_pEffectTemplate->m_bRequiresNormals && pAdditionalStreamManager)
655707
{
656708
// Find/create/set additional vertex stream
657709
pAdditionalStreamManager->MaybeSetAdditionalVertexStream(PrimitiveType, BaseVertexIndex, MinVertexIndex, NumVertices, startIndex, primCount);
@@ -660,26 +712,22 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
660712
// Apply custom parameters
661713
pShaderInstance->ApplyShaderParameters();
662714
// Apply common parameters
663-
pShaderInstance->m_pEffectWrap->ApplyCommonHandles();
715+
pEffectWrap->ApplyCommonHandles();
664716
// Apply mapped parameters
665-
pShaderInstance->m_pEffectWrap->ApplyMappedHandles();
717+
pEffectWrap->ApplyMappedHandles();
666718

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

671723
// Do shader passes
672-
ID3DXEffect* pD3DEffect = pShaderInstance->m_pEffectWrap->m_pD3DEffect;
673-
bool bEffectDeviceTemporarilyLost = false;
674-
bool bEffectDeviceOperational = true;
675-
if (pD3DEffect)
724+
bool bEffectDeviceTemporarilyLost = false;
725+
bool bEffectDeviceOperational = true;
726+
IDirect3DDevice9* pEffectDevice = nullptr;
727+
if (SUCCEEDED(pD3DEffect->GetDevice(&pEffectDevice)) && pEffectDevice)
676728
{
677-
IDirect3DDevice9* pEffectDevice = nullptr;
678-
if (SUCCEEDED(pD3DEffect->GetDevice(&pEffectDevice)) && pEffectDevice)
679-
{
680-
bEffectDeviceOperational = IsDeviceOperational(pEffectDevice, &bEffectDeviceTemporarilyLost);
681-
SAFE_RELEASE(pEffectDevice);
682-
}
729+
bEffectDeviceOperational = IsDeviceOperational(pEffectDevice, &bEffectDeviceTemporarilyLost);
730+
SAFE_RELEASE(pEffectDevice);
683731
}
684732

685733
if (!bEffectDeviceOperational)
@@ -694,7 +742,7 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
694742

695743
DWORD dwFlags = D3DXFX_DONOTSAVESHADERSTATE; // D3DXFX_DONOTSAVE(SHADER|SAMPLER)STATE
696744
uint uiNumPasses = 0;
697-
HRESULT hrBegin = pShaderInstance->m_pEffectWrap->Begin(&uiNumPasses, dwFlags);
745+
HRESULT hrBegin = pEffectWrap->Begin(&uiNumPasses, dwFlags);
698746
if (FAILED(hrBegin) || uiNumPasses == 0)
699747
{
700748
if (FAILED(hrBegin) && hrBegin != D3DERR_DEVICELOST && hrBegin != D3DERR_DEVICENOTRESET)
@@ -735,6 +783,7 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
735783
{
736784
// Make this the active shader for possible reuse
737785
dassert(dwFlags == D3DXFX_DONOTSAVESHADERSTATE);
786+
pShaderItem->AddRef();
738787
g_pActiveShader = pShaderItem;
739788
SAFE_RELEASE(pOriginalVertexShader);
740789
return D3D_OK;
@@ -754,7 +803,7 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
754803
bCompletedAnyPass = true;
755804
}
756805

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

@@ -787,10 +836,15 @@ HRESULT CDirect3DEvents9::DrawIndexedPrimitiveShader(IDirect3DDevice9* pDevice,
787836
/////////////////////////////////////////////////////////////
788837
void CDirect3DEvents9::CloseActiveShader(bool bDeviceOperational)
789838
{
790-
if (!g_pActiveShader)
839+
CShaderItem* pShaderItem = g_pActiveShader;
840+
g_pActiveShader = nullptr;
841+
if (!pShaderItem)
791842
return;
792843

793-
ID3DXEffect* pD3DEffect = g_pActiveShader->m_pShaderInstance->m_pEffectWrap->m_pD3DEffect;
844+
SResolvedShaderState shaderState;
845+
bool bHasShaderState = TryResolveShaderState(pShaderItem, shaderState);
846+
847+
ID3DXEffect* pD3DEffect = bHasShaderState ? shaderState.pEffect : nullptr;
794848
IDirect3DDevice9* pDevice = g_pGraphics ? g_pGraphics->GetDevice() : nullptr;
795849

796850
bool bAllowDeviceWork = bDeviceOperational;
@@ -809,8 +863,8 @@ void CDirect3DEvents9::CloseActiveShader(bool bDeviceOperational)
809863
}
810864

811865
// When the device is lost we intentionally skip touching the GPU beyond the required End call; the effect will be reset later.
812-
g_pActiveShader->m_pShaderInstance->m_pEffectWrap->End(bAllowDeviceWork);
813-
g_pActiveShader = nullptr;
866+
if (bHasShaderState)
867+
shaderState.pEffectWrap->End(bAllowDeviceWork);
814868

815869
if (CAdditionalVertexStreamManager* pAdditionalStreamManager = CAdditionalVertexStreamManager::GetExistingSingleton())
816870
pAdditionalStreamManager->MaybeUnsetAdditionalVertexStream();
@@ -821,6 +875,8 @@ void CDirect3DEvents9::CloseActiveShader(bool bDeviceOperational)
821875
pDevice->SetVertexShader(nullptr);
822876
pDevice->SetPixelShader(nullptr);
823877
}
878+
879+
pShaderItem->Release();
824880
}
825881

826882
/////////////////////////////////////////////////////////////
@@ -1925,7 +1981,10 @@ IDirect3DVertexBuffer9* CDirect3DEvents9::GetRealVertexBuffer(IDirect3DVertexBuf
19251981

19261982
// If so, use the original vertex buffer
19271983
if (pProxy)
1984+
{
19281985
pStreamData = pProxy->GetOriginal();
1986+
SAFE_RELEASE(pProxy);
1987+
}
19291988
}
19301989

19311990
return pStreamData;
@@ -1948,7 +2007,10 @@ IDirect3DIndexBuffer9* CDirect3DEvents9::GetRealIndexBuffer(IDirect3DIndexBuffer
19482007

19492008
// If so, use the original index buffer
19502009
if (pProxy)
2010+
{
19512011
pIndexData = pProxy->GetOriginal();
2012+
SAFE_RELEASE(pProxy);
2013+
}
19522014
}
19532015
return pIndexData;
19542016
}
@@ -1970,7 +2032,10 @@ IDirect3DBaseTexture9* CDirect3DEvents9::GetRealTexture(IDirect3DBaseTexture9* p
19702032

19712033
// If so, use the original texture
19722034
if (pProxy)
2035+
{
19732036
pTexture = pProxy->GetOriginal();
2037+
SAFE_RELEASE(pProxy);
2038+
}
19742039
}
19752040
return pTexture;
19762041
}
@@ -2036,6 +2101,8 @@ HRESULT CDirect3DEvents9::SetVertexDeclaration(IDirect3DDevice9* pDevice, IDirec
20362101
CProxyDirect3DDevice9::SD3DVertexDeclState* pInfo = MapFind(g_pProxyDevice->m_VertexDeclMap, pProxy);
20372102
if (pInfo)
20382103
g_pDeviceState->VertexDeclState = *pInfo;
2104+
2105+
SAFE_RELEASE(pProxy);
20392106
}
20402107
}
20412108

@@ -2054,26 +2121,33 @@ ERenderFormat CDirect3DEvents9::DiscoverReadableDepthFormat(IDirect3DDevice9* pD
20542121
IDirect3D9* pD3D = NULL;
20552122
pDevice->GetDirect3D(&pD3D);
20562123

2057-
// Formats to check for
2058-
ERenderFormat checkList[] = {RFORMAT_INTZ, RFORMAT_DF24, RFORMAT_DF16, RFORMAT_RAWZ};
2124+
ERenderFormat discoveredFormat = RFORMAT_UNKNOWN;
20592125

2060-
D3DDISPLAYMODE displayMode;
2061-
if (pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode) == D3D_OK)
2126+
if (pD3D)
20622127
{
2063-
for (uint i = 0; i < NUMELMS(checkList); i++)
2128+
// Formats to check for
2129+
ERenderFormat checkList[] = {RFORMAT_INTZ, RFORMAT_DF24, RFORMAT_DF16, RFORMAT_RAWZ};
2130+
2131+
D3DDISPLAYMODE displayMode;
2132+
if (pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode) == D3D_OK)
20642133
{
2065-
D3DFORMAT DepthFormat = (D3DFORMAT)checkList[i];
2134+
for (uint i = 0; i < NUMELMS(checkList); i++)
2135+
{
2136+
D3DFORMAT DepthFormat = (D3DFORMAT)checkList[i];
20662137

2067-
// Can use this format?
2068-
if (D3D_OK != pD3D->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, displayMode.Format, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, DepthFormat))
2069-
continue;
2138+
// Can use this format?
2139+
if (D3D_OK != pD3D->CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, displayMode.Format, D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, DepthFormat))
2140+
continue;
20702141

2071-
// Don't check for compatibility with multisampling, as we turn AA off when using readable depth buffer
2142+
// Don't check for compatibility with multisampling, as we turn AA off when using readable depth buffer
20722143

2073-
// Found a working format
2074-
return checkList[i];
2144+
// Found a working format
2145+
discoveredFormat = checkList[i];
2146+
break;
2147+
}
20752148
}
20762149
}
20772150

2078-
return RFORMAT_UNKNOWN;
2151+
SAFE_RELEASE(pD3D);
2152+
return discoveredFormat;
20792153
}

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)