FIX: CArchive May Corrupt Memory or Write Incorrect DataID: Q133433
|
The CArchive insertion operator (CArchive::operator<<) may, in rare circumstances, write incorrect data to its associated CFile or may overwrite other application memory. Likewise, the CArchive extraction operator (CArchive::operator>>) may incorrectly read data from its CFile object.
The CArchive class uses buffering for all of its operations. When
extracting data from the archive, it maintains a pointer to the current
location in the buffer. When the pointer goes past the end of the currently
retrieved buffer, it retrieves another block of data from the associated
CFile object. The extraction operators for CArchive do not properly check
for the condition where the buffer pointer reaches the end of the block. In
some cases, the pointer wraps to the beginning of its segment. Later calls
to extract data from the CArchive retrieve the data from the wrong memory
address.
The CArchive class uses a similar procedure when data is inserted into the
CArchive. The "More Information" section of this article gives more
details.
Below are three approaches to resolving this problem:
char *pBuf = new char[520];
CArchive *pArcLoad = new CArchive(&file,CArchive::load,512,pBuf);
// Use the archive in here
delete pArcLoad;
delete pBuf;
NOTE: This code allocates a block of 520 bytes but only uses 512 of them
in the CArchive. The reason for leaving a gap of eight bytes is because
the default CArchive insertion or extraction operators take operands
with a maximum size of eight bytes (double). By allocating a buffer
eight bytes greater than necessary, you prevent m_lpBufCur from ever
getting within eight bytes of 0xFFFF so it is never wrapped back to 0 on
the addition check in the 'if' statement.
HGLOBAL hGlob = ::GlobalAlloc(GMEM_FIXED,512);
LPVOID pBuf = ::GlobalLock(hGlob);
CArchive *pArcLoad = new CArchive(&file,CArchive::load,512,pBuf);
// Use the archive in here
delete pArcLoad;
::GlobalUnlock(hGlob);
::GlobalFree(hGlob);
NOTE: This piece of code and the previous one destroy the CArchive
object before the memory buffer. If the buffer is destroyed before the
CArchive object, earlier versions of Visual C++ may cause an assertion
failure (which can be safely ignored). For more information about this
problem, please see the following article in the Microsoft Knowledge
Base:
Q128113 FIX: Assertion Failed Line 178 or Line 527 in ARCCORE.CPP
_AFX_INLINE CArchive& CArchive::operator<<(WORD w)
{ if (m_lpBufCur + sizeof(WORD) > m_lpBufMax) Flush();
*(WORD FAR*)m_lpBufCur = w; m_lpBufCur += sizeof(WORD);
return *this; }
To this:
_AFX_INLINE CArchive& CArchive::operator<<(WORD w)
{ if (m_lpBufCur > m_lpBufMax - sizeof(WORD)) Flush();
*(WORD FAR*)m_lpBufCur = w; m_lpBufCur += sizeof(WORD);
return *this; }
The only change is in the 'if' statement.Microsoft has confirmed this to be a bug in the Microsoft products listed at the beginning of this article. This problem has been corrected in the 16- bit component that comes with Visual C++ version 2.2.
The problem with the CArchive insertion and extraction operators is in the way they check the pointer to the current location in the buffer (m_lpBufCur). For example, CArchive::operator<<(WORD w) is defined as:
_AFX_INLINE CArchive& CArchive::operator<<(WORD w)
{ if (m_lpBufCur + sizeof(WORD) > m_lpBufMax) Flush();
*(WORD FAR*)m_lpBufCur = w; m_lpBufCur += sizeof(WORD);
return *this; }
It is possible that an allocated buffer could end up with a memory block
ending address close to <s>:0xFFFF. As call insert data into your buffer
consecutively, m_lpBufCur approaches the end of the address block that
might be somewhere near <s>:0xFFFF.
m_lpBufCur==<s>:0xFFFE
In this scenario, the 'if' statement would evaluate as:
if (<s>:0xFFFE + 2 > <s>:0xFFFE) Flush();
Because both pointers are far pointers this is equivalent to:
if ( <s>:0x0000 > <s>:0xFFFE ) Flush();
This evaluation is not desirable. It appears that there is enough room left
in the buffer to put the WORD into it and then m_lpBufCur is set to
<s>:0x0000. Because the beginning of the buffer may not have been at the
beginning of the segment, subsequent operations will begin to write at
whatever data happens to be located at <s>:0x0000 on up to the beginning of
the buffer.Additional query words: 1.00 1.50 1.51 1.52 2.00 2.50 2.51 2.52 StoreFields LoadFields SaveToStorage LoadFromStorage
Keywords : kbnokeyword kbMFC kbVC
Version : 1.00 1.50 1.51 1.52
Platform : WINDOWS
Issue type :
Last Reviewed: July 29, 1999