Skip to content

Commit 2729baa

Browse files
committed
fix(data): Sanitize GlobalData values after reading INI data
Clamp m_numGlobalLights to [0, MAX_GLOBAL_LIGHTS] and floor allocation-size fields to prevent out-of-bounds array access and undefined behavior from bad INI values. Cap m_maxRoadVertex and m_maxRoadIndex to 65531 (16-bit DX8 buffer limit minus 4 headroom) and m_maxTankTrackEdges to MAX_TRACK_EDGE_COUNT.
1 parent 2d13736 commit 2729baa

2 files changed

Lines changed: 35 additions & 0 deletions

File tree

Generals/Code/GameEngine/Source/Common/GlobalData.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1171,6 +1171,24 @@ void GlobalData::parseGameDataDefinition( INI* ini )
11711171
// parse the ini weapon definition
11721172
ini->initFromINI( TheWritableGlobalData, s_GlobalDataFieldParseTable );
11731173

1174+
// TheSuperHackers @fix Sanitize values that are used as loop bounds or allocation sizes.
1175+
TheWritableGlobalData->m_numGlobalLights = std::clamp(TheWritableGlobalData->m_numGlobalLights, 0, MAX_GLOBAL_LIGHTS);
1176+
TheWritableGlobalData->m_maxVisibleTranslucentObjects = std::max(TheWritableGlobalData->m_maxVisibleTranslucentObjects, 0);
1177+
TheWritableGlobalData->m_maxVisibleOccluderObjects = std::max(TheWritableGlobalData->m_maxVisibleOccluderObjects, 0);
1178+
TheWritableGlobalData->m_maxVisibleOccludeeObjects = std::max(TheWritableGlobalData->m_maxVisibleOccludeeObjects, 0);
1179+
TheWritableGlobalData->m_maxVisibleNonOccluderOrOccludeeObjects = std::max(TheWritableGlobalData->m_maxVisibleNonOccluderOrOccludeeObjects, 0);
1180+
TheWritableGlobalData->m_maxLineBuildObjects = std::max(TheWritableGlobalData->m_maxLineBuildObjects, 0);
1181+
TheWritableGlobalData->m_maxRoadSegments = std::max(TheWritableGlobalData->m_maxRoadSegments, 0);
1182+
// Capped to 65531 because DX8 vertex/index buffers use 16-bit indices and allocate size + 4.
1183+
TheWritableGlobalData->m_maxRoadVertex = std::clamp(TheWritableGlobalData->m_maxRoadVertex, 0, 65531);
1184+
TheWritableGlobalData->m_maxRoadIndex = std::clamp(TheWritableGlobalData->m_maxRoadIndex, 0, 65531);
1185+
TheWritableGlobalData->m_maxRoadTypes = std::max(TheWritableGlobalData->m_maxRoadTypes, 0);
1186+
// Capped to 100 (MAX_TRACK_EDGE_COUNT) because TerrainTracksRenderObjClass has a fixed-size m_edges array.
1187+
TheWritableGlobalData->m_maxTankTrackEdges = std::clamp(TheWritableGlobalData->m_maxTankTrackEdges, 1, 100);
1188+
TheWritableGlobalData->m_networkFPSHistoryLength = std::max(TheWritableGlobalData->m_networkFPSHistoryLength, 1u);
1189+
TheWritableGlobalData->m_networkLatencyHistoryLength = std::max(TheWritableGlobalData->m_networkLatencyHistoryLength, 1u);
1190+
TheWritableGlobalData->m_networkCushionHistoryLength = std::max(TheWritableGlobalData->m_networkCushionHistoryLength, 1u);
1191+
11741192
TheWritableGlobalData->m_userDataDir.clear();
11751193
TheWritableGlobalData->m_userDataDir = BuildUserDataPathFromIni();
11761194
CreateDirectory(TheWritableGlobalData->m_userDataDir.str(), nullptr);

GeneralsMD/Code/GameEngine/Source/Common/GlobalData.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1181,6 +1181,23 @@ void GlobalData::parseGameDataDefinition( INI* ini )
11811181
// parse the ini weapon definition
11821182
ini->initFromINI( TheWritableGlobalData, s_GlobalDataFieldParseTable );
11831183

1184+
// TheSuperHackers @fix Sanitize values that are used as loop bounds or allocation sizes.
1185+
TheWritableGlobalData->m_numGlobalLights = std::clamp(TheWritableGlobalData->m_numGlobalLights, 0, MAX_GLOBAL_LIGHTS);
1186+
TheWritableGlobalData->m_maxVisibleTranslucentObjects = std::max(TheWritableGlobalData->m_maxVisibleTranslucentObjects, 0);
1187+
TheWritableGlobalData->m_maxVisibleOccluderObjects = std::max(TheWritableGlobalData->m_maxVisibleOccluderObjects, 0);
1188+
TheWritableGlobalData->m_maxVisibleOccludeeObjects = std::max(TheWritableGlobalData->m_maxVisibleOccludeeObjects, 0);
1189+
TheWritableGlobalData->m_maxVisibleNonOccluderOrOccludeeObjects = std::max(TheWritableGlobalData->m_maxVisibleNonOccluderOrOccludeeObjects, 0);
1190+
TheWritableGlobalData->m_maxLineBuildObjects = std::max(TheWritableGlobalData->m_maxLineBuildObjects, 0);
1191+
TheWritableGlobalData->m_maxRoadSegments = std::max(TheWritableGlobalData->m_maxRoadSegments, 0);
1192+
// Capped to 65531 because DX8 vertex/index buffers use 16-bit indices and allocate size + 4.
1193+
TheWritableGlobalData->m_maxRoadVertex = std::clamp(TheWritableGlobalData->m_maxRoadVertex, 0, 65531);
1194+
TheWritableGlobalData->m_maxRoadIndex = std::clamp(TheWritableGlobalData->m_maxRoadIndex, 0, 65531);
1195+
TheWritableGlobalData->m_maxRoadTypes = std::max(TheWritableGlobalData->m_maxRoadTypes, 0);
1196+
// Capped to 100 (MAX_TRACK_EDGE_COUNT) because TerrainTracksRenderObjClass has a fixed-size m_edges array.
1197+
TheWritableGlobalData->m_maxTankTrackEdges = std::clamp(TheWritableGlobalData->m_maxTankTrackEdges, 1, 100);
1198+
TheWritableGlobalData->m_networkFPSHistoryLength = std::max(TheWritableGlobalData->m_networkFPSHistoryLength, 1u);
1199+
TheWritableGlobalData->m_networkLatencyHistoryLength = std::max(TheWritableGlobalData->m_networkLatencyHistoryLength, 1u);
1200+
TheWritableGlobalData->m_networkCushionHistoryLength = std::max(TheWritableGlobalData->m_networkCushionHistoryLength, 1u);
11841201

11851202
// override INI values with user preferences
11861203
OptionPreferences optionPref;

0 commit comments

Comments
 (0)