From 8debce7eab1cfb7a145d592d757b75e7cac83610 Mon Sep 17 00:00:00 2001 From: Ladislav Zezula Date: Fri, 4 Aug 2023 11:19:49 +0200 Subject: Fixed heap overflow in handling of file patch --- src/SBaseCommon.cpp | 40 +++++++++++++++++++++++----------------- src/SFileAttributes.cpp | 5 ++++- src/StormCommon.h | 2 +- src/StormLib.h | 7 +++++-- 4 files changed, 33 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/SBaseCommon.cpp b/src/SBaseCommon.cpp index b0029e0..b5b880e 100644 --- a/src/SBaseCommon.cpp +++ b/src/SBaseCommon.cpp @@ -1154,6 +1154,7 @@ DWORD AllocateSectorBuffer(TMPQFile * hf) DWORD AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile) { TMPQArchive * ha = hf->ha; + TPatchInfo * pPatchInfo; DWORD dwLength = sizeof(TPatchInfo); // The following conditions must be true @@ -1164,35 +1165,39 @@ __AllocateAndLoadPatchInfo: // Allocate space for patch header. Start with default size, // and if its size if bigger, then we reload them - hf->pPatchInfo = STORM_ALLOC(TPatchInfo, 1); - if(hf->pPatchInfo == NULL) + pPatchInfo = (TPatchInfo *)(STORM_ALLOC(BYTE, dwLength)); + if(pPatchInfo == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Do we have to load the patch header from the file ? if(bLoadFromFile) { // Load the patch header - if(!FileStream_Read(ha->pStream, &hf->RawFilePos, hf->pPatchInfo, dwLength)) + if(!FileStream_Read(ha->pStream, &hf->RawFilePos, pPatchInfo, dwLength)) { - // Free the patch info - STORM_FREE(hf->pPatchInfo); - hf->pPatchInfo = NULL; + STORM_FREE(pPatchInfo); return GetLastError(); } // Perform necessary swapping - hf->pPatchInfo->dwLength = BSWAP_INT32_UNSIGNED(hf->pPatchInfo->dwLength); - hf->pPatchInfo->dwFlags = BSWAP_INT32_UNSIGNED(hf->pPatchInfo->dwFlags); - hf->pPatchInfo->dwDataSize = BSWAP_INT32_UNSIGNED(hf->pPatchInfo->dwDataSize); + pPatchInfo->dwLength = BSWAP_INT32_UNSIGNED(pPatchInfo->dwLength); + pPatchInfo->dwFlags = BSWAP_INT32_UNSIGNED(pPatchInfo->dwFlags); + pPatchInfo->dwDataSize = BSWAP_INT32_UNSIGNED(pPatchInfo->dwDataSize); + + // Do nothing if the patch info is not valid + if(!(pPatchInfo->dwFlags & MPQ_PATCH_INFO_VALID)) + { + STORM_FREE(pPatchInfo); + return ERROR_FILE_CORRUPT; + } // Verify the size of the patch header // If it's not default size, we have to reload them - if(hf->pPatchInfo->dwLength > dwLength) + if(pPatchInfo->dwLength > dwLength) { // Free the patch info - dwLength = hf->pPatchInfo->dwLength; - STORM_FREE(hf->pPatchInfo); - hf->pPatchInfo = NULL; + dwLength = pPatchInfo->dwLength; + STORM_FREE(pPatchInfo); // If the length is out of all possible ranges, fail the operation if(dwLength > 0x400) @@ -1201,16 +1206,17 @@ __AllocateAndLoadPatchInfo: } // Patch file data size according to the patch header - hf->dwDataSize = hf->pPatchInfo->dwDataSize; + hf->dwDataSize = pPatchInfo->dwDataSize; } else { - memset(hf->pPatchInfo, 0, dwLength); + memset(pPatchInfo, 0, dwLength); + pPatchInfo->dwLength = dwLength; + pPatchInfo->dwFlags = MPQ_PATCH_INFO_VALID; } // Save the final length to the patch header - hf->pPatchInfo->dwLength = dwLength; - hf->pPatchInfo->dwFlags = 0x80000000; + hf->pPatchInfo = pPatchInfo; return ERROR_SUCCESS; } diff --git a/src/SFileAttributes.cpp b/src/SFileAttributes.cpp index e22a09f..dbcc456 100644 --- a/src/SFileAttributes.cpp +++ b/src/SFileAttributes.cpp @@ -391,7 +391,10 @@ DWORD SAttrLoadAttributes(TMPQArchive * ha) pbAttrFile[cbAttrFile] = 0; // Load the entire file to memory - SFileReadFile(hFile, pbAttrFile, cbAttrFile, &dwBytesRead, NULL); + if(!SFileReadFile(hFile, pbAttrFile, cbAttrFile, &dwBytesRead, NULL)) + ha->dwFlags |= (GetLastError() == ERROR_FILE_CORRUPT) ? MPQ_FLAG_MALFORMED : 0; + + // Parse the (attributes) if(dwBytesRead == cbAttrFile) dwErrCode = LoadAttributesFile(ha, pbAttrFile, cbAttrFile); diff --git a/src/StormCommon.h b/src/StormCommon.h index 74b687e..34077fd 100644 --- a/src/StormCommon.h +++ b/src/StormCommon.h @@ -82,7 +82,7 @@ #endif //----------------------------------------------------------------------------- -// MTYPE definition - specifies what kind of MPQ is the map type +// MTYPE definition - specifies what kind of MPQ is the file typedef enum _MTYPE { diff --git a/src/StormLib.h b/src/StormLib.h index 4aa51c1..4d5992d 100644 --- a/src/StormLib.h +++ b/src/StormLib.h @@ -215,7 +215,7 @@ extern "C" { #define SFILE_INVALID_POS 0xFFFFFFFF #define SFILE_INVALID_ATTRIBUTES 0xFFFFFFFF -// Flags for SFileAddFile +// Flags for TMPQBlock::dwFlags #define MPQ_FILE_IMPLODE 0x00000100 // Implode method (By PKWARE Data Compression Library) #define MPQ_FILE_COMPRESS 0x00000200 // Compress methods (By multiple methods) #define MPQ_FILE_ENCRYPTED 0x00010000 // Indicates whether file is encrypted @@ -259,6 +259,9 @@ extern "C" { MPQ_FILE_FIX_KEY | \ MPQ_FILE_EXISTS) +// Flags for TPatchInfo::dwFlags +#define MPQ_PATCH_INFO_VALID 0x80000000 // Set if the patch info is valid + // We need to mask out the upper 4 bits of the block table index. // This is because it gets shifted out when calculating block table offset // BlockTableOffset = pHash->dwBlockIndex << 0x04 @@ -676,7 +679,7 @@ typedef struct _TMPQBlock typedef struct _TPatchInfo { DWORD dwLength; // Length of patch info header, in bytes - DWORD dwFlags; // Flags. 0x80000000 = MD5 (?) + DWORD dwFlags; // Flags. 0x80000000 = valid (?) DWORD dwDataSize; // Uncompressed size of the patch file BYTE md5[0x10]; // MD5 of the entire patch file after decompression -- cgit v1.2.3