Vulnerability: Valve's Game Networking Sockets prior to version v1.2.0 improperly handles long encrypted messages in function AES_GCM_DecryptContext::Decrypt() when compiled using libsodium, leading to a Stack-Based Buffer Overflow and resulting in a memory corruption and possibly even a remote code execution.
Vulnerability ID: CVE-2020-6018
CWE: CWE-787
Severity score: 6.4
Project: https://github.com/ValveSoftware/GameNetworkingSockets
Published: 2020-12-02
File: src/common/crypto_libsodium.cpp
Commit message: Check if output buffer is too small.

It really seems like libsodium (whose entire purpose is to make crypto
idiot-proof) making me mess with these details is a flaw in the API design.

Also, correct Hungarian.
Commit author: Fletcher Dunn

Vulnerable version (before fix):
 bool AES_GCM_EncryptContext::Encrypt( 	void *pEncryptedDataAndTag, uint32 *pcbEncryptedDataAndTag, 	const void *pAdditionalAuthenticationData, size_t cbAuthenticationData ) {     unsigned long long pcbEncryptedDataAndTag_longlong = *pcbEncryptedDataAndTag;      crypto_aead_aes256gcm_encrypt_afternm( 		static_cast<unsigned char*>( pEncryptedDataAndTag ), &pcbEncryptedDataAndTag_longlong, 		static_cast<const unsigned char*>( pPlaintextData ), cbPlaintextData, 		static_cast<const unsigned char*>(pAdditionalAuthenticationData), cbAuthenticationData, 		nullptr, 		static_cast<const unsigned char*>( pIV ), 		static_cast<const crypto_aead_aes256gcm_state*>( m_ctx ) 	);      *pcbEncryptedDataAndTag = pcbEncryptedDataAndTag_longlong;      return true; } bool AES_GCM_DecryptContext::Decrypt( 	void *pPlaintextData, uint32 *pcbPlaintextData, 	const void *pAdditionalAuthenticationData, size_t cbAuthenticationData ) {     unsigned long long pcbPlaintextData_longlong;      const int nDecryptResult = crypto_aead_aes256gcm_decrypt_afternm( 		static_cast<unsigned char*>( pPlaintextData ), &pcbPlaintextData_longlong, 		nullptr, 		static_cast<const unsigned char*>( pEncryptedDataAndTag ), cbEncryptedDataAndTag, 		static_cast<const unsigned char*>( pAdditionalAuthenticationData ), cbAuthenticationData, 		static_cast<const unsigned char*>( pIV ), static_cast<const crypto_aead_aes256gcm_state*>( m_ctx ) 	);      *pcbPlaintextData = pcbPlaintextData_longlong;      return nDecryptResult == 0; }

Fixed version (after fix):
 bool AES_GCM_EncryptContext::Encrypt( 	void *pEncryptedDataAndTag, uint32 *pcbEncryptedDataAndTag, 	const void *pAdditionalAuthenticationData, size_t cbAuthenticationData ) {  	// Make sure caller's buffer is big enough to hold the result. 	if ( cbPlaintextData + crypto_aead_aes256gcm_ABYTES > *pcbEncryptedDataAndTag ) 	{ 		*pcbEncryptedDataAndTag = 0; 		return false; 	}      unsigned long long cbEncryptedDataAndTag_longlong;     crypto_aead_aes256gcm_encrypt_afternm( 		static_cast<unsigned char*>( pEncryptedDataAndTag ), &cbEncryptedDataAndTag_longlong, 		static_cast<const unsigned char*>( pPlaintextData ), cbPlaintextData, 		static_cast<const unsigned char*>(pAdditionalAuthenticationData), cbAuthenticationData, 		nullptr, 		static_cast<const unsigned char*>( pIV ), 		static_cast<const crypto_aead_aes256gcm_state*>( m_ctx ) 	);      *pcbEncryptedDataAndTag = cbEncryptedDataAndTag_longlong;      return true; } bool AES_GCM_DecryptContext::Decrypt( 	void *pPlaintextData, uint32 *pcbPlaintextData, 	const void *pAdditionalAuthenticationData, size_t cbAuthenticationData ) { 	// Make sure caller's buffer is big enough to hold the result 	if ( cbEncryptedDataAndTag > *pcbPlaintextData + crypto_aead_aes256gcm_ABYTES ) 	{ 		*pcbPlaintextData = 0; 		return false; 	}      unsigned long long cbPlaintextData_longlong;     const int nDecryptResult = crypto_aead_aes256gcm_decrypt_afternm( 		static_cast<unsigned char*>( pPlaintextData ), &cbPlaintextData_longlong, 		nullptr, 		static_cast<const unsigned char*>( pEncryptedDataAndTag ), cbEncryptedDataAndTag, 		static_cast<const unsigned char*>( pAdditionalAuthenticationData ), cbAuthenticationData, 		static_cast<const unsigned char*>( pIV ), static_cast<const crypto_aead_aes256gcm_state*>( m_ctx ) 	);      *pcbPlaintextData = cbPlaintextData_longlong;      return nDecryptResult == 0; }

Patch:
patch: @@ -50,18 +50,25 @@ bool AES_GCM_EncryptContext::Encrypt(
 	void *pEncryptedDataAndTag, uint32 *pcbEncryptedDataAndTag,
 	const void *pAdditionalAuthenticationData, size_t cbAuthenticationData
 ) {
-    unsigned long long pcbEncryptedDataAndTag_longlong = *pcbEncryptedDataAndTag;
 
+	// Make sure caller's buffer is big enough to hold the result.
+	if ( cbPlaintextData + crypto_aead_aes256gcm_ABYTES > *pcbEncryptedDataAndTag )
+	{
+		*pcbEncryptedDataAndTag = 0;
+		return false;
+	}
+
+    unsigned long long cbEncryptedDataAndTag_longlong;
     crypto_aead_aes256gcm_encrypt_afternm(
-		static_cast<unsigned char*>( pEncryptedDataAndTag ), &pcbEncryptedDataAndTag_longlong,
+		static_cast<unsigned char*>( pEncryptedDataAndTag ), &cbEncryptedDataAndTag_longlong,
 		static_cast<const unsigned char*>( pPlaintextData ), cbPlaintextData,
 		static_cast<const unsigned char*>(pAdditionalAuthenticationData), cbAuthenticationData,
 		nullptr,
 		static_cast<const unsigned char*>( pIV ),
 		static_cast<const crypto_aead_aes256gcm_state*>( m_ctx )
 	);
 
-    *pcbEncryptedDataAndTag = pcbEncryptedDataAndTag_longlong;
+    *pcbEncryptedDataAndTag = cbEncryptedDataAndTag_longlong;
 
     return true;
 }
@@ -72,17 +79,23 @@ bool AES_GCM_DecryptContext::Decrypt(
 	void *pPlaintextData, uint32 *pcbPlaintextData,
 	const void *pAdditionalAuthenticationData, size_t cbAuthenticationData
 ) {
-    unsigned long long pcbPlaintextData_longlong;
-
+	// Make sure caller's buffer is big enough to hold the result
+	if ( cbEncryptedDataAndTag > *pcbPlaintextData + crypto_aead_aes256gcm_ABYTES )
+	{
+		*pcbPlaintextData = 0;
+		return false;
+	}
+
+    unsigned long long cbPlaintextData_longlong;
     const int nDecryptResult = crypto_aead_aes256gcm_decrypt_afternm(
-		static_cast<unsigned char*>( pPlaintextData ), &pcbPlaintextData_longlong,
+		static_cast<unsigned char*>( pPlaintextData ), &cbPlaintextData_longlong,
 		nullptr,
 		static_cast<const unsigned char*>( pEncryptedDataAndTag ), cbEncryptedDataAndTag,
 		static_cast<const unsigned char*>( pAdditionalAuthenticationData ), cbAuthenticationData,
 		static_cast<const unsigned char*>( pIV ), static_cast<const crypto_aead_aes256gcm_state*>( m_ctx )
 	);
 
-    *pcbPlaintextData = pcbPlaintextData_longlong;
+    *pcbPlaintextData = cbPlaintextData_longlong;
 
     return nDecryptResult == 0;
 }
path: src/common/crypto_libsodium.cpp