diff --git a/Core/GameEngineDevice/Include/W3DDevice/GameClient/WorldHeightMap.h b/Core/GameEngineDevice/Include/W3DDevice/GameClient/WorldHeightMap.h index 846c11bbd42..8532f6b2eab 100644 --- a/Core/GameEngineDevice/Include/W3DDevice/GameClient/WorldHeightMap.h +++ b/Core/GameEngineDevice/Include/W3DDevice/GameClient/WorldHeightMap.h @@ -238,6 +238,7 @@ class WorldHeightMap : public RefCountClass, public: // height map info. static Int getMinHeightValue() {return K_MIN_HEIGHT;} static Int getMaxHeightValue() {return K_MAX_HEIGHT;} + static Bool isValidMapSize(Int width, Int height, Int borderSize); ///< Within MAX_MAP_DIMENSION and MAX_MAP_AREA, width and height include the border UnsignedByte *getDataPtr() {return m_data;} diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp index 4d4946c2b3a..130e2e3d82e 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/Water/W3DWater.cpp @@ -2726,10 +2726,17 @@ void WaterRenderObjClass::drawRiverWater(PolygonTrigger *pTrig) static Bool doWobble = true; if (m_disableRiver) return; - m_drawingRiver = true; //allocate 2 triangles per side with 3 indices per triangle - DynamicIBAccessClass ib_access(BUFFER_TYPE_DYNAMIC_DX8,(rectangleCount+1)*2*3); + const Int indexCount = (rectangleCount+1)*2*3; + // TheSuperHackers @bugfix arcticdolphin 12/10/2026 Skip rivers that do not fit the 16 bit dynamic buffers. + DEBUG_ASSERTCRASH(indexCount <= DynamicIBAccessClass::MAX_INDEX_COUNT, ("River has too many points to draw")); + if (indexCount > DynamicIBAccessClass::MAX_INDEX_COUNT) + return; + + m_drawingRiver = true; + + DynamicIBAccessClass ib_access(BUFFER_TYPE_DYNAMIC_DX8,indexCount); { DynamicIBAccessClass::WriteLockClass lockib(&ib_access); UnsignedShort *curIb = lockib.Get_Index_Array(); diff --git a/Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp b/Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp index 1893a5376d4..0f0b19a3cb6 100644 --- a/Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp +++ b/Core/GameEngineDevice/Source/W3DDevice/GameClient/WorldHeightMap.cpp @@ -827,6 +827,24 @@ Bool WorldHeightMap::ParseObjectsDataChunk(DataChunkInput &file, DataChunkInfo * return (file.parse(userData)); } +Bool WorldHeightMap::isValidMapSize(Int width, Int height, Int borderSize) +{ + if (width <= 0 || width > MAX_MAP_DIMENSION) + return false; + if (height <= 0 || height > MAX_MAP_DIMENSION) + return false; + if (width * height > MAX_MAP_AREA) + return false; + if (borderSize < 0 || borderSize > (width - 1) / 2 || borderSize > (height - 1) / 2) + return false; + return true; +} + +static Bool isValidHeightDataSize(DataChunkInput &file, Int dataSize, Int width, Int height) +{ + return dataSize > 0 && dataSize == width * height && (UnsignedInt)dataSize <= file.getChunkDataSizeLeft(); +} + /** * WorldHeightMap::ParseHeightMapData - read a height map chunk. * Format is the newer CHUNKY format. @@ -857,6 +875,10 @@ Bool WorldHeightMap::ParseHeightMapData(DataChunkInput &file, DataChunkInfo *inf m_borderSize = 0; } + if (!isValidMapSize(m_width, m_height, m_borderSize)) { + throw ERROR_CORRUPT_FILE_FORMAT; + } + if (info->version >= K_HEIGHT_MAP_VERSION_4) { Int numBorders = file.readInt(); m_boundaries.resize(numBorders); @@ -871,11 +893,12 @@ Bool WorldHeightMap::ParseHeightMapData(DataChunkInput &file, DataChunkInfo *inf } m_dataSize = file.readInt(); - m_data = MSGNEW("WorldHeightMap_ParseHeightMapData") UnsignedByte[m_dataSize]; - if (m_dataSize <= 0 || (m_dataSize != (m_width*m_height))) { + if (!isValidHeightDataSize(file, m_dataSize, m_width, m_height)) { throw ERROR_CORRUPT_FILE_FORMAT ; } + m_data = MSGNEW("WorldHeightMap_ParseHeightMapData") UnsignedByte[m_dataSize]; + Int numBytesX = (m_width+7)/8; //how many bytes to fit all bitflags Int numBytesY = m_height; m_seismicUpdateWidth=numBytesX; @@ -931,6 +954,10 @@ Bool WorldHeightMap::ParseSizeOnly(DataChunkInput &file, DataChunkInfo *info, vo m_borderSize = 0; } + if (!isValidMapSize(m_width, m_height, m_borderSize)) { + throw ERROR_CORRUPT_FILE_FORMAT; + } + if (info->version >= K_HEIGHT_MAP_VERSION_4) { Int numBorders = file.readInt(); m_boundaries.resize(numBorders); @@ -945,10 +972,11 @@ Bool WorldHeightMap::ParseSizeOnly(DataChunkInput &file, DataChunkInfo *info, vo } m_dataSize = file.readInt(); - m_data = MSGNEW("WorldHeightMap_ParseSizeOnly") UnsignedByte[m_dataSize]; - if (m_dataSize <= 0 || (m_dataSize != (m_width*m_height))) { + if (!isValidHeightDataSize(file, m_dataSize, m_width, m_height)) { throw ERROR_CORRUPT_FILE_FORMAT ; } + + m_data = MSGNEW("WorldHeightMap_ParseSizeOnly") UnsignedByte[m_dataSize]; file.readArrayOfBytes((char *)m_data, m_dataSize); // Resize me. if (info->version == K_HEIGHT_MAP_VERSION_1) { @@ -1074,6 +1102,7 @@ Bool WorldHeightMap::ParseBlendTileData(DataChunkInput &file, DataChunkInfo *inf m_cellCliffState[j*m_flipStateWidth + i] = data[j*byteWidth + i]; } } + delete[] data; } else { file.readArrayOfBytes((char*)m_cellCliffState, m_height*m_flipStateWidth); } diff --git a/Core/Libraries/Source/WWVegas/WW3D2/dx8indexbuffer.h b/Core/Libraries/Source/WWVegas/WW3D2/dx8indexbuffer.h index 3ae18aabe10..f69e6997e27 100644 --- a/Core/Libraries/Source/WWVegas/WW3D2/dx8indexbuffer.h +++ b/Core/Libraries/Source/WWVegas/WW3D2/dx8indexbuffer.h @@ -119,6 +119,8 @@ class DynamicIBAccessClass void Allocate_DX8_Dynamic_Buffer(); public: + enum { MAX_INDEX_COUNT = 0xffff }; ///< index_count is an unsigned short + DynamicIBAccessClass(unsigned short type, unsigned short index_count); ~DynamicIBAccessClass(); diff --git a/Generals/Code/GameEngine/Include/Common/MapReaderWriterInfo.h b/Generals/Code/GameEngine/Include/Common/MapReaderWriterInfo.h index c370ac5b3c0..0a3b698bcdc 100644 --- a/Generals/Code/GameEngine/Include/Common/MapReaderWriterInfo.h +++ b/Generals/Code/GameEngine/Include/Common/MapReaderWriterInfo.h @@ -53,6 +53,11 @@ #define K_LIGHTING_VERSION_3 3 // Added 2 additional global lights for terrain. #define K_WORLDDICT_VERSION_1 1 #define K_MAPPREVIEW_VERSION_1 1 + +// TheSuperHackers @bugfix arcticdolphin 12/10/2026 Map size limits. +#define MAX_MAP_DIMENSION 8192 // The shroud texture spans the map and shoreline tiles pack each coordinate into 16 bits +#define MAX_MAP_AREA (4096*4096) // Terrain, pathfinding and partition arrays allocate per cell in a 32 bit process + /** Virtual helper class, so that we can write map data using FILE* or CFile. */ class OutputStream { public: diff --git a/GeneralsMD/Code/GameEngine/Include/Common/MapReaderWriterInfo.h b/GeneralsMD/Code/GameEngine/Include/Common/MapReaderWriterInfo.h index c114ad29109..02eb3a762be 100644 --- a/GeneralsMD/Code/GameEngine/Include/Common/MapReaderWriterInfo.h +++ b/GeneralsMD/Code/GameEngine/Include/Common/MapReaderWriterInfo.h @@ -55,6 +55,11 @@ #define K_LIGHTING_VERSION_3 3 // Added 2 additional global lights for terrain. #define K_WORLDDICT_VERSION_1 1 #define K_MAPPREVIEW_VERSION_1 1 + +// TheSuperHackers @bugfix arcticdolphin 12/10/2026 Map size limits. +#define MAX_MAP_DIMENSION 8192 // The shroud texture spans the map and shoreline tiles pack each coordinate into 16 bits +#define MAX_MAP_AREA (4096*4096) // Terrain, pathfinding and partition arrays allocate per cell in a 32 bit process + /** Virtual helper class, so that we can write map data using FILE* or CFile. */ class OutputStream { public: