Best thing to do is probably just use the updated network code as other users have suggested. It looks more clean and more resolved. Otherwiise...
There are a multitude of reasons why this could be happening because most of the network code looks and seems unsafe. For instance, a lot of the buffer code isn't safe when checking limits or sizes so you're able to get some weird results if some things happen.
I believe it's more so a small spoof than a massive packet. A user or server still needs to emit that data to the server or partial of it, which is more likely than someone trying to upload 6gb of data over the tcp connection or some mismatch occuring.
This could be due to the CRC code is only enabled in some of the servers and not all, even though CRC isn't used in most of the servers, but who knows.
Code:
if( &pClientSock->m_ovRecv == lpov ) // receive i/o completed
{
CBuffer* pBuffer = pClientSock->Fetch( dwBytes );
if( pBuffer )
{
if( pBuffer->cb > 0 ) {
#ifdef __PROTOCOL0910
if( pClientSock->IsCrcWrite() && pClientSock->m_dwProtocolId == 0 )
{
LPBYTE ptr = pBuffer->m_lpBufStart;
if( *(UNALIGNED LPDWORD)( ptr + pDPSock->m_dwDataSizeOffset ) == 8 )
{
if (*(UNALIGNED LPDWORD)(&ptr[pDPSock->m_dwReadHeaderSize]) == 0x00000000)
pDPSock->SetProtocolId( *(UNALIGNED LPDWORD)( &ptr[pDPSock->m_dwReadHeaderSize + 4] ) );
}
}
#endif
pBuffer->dpid = hSocket;
pDPSock->m_lspRecvBuffer.AddTail( pBuffer );
SetEvent( pDPSock->GetRecvHandle() );
}
else
SAFE_DELETE( pBuffer );
}
else if( WSAGetLastError() == ERROR_BAD_NET_NAME )
{
pDPSock->CloseConnection( hSocket );
continue;
}
if( pClientSock->Recv() != 0 ) // i/o error
{
pClientSock->m_AccessLock.Enter();
pClientSock->m_bDoNotWrite = TRUE;
int nReference = --pClientSock->m_nReference;
pClientSock->m_AccessLock.Leave();
if( nReference == 0 )
pDPSock->CloseConnection( hSocket );
}
}
If the receive handle is triggered within the IoWorkerThread, fetch is ran with the expected bytes sent.
And within that function, it tries to increase the m_pTail pointer by the bytes sent, and then it grabs the readablebuffer.
C++:
m_pRecvBuffer->m_pTail += dwBytes;
int nRemnant;
LPBYTE ptr = m_pRecvBuffer->GetReadableBuffer( &nRemnant );
Well, what if m_pTail > m_lpBuffMax after the add to dwBytes. You'd be traversing unowned memory, which is UB, generally an access violation. Checking If the writable size allows the tail to be written, then that helps.
C++:
if (m_pRecvBuffer->GetWritableBufferSize() >= dwBytes)
m_pRecvBuffer->m_pTail += dwBytes;
Then the that function itself, also doesn't validate if the tail was already overflown. This is probably less likely a cause in this case.
C++:
inline int CBuffer::GetWritableBufferSize( void )
{
if (m_pTail > m_lpBufMax) // Validation
return 0;
return (int)( m_lpBufMax - m_pTail );
}
The same issue can persist within the other function, GetReadableBuffer and GetReadableBufferSize
C++:
inline LPBYTE CBuffer::GetReadableBuffer( int* pnBufSize )
{
*pnBufSize = GetReadableBufferSize(); // might as well call the other inline function below to get the size and set it to the pointer passed through.
return m_pHead;
}
inline int CBuffer::GetReadableBufferSize( void )
{
if (m_pHead > m_pTail) // validation
return 0;
return (int)( m_pTail - m_pHead );
}
---
Within the loop itself, there are a few things that happens. If the remaining data size is less than the header size, that means that the packet is incomplete, there's no header information to peak in to.
C++:
#ifdef __CRC
if( nRemnant < (int)( m_dwReadHeaderSize ) )
#else // __CRC
if( nRemnant < (int)( m_pRecvBuffer->GetHeaderLength() ) )
#endif // __CRC
{
if( m_pRecvBuffer->cb > 0 )
{
pOld = m_pRecvBuffer;
pOld->m_pTail -= nRemnant; // remove remnant from old buffer
m_pRecvBuffer = CBufferFactory::GetInstance().CreateBuffer( m_nBufferType );
#ifdef __SO1014
if( !m_pRecvBuffer->m_lpBufStart )
{
WriteError( "FETCH//2" );
WSASetLastError( ERROR_BAD_NET_NAME );
return NULL;
}
#endif // __SO1014
memcpy( m_pRecvBuffer->m_pTail, ptr, nRemnant );
m_pRecvBuffer->m_pTail += nRemnant;
}
return pOld;
}
I assume cb is short for "current blocks". Meaning, if there is more blocks to be read, we need to do something to the remnant data so we are able to parse the remnant data with the new data. So, in this case, it stores the m_pRecvBuffer point in pOld, then subtracts the remaining size from that buffer. This is to not overread from the old buffer. The buffer Factory then generates another buffer (from the type which isn't used unless you're utilizing dpbilling and adbil for CBuffer2). It checks to see if the internal buffer was created from the malloc call within the SO1014 call then it copies that data to the new buffer's tail.
This should be safe as nRemnant is smaller than the header sizes, so having to validate the copy when the CBuffer object gets allocated with a size of 8192, and you shouldn't have to change the m_pTail += nRemnant in this code block either because of that -- the writable size should be properly done. Then it returns the old buffer.
The next section of the code, checks for the size (and this is where you experience a crash). Servers that are using CRC, expects a different size header because of the extra information transferred (crc). So, it uses an offset, casting and pointer dereferencing to convert to the expected variables. Ideally, and I noticed this from a quick look at the newer net code, they converted this to a struct and sizeof(struct) for cleaner visibility.
Another thing missing is Header Mark validation and whenver 'BadNetName" is called the connection will close from:
C++:
else if( WSAGetLastError() == ERROR_BAD_NET_NAME )
{
pDPSock->CloseConnection( hSocket );
continue;
}
And looking at the newer network code, it seems like the removed the calls to this.
You could try the following:
C++:
while( 1 )
{
VERIFY_LOOP( __FILE__, __LINE__ );
#ifdef __CRC
if( nRemnant < (int)( m_dwReadHeaderSize ) )
#else // __CRC
if( nRemnant < (int)( m_pRecvBuffer->GetHeaderLength() ) )
#endif // __CRC
{
if( m_pRecvBuffer->cb > 0 )
{
pOld = m_pRecvBuffer;
pOld->m_pTail -= nRemnant; // remove remnant from old buffer
m_pRecvBuffer = CBufferFactory::GetInstance().CreateBuffer( m_nBufferType );
#ifdef __SO1014
if( !m_pRecvBuffer->m_lpBufStart )
{
WriteError( "FETCH//2" );
return nullptr;
}
#endif
memcpy( m_pRecvBuffer->m_pTail, ptr, nRemnant );
m_pRecvBuffer->m_pTail += nRemnant;
}
return pOld;
}
// Check for header mark before moving the offset
if (*ptr != HEADERMARK)
{
WriteError("Invalid HeaderMark.");
return nullptr;
}
int nOffset = sizeof(char); // skips first character
#ifdef __CRC
if (m_dwReadHeaderSize == HEADERSIZE13)
{
// reads unigned long and adds the size of unsigned long to offset
// This crc value is for the datasize.
dwCrc = *(UNALIGNED LPDWORD)(ptr + nOffset);
nOffset += sizeof(DWORD);
// reads unigned long and adds the size of unsigned long to offset
dwDataSize = *(UNALIGNED LPDWORD)(ptr + nOffset);
nOffset += sizeof(DWORD);
// starts the crc engine
m_crcRead.Restart();
m_crcRead.Update((const byte*)(&dwDataSize), sizeof(DWORD));
m_crcRead.Final(digest);
// converts the size and uses the digest to check the protocol id.
if (~(*(UNALIGNED LPDWORD)digest ^ m_dwProtocolId) != dwCrc)
return nullptr;
// Grabs another unsigned long (another crc value) replacing the old one.and pushes the offset to the end of the header.
dwCrc = *(UNALIGNED LPDWORD)(ptr + nOffset);
nOffset += sizeof(DWORD);
// The packet sizs itself is the total DataSize and the HeaderSize
uPacketSize = m_dwReadHeaderSize + dwDataSize;
// if the data size is larger than the maximum buffer.
if (dwDataSize > MAX_BUFFER) // if( uPacketSize > MAX_BUFFER ) // kang: 2008/08/14
{
char lpAddr[16] = { 0, };
GetPeerAddr(DPID_UNKNOWN, lpAddr, NULL);
Error("CClientSocket.Fetch: ip = %s, size = %d", lpAddr, dwDataSize);
return nullptr;
}
}
else
{
// If it isn't the crc Header, the packet size variable is equal to the header size and the unsigned long after the header mark in the data.
uPacketSize = m_dwReadHeaderSize + *(UNALIGNED LPDWORD)(ptr + nOffset);
}
#else // __CRC
// And if crc is disabled on the server, it runs the functions where you crash on one.
uPacketSize = m_pRecvBuffer->GetHeaderLength() + m_pRecvBuffer->GetPacketSize(ptr); // *(UNALIGNED LPDWORD)(ptr + nOffset) is the same in this case here.
#endif // __CRC
// if the remaining writable data is less the packet size
if (nRemnant < (int)(uPacketSize))
{
// and if the packet size is greater than the recieve buffer size or if the current blocks within the buffer is more than 0 (GetSize should be safe because it always checks the size of the allocated internal buffer by checking the max (end pointer) subtracted by the start (starting pointer)
if ((int)(uPacketSize) > m_pRecvBuffer->GetSize() || m_pRecvBuffer->cb > 0)
{
pOld = m_pRecvBuffer;
// Tries to allocate the Exact packet size rather than 8192
m_pRecvBuffer = CBufferFactory::GetInstance().CreateBuffer(m_nBufferType, uPacketSize);
if (!m_pRecvBuffer->m_lpBufStart)
{
char lpAddr[16] = { 0, };
GetPeerAddr(DPID_UNKNOWN, lpAddr, NULL);
Error("CClientSocket.Fetch: ip = %s, size = %d", lpAddr, uPacketSize);
return NULL;
}
}
// If pOld got set from above and is VALID.
if (pOld)
{
if (!m_pRecvBuffer->m_lpBufStart)
{
WriteError("FETCH//3");
WSASetLastError(ERROR_BAD_NET_NAME);
return NULL;
}
// it than adds the remant to the new buffer. I don't know the name of this style but it's just moving pieces around to new buffers whihc should be safe, but if not, there's always stuff in the newer network code like validating if the size and nRemnant could be written here to the tail.
memcpy(m_pRecvBuffer->m_pTail, ptr, nRemnant);
m_pRecvBuffer->m_pTail += nRemnant;
}
return pOld;
}
#ifdef __CRC
// if crc is enabled, check the header size for the crc header
if (m_dwReadHeaderSize == HEADERSIZE13)
{
m_crcRead.Restart();
m_crcRead.Update((const byte*)(ptr + m_dwReadHeaderSize), dwDataSize);
m_crcRead.Final(digest);
// Pass the data in the the crc and then validate the crc with the protocol
if (~(*(UNALIGNED LPDWORD)digest ^ m_dwProtocolId) != dwCrc)
{
char lpAddr[16] = { 0, };
GetPeerAddr(DPID_UNKNOWN, lpAddr, NULL);
Error("CClientSock.Fetch: replay: %s", lpAddr);
WSASetLastError(ERROR_BAD_NET_NAME);
return NULL;
}
}
#endif // __CRC
// Received data, increase the currently block
m_pRecvBuffer->cb++;
// decrease the packetsize from the writable size
nRemnant -= (uPacketSize);
// offset the pointer
ptr += (uPacketSize);
}
return nullptr;
Note: You could just try applying the buffer and bufferqueue changes where they appear and see if that helps as well. I'll continue with a deep dive of the network code at another time if it still happens, but PLEASE provide more details. Show more of the stack trace, and what the objects are set from the auto window, etc.