Skip to content

Commit e6d9670

Browse files
committed
Test crash fix (avert) for old, Top 10 crash: SA @ 0x011630F8
1 parent f129051 commit e6d9670

2 files changed

Lines changed: 52 additions & 3 deletions

File tree

Client/game_sa/CRenderWareSA.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,14 +1008,20 @@ CColModel* CRenderWareSA::ReadCOL(const SString& buffer)
10081008

10091009
// Ensure shadow data consistency to prevent crash in GTA's stencil shadow rendering
10101010
// GTA accesses shadow vertices without NULL checks, causing crash if pointer is NULL but count is non-zero
1011+
// Also cap counts to catch corrupt data before CCollisionData::Copy amplifies it into an out-of-bounds read
10111012
auto* pInterface = pColModel->GetInterface();
10121013
if (pInterface && pInterface->m_data)
10131014
{
10141015
auto* pData = pInterface->m_data;
10151016

1016-
// shadow counts indicate data exists but pointers are NULL
1017-
const bool shadowVertsCorrupt = pData->m_numShadowVertices > 0 && pData->m_shadowVertices == nullptr;
1018-
const bool shadowTrisCorrupt = pData->m_numShadowTriangles > 0 && pData->m_shadowTriangles == nullptr;
1017+
constexpr std::uint32_t MAX_SHADOW_VERTICES = 10000;
1018+
constexpr std::uint32_t MAX_SHADOW_TRIANGLES = 5000;
1019+
1020+
// shadow counts indicate data exists but pointers are NULL, or counts are beyond any legitimate model
1021+
const bool shadowVertsCorrupt =
1022+
(pData->m_numShadowVertices > 0 && pData->m_shadowVertices == nullptr) || pData->m_numShadowVertices > MAX_SHADOW_VERTICES;
1023+
const bool shadowTrisCorrupt =
1024+
(pData->m_numShadowTriangles > 0 && pData->m_shadowTriangles == nullptr) || pData->m_numShadowTriangles > MAX_SHADOW_TRIANGLES;
10191025

10201026
if (shadowVertsCorrupt || shadowTrisCorrupt)
10211027
{

Client/multiplayer_sa/CMultiplayerSA_CrashFixHacks.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "../game_sa/TaskBasicSA.h"
1818
#include "../game_sa/CFxSystemBPSA.h"
1919
#include "../game_sa/CFxSystemSA.h"
20+
#include "../game_sa/CColModelSA.h"
2021

2122
extern CCoreInterface* g_pCore;
2223

@@ -3098,6 +3099,46 @@ static void __declspec(naked) HOOK_CFire_ProcessFire()
30983099
// clang-format on
30993100
}
31003101

3102+
//////////////////////////////////////////////////////////////////////////////////////////
3103+
//
3104+
// CColModel::MakeMultipleAlloc - Validate collision data before converting
3105+
// single-allocation to multi-allocation format via CCollisionData::Copy.
3106+
//
3107+
// Corrupt collision data (e.g. from PC_Scratch buffer overflow) can produce
3108+
// bogus shadow vertex/triangle counts, causing Copy to read past allocations.
3109+
//
3110+
//////////////////////////////////////////////////////////////////////////////////////////
3111+
#define HOOKPOS_CColModel_MakeMultipleAlloc 0x40F740
3112+
#define HOOKSIZE_CColModel_MakeMultipleAlloc 5
3113+
static void __fastcall HOOK_CColModel_MakeMultipleAlloc(CColModelSAInterface* pColModel, void*)
3114+
{
3115+
if (CColDataSA* pData = pColModel->m_data)
3116+
{
3117+
constexpr std::uint32_t MAX_SHADOW_TRIANGLES = 5000;
3118+
constexpr std::uint32_t MAX_SHADOW_VERTICES = 10000;
3119+
3120+
const bool shadowCorrupt = pData->m_numShadowTriangles > MAX_SHADOW_TRIANGLES || pData->m_numShadowVertices > MAX_SHADOW_VERTICES ||
3121+
(pData->m_numShadowVertices > 0 && !pData->m_shadowVertices) ||
3122+
(pData->m_numShadowTriangles > 0 && !pData->m_shadowTriangles);
3123+
3124+
if (shadowCorrupt)
3125+
{
3126+
pData->m_numShadowTriangles = 0;
3127+
pData->m_numShadowVertices = 0;
3128+
pData->m_shadowVertices = nullptr;
3129+
pData->m_shadowTriangles = nullptr;
3130+
pData->m_hasShadowInfo = 0;
3131+
pData->m_hasShadow = 0;
3132+
3133+
OnCrashAverted(9801);
3134+
}
3135+
}
3136+
3137+
// Call the original MakeMultipleAlloc
3138+
using MakeMultipleAlloc_t = void(__thiscall*)(CColModelSAInterface*);
3139+
reinterpret_cast<MakeMultipleAlloc_t>(0x1564A10)(pColModel);
3140+
}
3141+
31013142
//////////////////////////////////////////////////////////////////////////////////////////
31023143
//
31033144
// Setup hooks for CrashFixHacks
@@ -3169,6 +3210,8 @@ void CMultiplayerSA::InitHooks_CrashFixHacks()
31693210
EZHookInstall(FxPrim_c__Enable);
31703211
EZHookInstall(CFire_ProcessFire);
31713212

3213+
EZHookInstall(CColModel_MakeMultipleAlloc);
3214+
31723215
// Install train crossing crashfix (the temporary variable is required for the template logic)
31733216
void (*temp)() = HOOK_TrainCrossingBarrierCrashFix<RETURN_CObject_Destructor_TrainCrossing_Check, RETURN_CObject_Destructor_TrainCrossing_Invalid>;
31743217
HookInstall(HOOKPOS_CObject_Destructor_TrainCrossing_Check, (DWORD)temp, 5);

0 commit comments

Comments
 (0)