CVE-2020-6016 (published 2020-11-18T15:15Z)

Valve's Game Networking Sockets prior to version v1.2.0 improperly handles unreliable segments with negative offsets in function SNP_ReceiveUnreliableSegment(), leading to a Heap-Based Buffer Underflow and a free() of memory not from the heap, resulting in a memory corruption and probably even a remote code execution.

Severity: HIGH
CVSS v2 base score: 10.0 (AV:N/AC:L/Au:N/C:C/I:C/A:C)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-787
cwe_name: Out-of-bounds Write
description: The product writes data past the end, or before the beginning, of the intended buffer.
extended_description: 
            
            
             
			 
         
         

Fix information:
repo_url: https://github.com/ValveSoftware/GameNetworkingSockets
commit_details: repo_url: https://github.com/ValveSoftware/GameNetworkingSockets
author: Fletcher Dunn
author_date: 2020-09-03 11:01:54-07:00
msg: Drop unreliable segments with weird offset/size.

And be more deliberate about limits of unreliable message/segment sizes.
repository: repo_url: https://github.com/ValveSoftware/GameNetworkingSockets
repo_name: ValveSoftware/GameNetworkingSockets
description: Reliable & unreliable messages over UDP.  Robust message fragmentation & reassembly.  P2P networking / NAT traversal.  Encryption.
repo_language: C++
owner: ValveSoftware
file_changes: filename: steamnetworkingsockets_snp.cpp
change_type: ModificationType.MODIFY
diff: @@ -234,7 +234,7 @@ int64 CSteamNetworkConnectionBase::SNP_SendMessage( CSteamNetworkingMessage *pSe
 	}
 
 	// Check if they try to send a really large message
-	if ( cbData > k_cbMaxUnreliableMsgSize && !( pSendMessage->m_nFlags & k_nSteamNetworkingSend_Reliable )  )
+	if ( cbData > k_cbMaxUnreliableMsgSizeSend && !( pSendMessage->m_nFlags & k_nSteamNetworkingSend_Reliable )  )
 	{
 		SpewWarningRateLimited( usecNow, "Trying to send a very large (%d bytes) unreliable message.  Sending as reliable instead.\n", cbData );
 		pSendMessage->m_nFlags |= k_nSteamNetworkingSend_Reliable;
@@ -577,11 +577,23 @@ bool CSteamNetworkConnectionBase::ProcessPlainTextDataChunk( int usecTimeSinceLa
 			// Decode size, locate segment data
 			//
 			READ_SEGMENT_DATA_SIZE( unreliable )
-			Assert( cbSegmentSize > 0 ); // !TEST! Bogus assert, zero byte messages are OK.  Remove after testing
 
-			// Receive the segment
-			bool bLastSegmentInMessage = ( nFrameType & 0x20 ) != 0;
-			SNP_ReceiveUnreliableSegment( nCurMsgNum, nOffset, pSegmentData, cbSegmentSize, bLastSegmentInMessage, usecNow );
+			// Check if offset+size indicates a message larger than what we support.  (Also,
+			// protect against malicious sender sending *extremely* large offset causing overflow.)
+			if ( (int64)nOffset + cbSegmentSize > k_cbMaxUnreliableMsgSizeRecv || cbSegmentSize > k_cbMaxUnreliableSegmentSizeRecv )
+			{
+
+				// Since this is unreliable data, we can just ignore the segment.
+				SpewWarningRateLimited( usecNow, "[%s] Ignoring unreliable segment with invalid offset %u size %d\n",
+					GetDescription(), nOffset, cbSegmentSize );
+			}
+			else
+			{
+
+				// Receive the segment
+				bool bLastSegmentInMessage = ( nFrameType & 0x20 ) != 0;
+				SNP_ReceiveUnreliableSegment( nCurMsgNum, nOffset, pSegmentData, cbSegmentSize, bLastSegmentInMessage, usecNow );
+			}
 		}
 		else if ( ( nFrameType & 0xe0 ) == 0x40 )
 		{

programming_language: C++
filename: steamnetworkingsockets_snp.h
change_type: ModificationType.MODIFY
diff: @@ -90,7 +90,14 @@ constexpr int k_nMaxBufferedUnreliableSegments = 20;
 // If app tries to send a message larger than N bytes unreliably,
 // complain about it, and automatically convert to reliable.
 // About 15 segments.
-constexpr int k_cbMaxUnreliableMsgSize = 15*1100;
+constexpr int k_cbMaxUnreliableMsgSizeSend = 15*1100;
+
+// Max possible size of an unreliable segment we could receive.
+constexpr int k_cbMaxUnreliableSegmentSizeRecv = k_cbSteamNetworkingSocketsMaxPlaintextPayloadRecv;
+
+// Largest possible total unreliable message we can receive, based on the constraints above
+constexpr int k_cbMaxUnreliableMsgSizeRecv = k_nMaxBufferedUnreliableSegments*k_cbMaxUnreliableSegmentSizeRecv;
+COMPILE_TIME_ASSERT( k_cbMaxUnreliableMsgSizeRecv > k_cbMaxUnreliableMsgSizeSend + 4096 ); // Postel's law; confirm how much slack we have here
 
 class CSteamNetworkConnectionBase;
 class CConnectionTransport;
@@ -468,7 +475,7 @@ struct SSNPRecvUnreliableSegmentData
 {
 	int m_cbSegSize = -1;
 	bool m_bLast = false;
-	char m_buf[ k_cbSteamNetworkingSocketsMaxPlaintextPayloadRecv ];
+	char m_buf[ k_cbMaxUnreliableSegmentSizeRecv ];
 };
 
 struct SSNPPacketGap

programming_language: C++