GameNetworkingSockets icon indicating copy to clipboard operation
GameNetworkingSockets copied to clipboard

fix Integer Overflow via Improper Cast in Valve's GameNetworkingSockets

Open odaysec opened this issue 8 months ago • 0 comments

an integer overflow caused by performing a multiplication between two unsigned 32-bit integers and then casting the result to a larger data type (size_t) after the multiplication. This cast does not prevent the overflow, as the multiplication itself is already performed using the smaller type. If the multiplication result exceeds the maximum value representable by a 32-bit unsigned integer (i.e., 4,294,967,295), the result wraps around, producing a significantly smaller and incorrect value.

This can lead to serious memory allocation issues such as buffer overflows, heap corruption, and potentially remote code execution in certain scenarios. The vulnerability is classified under multiple Common Weakness Enumerations (CWEs), including CWE-190 (Integer Overflow), CWE-681 (Incorrect Conversion), and CWE-192 (Integer Coercion).

https://github.com/ValveSoftware/GameNetworkingSockets/blob/725e273c7442bac7a8bc903c0b210b1c15c34d92/src/tier1/utlmemory.cpp#L21-L21 This rule finds code that converts the result of an integer multiplication to a larger type. Since the conversion applies after the multiplication, arithmetic overflow may still occur. The rule flags every multiplication of two non-constant integer expressions that is (explicitly or implicitly) converted to a larger integer type. The conversion is an indication that the expression would produce a result that would be too large to fit in the smaller integer type.

Vulnerable POC

The vulnerability is located in the following line within src/tier1/memory.cpp:

pMemory = Pvalloc(_allocationCount * m_uiSizeOfElements);

Here, both _allocationCount and m_uiSizeOfElements are declared as unsigned int (32-bit). When these two values are multiplied, the operation is carried out in 32-bit space, and the result is subject to overflow. Even though the result is stored in a size_t variable, the overflow has already occurred.

#include <iostream>
#include <cstdlib>

int main() {
    unsigned int count = 3000000000;   // large value
    unsigned int size = 4;             // 4-byte element size

    size_t actual = count * size;      // Overflow happens here

    std::cout << "Computed allocation size: " << actual << std::endl;

    void* ptr = malloc(actual);        // Pvalloc behavior
    if (!ptr) {
        std::cerr << "Memory allocation failed!" << std::endl;
    } else {
        std::cout << "Memory allocation succeeded!" << std::endl;
        free(ptr);
    }

    return 0;
}

Exploitation Potential

If this vulnerable allocation routine is used in scenarios where the allocation size is calculated based on external or user-controlled input, this bug can be exploited. An attacker can manipulate the size or count values to trigger a miscalculation. Since the application may believe a large buffer has been allocated (but in reality a small one has), subsequent writes to the allocated memory can overflow the buffer boundaries.

This can result in:

  • Heap memory corruption
  • Application crashes (DoS)
  • Potential remote code execution depending on the platform and allocator behavior

odaysec avatar Apr 07 '25 12:04 odaysec