Skip to content

Commit 9f2a170

Browse files
committed
(Heap) safety fixes in CRenderItemManager::OnLostDevice, CRenderItemManager::SaveDefaultRenderTarget and CRenderItemManager::ChangeRenderTarget
SaveDefaultRenderTarget now takes explicit references on the saved surfaces (AddRef) instead of storing raw pointers that had just been SAFE_RELEASE'd. The old flow dropped the last reference and left m_pDefaultD3DRenderTarget/m_pDefaultD3DZStencilSurface dangling, so any later use (e.g. RestoreDefaultRenderTarget) dereferenced freed COM objects (UAF). OnLostDevice iterates with a pre-incremented iterator copy and releases the cached default surfaces. Before this, if a render item destroyed itself during OnLostDevice, erasing from m_CreatedItemList, the loop’s iterator became invalid and the next increment reused free'd set nodes. Releasing the cached surfaces also clears pointers that'd otherwise reference resources the runtime has already discarded, avoiding future dereferences of invalid memory.
1 parent 97afe85 commit 9f2a170

File tree

1 file changed

+79
-20
lines changed

1 file changed

+79
-20
lines changed

Client/core/Graphics/CRenderItemManager.cpp

Lines changed: 79 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,20 @@ void CRenderItemManager::OnDeviceCreate(IDirect3DDevice9* pDevice, float fViewpo
122122
////////////////////////////////////////////////////////////////
123123
void CRenderItemManager::OnLostDevice()
124124
{
125-
for (std::set<CRenderItem*>::iterator iter = m_CreatedItemList.begin(); iter != m_CreatedItemList.end(); iter++)
126-
(*iter)->OnLostDevice();
125+
for (std::set<CRenderItem*>::iterator iter = m_CreatedItemList.begin(); iter != m_CreatedItemList.end();)
126+
{
127+
std::set<CRenderItem*>::iterator current = iter++;
128+
(*current)->OnLostDevice();
129+
}
127130

128131
SAFE_RELEASE(m_pSavedSceneDepthSurface);
129132
SAFE_RELEASE(m_pSavedSceneRenderTargetAA);
130133
SAFE_RELEASE(g_pDeviceState->MainSceneState.DepthBuffer);
131134
SAFE_RELEASE(m_pNonAARenderTargetTexture);
132135
SAFE_RELEASE(m_pNonAARenderTarget);
133136
SAFE_RELEASE(m_pNonAADepthSurface2);
137+
SAFE_RELEASE(m_pDefaultD3DRenderTarget);
138+
SAFE_RELEASE(m_pDefaultD3DZStencilSurface);
134139
}
135140

136141
////////////////////////////////////////////////////////////////
@@ -594,17 +599,40 @@ bool CRenderItemManager::SaveDefaultRenderTarget()
594599
SaveReadableDepthBuffer();
595600

596601
// Update our info about what rendertarget is active
597-
IDirect3DSurface9* pActiveD3DRenderTarget;
598-
IDirect3DSurface9* pActiveD3DZStencilSurface;
599-
m_pDevice->GetRenderTarget(0, &pActiveD3DRenderTarget);
600-
m_pDevice->GetDepthStencilSurface(&pActiveD3DZStencilSurface);
602+
IDirect3DSurface9* pActiveD3DRenderTarget = nullptr;
603+
const HRESULT hrRenderTarget = m_pDevice->GetRenderTarget(0, &pActiveD3DRenderTarget);
604+
if (FAILED(hrRenderTarget) || !pActiveD3DRenderTarget)
605+
{
606+
SAFE_RELEASE(pActiveD3DRenderTarget);
607+
SAFE_RELEASE(m_pDefaultD3DRenderTarget);
608+
SAFE_RELEASE(m_pDefaultD3DZStencilSurface);
609+
return false;
610+
}
611+
612+
IDirect3DSurface9* pActiveD3DZStencilSurface = nullptr;
613+
const HRESULT hrDepthStencil = m_pDevice->GetDepthStencilSurface(&pActiveD3DZStencilSurface);
614+
if (FAILED(hrDepthStencil) && hrDepthStencil != D3DERR_NOTFOUND)
615+
{
616+
SAFE_RELEASE(pActiveD3DRenderTarget);
617+
SAFE_RELEASE(pActiveD3DZStencilSurface);
618+
SAFE_RELEASE(m_pDefaultD3DRenderTarget);
619+
SAFE_RELEASE(m_pDefaultD3DZStencilSurface);
620+
return false;
621+
}
622+
623+
SAFE_RELEASE(m_pDefaultD3DRenderTarget);
624+
SAFE_RELEASE(m_pDefaultD3DZStencilSurface);
601625

602626
m_pDefaultD3DRenderTarget = pActiveD3DRenderTarget;
627+
if (m_pDefaultD3DRenderTarget)
628+
m_pDefaultD3DRenderTarget->AddRef();
629+
603630
m_pDefaultD3DZStencilSurface = pActiveD3DZStencilSurface;
631+
if (m_pDefaultD3DZStencilSurface)
632+
m_pDefaultD3DZStencilSurface->AddRef();
604633

605-
// Don't hold any refs because it goes all funny during fullscreen minimize/maximize, even if they are released at onLostDevice
606-
SAFE_RELEASE(pActiveD3DRenderTarget)
607-
SAFE_RELEASE(pActiveD3DZStencilSurface)
634+
SAFE_RELEASE(pActiveD3DRenderTarget);
635+
SAFE_RELEASE(pActiveD3DZStencilSurface);
608636

609637
// Do this in case dxSetRenderTarget is being called from some unexpected place
610638
CGraphics::GetSingleton().MaybeEnteringMTARenderZone();
@@ -625,7 +653,8 @@ bool CRenderItemManager::RestoreDefaultRenderTarget()
625653
{
626654
if (ChangeRenderTarget(m_uiDefaultViewportSizeX, m_uiDefaultViewportSizeY, m_pDefaultD3DRenderTarget, m_pDefaultD3DZStencilSurface))
627655
{
628-
m_pDefaultD3DRenderTarget = nullptr;
656+
SAFE_RELEASE(m_pDefaultD3DRenderTarget);
657+
SAFE_RELEASE(m_pDefaultD3DZStencilSurface);
629658

630659
// Do this in case dxSetRenderTarget is being called from some unexpected place
631660
CGraphics::GetSingleton().MaybeLeavingMTARenderZone();
@@ -668,26 +697,53 @@ bool CRenderItemManager::ChangeRenderTarget(uint uiSizeX, uint uiSizeY, IDirect3
668697
SaveReadableDepthBuffer();
669698

670699
// Check if we need to change
671-
IDirect3DSurface9* pCurrentRenderTarget;
672-
IDirect3DSurface9* pCurrentZStencilSurface;
673-
m_pDevice->GetRenderTarget(0, &pCurrentRenderTarget);
674-
m_pDevice->GetDepthStencilSurface(&pCurrentZStencilSurface);
700+
IDirect3DSurface9* pCurrentRenderTarget = nullptr;
701+
HRESULT hrRenderTarget = m_pDevice->GetRenderTarget(0, &pCurrentRenderTarget);
702+
if (FAILED(hrRenderTarget) || !pCurrentRenderTarget)
703+
{
704+
SAFE_RELEASE(pCurrentRenderTarget);
705+
return false;
706+
}
675707

676-
bool bAlreadySet = (pD3DRenderTarget == pCurrentRenderTarget && pD3DZStencilSurface == pCurrentZStencilSurface);
708+
IDirect3DSurface9* pCurrentZStencilSurface = nullptr;
709+
HRESULT hrDepthStencil = m_pDevice->GetDepthStencilSurface(&pCurrentZStencilSurface);
710+
if (FAILED(hrDepthStencil) && hrDepthStencil != D3DERR_NOTFOUND)
711+
{
712+
SAFE_RELEASE(pCurrentRenderTarget);
713+
SAFE_RELEASE(pCurrentZStencilSurface);
714+
return false;
715+
}
677716

678-
SAFE_RELEASE(pCurrentRenderTarget);
679-
SAFE_RELEASE(pCurrentZStencilSurface);
717+
const bool bAlreadySet = (pD3DRenderTarget == pCurrentRenderTarget && pD3DZStencilSurface == pCurrentZStencilSurface);
680718

681-
// Already set?
682719
if (bAlreadySet)
720+
{
721+
SAFE_RELEASE(pCurrentRenderTarget);
722+
SAFE_RELEASE(pCurrentZStencilSurface);
683723
return true;
724+
}
684725

685726
// Tell graphics things are about to change
686727
CGraphics::GetSingleton().OnChangingRenderTarget(uiSizeX, uiSizeY);
687728

688729
// Do change
689-
m_pDevice->SetRenderTarget(0, pD3DRenderTarget);
690-
m_pDevice->SetDepthStencilSurface(pD3DZStencilSurface);
730+
hrRenderTarget = m_pDevice->SetRenderTarget(0, pD3DRenderTarget);
731+
if (FAILED(hrRenderTarget))
732+
{
733+
SAFE_RELEASE(pCurrentRenderTarget);
734+
SAFE_RELEASE(pCurrentZStencilSurface);
735+
return false;
736+
}
737+
738+
HRESULT hrSetDepth = m_pDevice->SetDepthStencilSurface(pD3DZStencilSurface);
739+
if (FAILED(hrSetDepth))
740+
{
741+
m_pDevice->SetRenderTarget(0, pCurrentRenderTarget);
742+
m_pDevice->SetDepthStencilSurface(pCurrentZStencilSurface);
743+
SAFE_RELEASE(pCurrentRenderTarget);
744+
SAFE_RELEASE(pCurrentZStencilSurface);
745+
return false;
746+
}
691747

692748
D3DVIEWPORT9 viewport;
693749
viewport.X = 0;
@@ -698,6 +754,9 @@ bool CRenderItemManager::ChangeRenderTarget(uint uiSizeX, uint uiSizeY, IDirect3
698754
viewport.MaxZ = 1.0f;
699755
m_pDevice->SetViewport(&viewport);
700756

757+
SAFE_RELEASE(pCurrentRenderTarget);
758+
SAFE_RELEASE(pCurrentZStencilSurface);
759+
701760
return true;
702761
}
703762

0 commit comments

Comments
 (0)