GLib's Windows Woes: The 2GB Signed Integer Overflow
Feb 2, 2026·7 min read·3 visits
Executive Summary (TL;DR)
GLib, the utility library powering GNOME and countless other projects, contained a signed integer overflow in its Windows process spawning helper. If an attacker forces an application to spawn a subprocess with a command line string exceeding ~2GB, the length counter overflows. This results in a tiny heap allocation followed by a massive copy operation, obliterating the heap. Patched in version 2.84.1.
A classic integer overflow vulnerability in GLib's Windows-specific process spawning logic allows attackers to cause a heap-based buffer overflow. By supplying an argument string approaching 2GB in length, the signed integer length calculation wraps around, leading to an undersized memory allocation and subsequent heap corruption. While primarily a Denial of Service vector, the underlying memory corruption makes this a dangerous flaw in core infrastructure code.
The Hook: Windows is special (and not in a good way)
If you've ever written cross-platform code, you know the drill: Unix-like systems are elegant. You pass an array of strings (argv[]) to execve, and the kernel handles the rest. It's clean, precise, and logical.
Then there's Windows. On Windows, the process creation API (CreateProcess) doesn't take an array of arguments. It takes a single, massive command-line string. It is up to the caller to concatenate, quote, and escape every single argument perfectly so that the child process can parse them back out again. It is a legacy nightmare that has haunted developers since the 90s.
GLib, being the helpful abstraction layer it is, tries to hide this madness from you. The functions protect_argv_string inside glib/gspawn-win32.c are designed to take your nice, clean argument list and mangle it into a Windows-compatible command line string. It handles the quotes, the backslashes, and the weird edge cases.
But here's the kicker: The developers assumed that nobody in their right mind would ever try to pass a command line string longer than what a standard signed 32-bit integer could hold. They were wrong. And because C lets you shoot your own foot off with signed math, we have CVE-2025-4056.
The Flaw: Signed Integers are the Devil's Plaything
The vulnerability lives deep inside glib/gspawn-win32.c. The function protect_argv_string is responsible for iterating through the arguments and calculating how much memory is needed to hold the final, escaped command line string.
Here is the fatal mistake: The variable len was declared as a gint. In GLib land, gint is a standard signed int (usually 32-bit). The maximum positive value a signed 32-bit integer can hold is 2,147,483,647.
So, what happens if we feed it 2,147,483,648 bytes of data? In binary, that's a 1 followed by 31 zeros. In a signed 32-bit context, that isn't a large number; it's -2,147,483,648. The counter wraps around to negative.
Because the code didn't check for this overflow, it happily calculates a negative length. But memory allocators don't take negative numbers. When g_malloc receives this "negative" number, it is cast to size_t (unsigned). If the wrap-around results in a small negative number (like -50), the cast makes it a massive unsigned number, and the allocation fails safely (OOM).
However, if the math wraps just right (or if subsequent addition operations bring it back to a small positive number), we get a tiny allocation. Specifically, the code performs len + 1. If len has overflowed to something ridiculous, the allocation size becomes unpredictable and usually far too small for the data we are about to copy into it.
The Code: The Smoking Gun
Let's look at the actual code before the patch. This is a textbook example of why types matter.
// glib/gspawn-win32.c (VULNERABLE)
static gchar *
protect_argv_string (const gchar *string)
{
const gchar *p = string;
gchar *retval, *q;
gint len = 0; // <--- ERROR: Signed 32-bit integer
gint pre_bslash = 0;
while (*p) {
if (*p == '\"') len++;
if (*p == '\\') pre_bslash++;
else {
len += pre_bslash;
pre_bslash = 0;
}
len++; // <--- ERROR: Unchecked increment
p++;
}
// If len overflowed, it might be negative here.
retval = g_malloc (len + 1);
// ... strcpy ensues ...
}The fix, implemented in commit f1a498e178709dcd5c26303a5b54ae5007733fc5, is delightfully simple but critical. It promotes the counters to size_t (which is 64-bit on modern Windows builds) and uses g_new which has built-in overflow protection for the allocation size calculation.
// glib/gspawn-win32.c (PATCHED)
static gchar *
protect_argv_string (const gchar *string)
{
const gchar *p = string;
gchar *retval, *q;
gsize len = 0; // <--- FIX: size_t (unsigned, architecture width)
gsize pre_bslash = 0;
// ... same loop logic ...
// FIX: g_new checks for multiplication overflows, though here we just need the size check
retval = g_new (gchar, len + 1);
}By switching to gsize (alias for size_t), the counter on a 64-bit system can now count up to 18,446,744,073,709,551,615. If you are passing an 18-exabyte string to a process, you have bigger problems than a buffer overflow.
The Exploit: Crashing the Party
Exploiting this requires the attacker to control the input to a GLib application that eventually calls g_spawn_*. This isn't as rare as it sounds. Think of a server-side application that wraps a CLI tool (like git or ffmpeg) and takes user input as arguments.
The Attack Chain:
- Preparation: The attacker constructs a payload consisting of a massive string. It doesn't need to be complex; just long. A sequence of 'A's repeated
2GB + 100 bytestimes works. - Delivery: The attacker sends this via an upload, a long HTTP header, or a configuration file.
- The Trigger: The vulnerable application calls
g_spawn_async. - The Overflow:
protect_argv_stringiterates the string.lenhitsINT_MAX, wraps toINT_MIN, and continues counting up toward zero. - The Allocation: Suppose
lenends up at100after the loop finishes (due to the wrap-around).g_malloc(101)is called. The heap manager happily hands over 101 bytes. - The Corruption: The code then proceeds to copy the original 2GB+ string into that 101-byte buffer. This writes past the allocated chunk, overwriting adjacent heap metadata, other objects, and essentially nuking the process memory.
While this is an absolute slam-dunk for Denial of Service, achieving Remote Code Execution (RCE) is tricky. On 64-bit Windows, the heap is randomized, and the sheer volume of data being written (2GB) makes it hard to stop writing exactly where you want to execute code. You're more likely to hit an unmapped page and segfault before you can overwrite a function pointer nicely.
The Impact: Why should we care?
You might be thinking, "Who sends 2GB of data to a command line?" In the world of modern computing, the answer is "automated systems handling big data" or "fuzzers."
Denial of Service (DoS) is the guaranteed outcome. For a desktop app, this is an annoyance. For a server-side component processing user uploads or IPC messages, this is a service outage. If a critical system daemon uses GLib to spawn helpers, an attacker can crash the daemon repeatedly.
Code Execution Risk: While I downplayed RCE earlier, do not underestimate the creativity of exploit devs. If the overflow can be manipulated to result in a len that is just slightly smaller than the actual data, and the attacker can control the heap layout (Heap Feng Shui), it might be possible to overwrite a critical structure immediately following the buffer without triggering a page fault. It is a high-difficulty, high-reward scenario.
The Fix: Upgrade or Filter
The remediation is straightforward: Update GLib. Version 2.84.1 contains the fix. If you are a developer bundling GLib (e.g., using vcpkg, MSYS2, or vending the DLLs with your app), you must rebuild or redistribute the new version.
If you cannot update immediately, you must sanitize your inputs. Ensure that no code path allows user-controlled input to result in a command-line argument list approaching 2GB. Frankly, if your application design requires passing 2GB of data via command-line arguments, you should probably rethink your architecture and use pipes or temporary files instead.
> [!NOTE]
> This vulnerability is specific to Windows because the Unix implementation uses execve with an array of pointers, avoiding this specific string concatenation/length calculation logic.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:HAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
GLib GNOME | < 2.84.1 | 2.84.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-190 (Integer Overflow) |
| Secondary CWE | CWE-122 (Heap-based Buffer Overflow) |
| Attack Vector | Network / Local (Input Dependent) |
| CVSS v3.1 | 7.5 (High) |
| Impact | DoS, Potential RCE |
| Platform | Windows (Exclusive) |
MITRE ATT&CK Mapping
The software performs a calculation that can produce an integer overflow or wraparound, when the logic assumes that the resulting value will always be larger than the original value. This can be used to bypass security checks or allocate insufficient memory.
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.