Back to Blog
high SEVERITY7 min read

Integer Overflow in wf_cliprdr.c: How Clipboard Data Could Corrupt Memory

A high-severity integer overflow vulnerability (CWE-190) was discovered in `libs/clipboard/src/windows/wf_cliprdr.c` at line 774, where the `m_nStreams` value derived from remote clipboard data was passed directly to `calloc()` without bounds validation. A malicious remote peer could supply a crafted stream count near `SIZE_MAX / sizeof(LPSTREAM)`, causing the size calculation to overflow and producing an undersized allocation that subsequent writes would overflow. The fix adds explicit bounds c

O
By Orbis AppSec
Published June 1, 2026Reviewed June 3, 2026

Answer Summary

This is an integer overflow vulnerability (CWE-190) in C affecting FreeRDP's clipboard code in `wf_cliprdr.c`. When `m_nStreams` from remote clipboard data is passed to `calloc()` without validation, a value near `SIZE_MAX / sizeof(LPSTREAM)` causes the size calculation to wrap around, producing an undersized allocation. The fix adds explicit bounds checking to reject stream counts that would cause overflow before calling `calloc()`.

Vulnerability at a Glance

cweCWE-190 (Integer Overflow or Wraparound)
fixAdd explicit bounds validation to reject stream counts that would overflow
riskRemote code execution via heap corruption from malicious clipboard data
languageC
root causeUnchecked `m_nStreams` value passed directly to calloc() size calculation
vulnerabilityInteger Overflow leading to Heap Buffer Overflow

Integer Overflow in wf_cliprdr.c: How Clipboard Data Could Corrupt Memory

Introduction

The libs/clipboard/src/windows/wf_cliprdr.c file handles Windows clipboard redirection — the mechanism that lets users copy and paste between a local machine and a remote session. It's the kind of code that quietly does its job in the background, processing data that arrives from a remote peer. But buried at line 774, a subtle integer overflow flaw meant that a malicious remote peer could send specially crafted clipboard data and corrupt the application's heap.

This post breaks down exactly how that happens, what the fix looks like, and what every C developer should take away from this class of vulnerability.


The Vulnerability Explained

What Was Happening at Line 774

The vulnerable code allocated memory for an array of clipboard stream pointers using calloc:

// VULNERABLE - before the fix (wf_cliprdr.c, line 774)
instance->m_pStreams = (LPSTREAM*)calloc(instance->m_nStreams, sizeof(LPSTREAM));

At first glance, this looks perfectly normal. calloc(count, size) is the idiomatic C way to allocate a zeroed array. The problem is where m_nStreams comes from: it is derived directly from remote clipboard data, with no validation before it reaches this allocation.

The Integer Overflow Mechanics

calloc(count, size) internally computes count * size to determine how many bytes to allocate. On a 64-bit system, sizeof(LPSTREAM) — which is sizeof(void*) — is 8 bytes.

Now consider what happens when an attacker sends a crafted m_nStreams value of, say, SIZE_MAX / 8 + 1 (which is 0x2000000000000001 on a 64-bit system):

count = 0x2000000000000001
size  = 8

count * size = 0x2000000000000001 * 8
             = 0x10000000000000008  (overflows to 0x8 on 64-bit)

The multiplication wraps around to just 8 bytes — enough for a single pointer. But the code then proceeds to write m_nStreams worth of stream pointers into that allocation, writing far beyond the end of the 8-byte buffer.

This is a classic heap buffer overflow triggered by integer overflow — the allocation appears to succeed, the pointer is non-NULL, and nothing signals an error. The damage happens silently when the streams are populated.

Why This Is Particularly Dangerous

A few factors make this vulnerability especially concerning:

  1. Remote trigger: The m_nStreams value comes from the remote peer's clipboard announcement. No local user interaction is required beyond having an active clipboard-sharing session.

  2. Silent failure: calloc returns a valid (but tiny) pointer. There's no NULL check that would catch the problem, because the allocation succeeded — just with the wrong size.

  3. Heap corruption: Out-of-bounds writes to the heap can overwrite allocator metadata, adjacent objects, or function pointers, potentially enabling arbitrary code execution.

  4. Production path: This is not test code or an edge-case handler. Clipboard redirection is a core feature of remote desktop workflows.

Attack Scenario

A malicious remote peer — or a compromised machine on the other end of a remote desktop session — crafts a clipboard format announcement where the stream count field is set to a value near SIZE_MAX / sizeof(LPSTREAM). When the local client processes this announcement and reaches the calloc at line 774, the undersized allocation is made. As the code iterates over the "streams" and writes pointers into the array, it overwrites adjacent heap memory. Depending on what lives next to that allocation, this can crash the process, corrupt application state, or, in a worst case, redirect execution.


The Fix

The fix introduces explicit bounds validation for m_nStreams before it is used in the calloc call. The security invariant is straightforward: if multiplying the count by sizeof(LPSTREAM) would overflow SIZE_MAX, or if the count exceeds a reasonable upper bound for clipboard streams, the allocation must be rejected.

Before: Unchecked Allocation

// BEFORE - no validation of m_nStreams
instance->m_pStreams = (LPSTREAM*)calloc(instance->m_nStreams, sizeof(LPSTREAM));

After: Guarded Allocation

// AFTER - bounds check before allocation
#define MAX_SAFE_STREAM_COUNT 1024

// Reject zero (invalid data)
if (instance->m_nStreams == 0) {
    return ERROR_INVALID_DATA;
}

// Reject counts exceeding a reasonable maximum
if (instance->m_nStreams > MAX_SAFE_STREAM_COUNT) {
    return ERROR_INVALID_DATA;
}

// Reject counts that would overflow the size calculation
if (instance->m_nStreams > SIZE_MAX / sizeof(LPSTREAM)) {
    return ERROR_INVALID_DATA;
}

// Safe to allocate
instance->m_pStreams = (LPSTREAM*)calloc(instance->m_nStreams, sizeof(LPSTREAM));

Why Three Separate Checks?

Each guard addresses a distinct failure mode:

Check What It Prevents
count == 0 Allocating a zero-byte buffer then writing into it
count > MAX_SAFE_STREAM_COUNT Legitimate-looking but excessive allocations (DoS / logic abuse)
count > SIZE_MAX / sizeof(LPSTREAM) The integer overflow that triggers the undersized allocation

The MAX_SAFE_STREAM_COUNT bound of 1,024 deserves special mention. Real-world clipboard operations involve a small number of format streams — dozens at most. A count of 1,025 almost certainly indicates malicious or malformed data. This "semantic" bound catches dangerous inputs that are numerically valid but logically impossible for legitimate clipboard use.

The Regression Test

The fix is accompanied by a comprehensive regression test (tests/test_invariant_wf_cliprdr.c) that encodes the security invariant directly. Key test cases include:

/* Adversarial count values derived from remote clipboard data */
size_t adversarial_counts[] = {
    SIZE_MAX,
    SIZE_MAX / 2,
    SIZE_MAX / LPSTREAM_SIZE,
    SIZE_MAX / LPSTREAM_SIZE + 1,
    0xFFFFFFFF,
    MAX_SAFE_STREAM_COUNT + 1,
    /* ... */
};

// INVARIANT: all of these must return NULL (rejected)
void **result = safe_alloc_streams(count);
ck_assert_msg(result == NULL, "SECURITY VIOLATION: ...");

This test will catch any future regression where the bounds check is accidentally removed or weakened.

A CI workflow (.github/workflows/wf-cliprdr-ci.yml) was also added to run these tests automatically on every change to the clipboard code, ensuring the invariant is continuously enforced.


Prevention & Best Practices

1. Never Trust Remote-Supplied Counts

Any value that comes from a network peer, a file, or any external source and is used as an allocation size must be validated. The pattern calloc(remote_value, element_size) is a red flag that should always trigger a review.

2. Use the count > SIZE_MAX / element_size Pattern

This is the idiomatic, portable way to check for overflow before a count * size multiplication in C:

if (count > SIZE_MAX / element_size) {
    // overflow would occur — reject
}

Avoid relying on compiler extensions or platform-specific behavior for overflow detection.

3. Add Semantic Bounds

Mathematical overflow checks are necessary but not sufficient. Add a domain-specific upper bound that reflects what is reasonable for your application. For clipboard streams, 1,024 is generous. For other contexts, choose a bound that matches the real-world maximum with a safety margin.

4. Use Static Analysis

Tools like CodeQL, Coverity, and clang-analyzer can flag unvalidated values flowing into allocation functions. The multi-agent AI scanner that caught this issue (V-003) is another example of automated detection that should be part of any security pipeline.

5. Consider Safe Integer Libraries

For C code that performs many size calculations, consider using a safe integer library such as safe-iop or the __builtin_mul_overflow GCC/Clang intrinsic:

size_t total;
if (__builtin_mul_overflow(count, sizeof(LPSTREAM), &total)) {
    // overflow detected
}

References


Key Takeaways

  • m_nStreams at line 774 was the root cause: A single unvalidated field from remote clipboard data was sufficient to trigger heap corruption. Always trace allocation inputs back to their source.
  • calloc is not safe by itself: calloc(count, size) does check for overflow internally on some platforms, but you cannot rely on this — and even when it fails, the behavior is implementation-defined. Validate before calling.
  • Three-layer validation is the right model: Zero check, semantic maximum, and overflow check together cover the full attack surface for count-based allocations.
  • Regression tests should encode invariants, not just behavior: The test suite added here doesn't just test the happy path — it explicitly encodes the security property ("adversarial counts must return NULL") as machine-checkable assertions.
  • CI enforcement closes the loop: Adding the .github/workflows/wf-cliprdr-ci.yml workflow means this invariant will be re-verified on every future change to the clipboard code, preventing the fix from being quietly undone.

Conclusion

The integer overflow in wf_cliprdr.c is a textbook example of why remote-supplied numeric values demand the same skepticism as remote-supplied strings. The fix is small — three if statements before a calloc call — but the absence of those checks created a direct path from a malicious clipboard announcement to heap corruption. By validating m_nStreams against zero, a semantic maximum, and the overflow boundary before allocation, the fix closes all three attack vectors simultaneously. If you write C code that allocates memory based on externally-supplied counts, audit those sites now. The pattern is common, the fix is straightforward, and the cost of missing one can be severe.

Frequently Asked Questions

What is integer overflow in C?

Integer overflow occurs when an arithmetic operation produces a value that exceeds the maximum value the data type can hold, causing it to wrap around to a small or negative number, leading to unexpected behavior.

How do you prevent integer overflow in C?

Validate input values against safe bounds before arithmetic operations, use safe integer arithmetic libraries, check for overflow conditions explicitly, and consider using compiler built-ins like `__builtin_mul_overflow()`.

What CWE is integer overflow?

Integer overflow is classified as CWE-190 (Integer Overflow or Wraparound), which describes conditions where arithmetic operations produce values outside the representable range of the data type.

Is using calloc() instead of malloc() enough to prevent integer overflow?

No. While calloc() takes separate count and size arguments, the internal multiplication can still overflow on some implementations. Explicit bounds checking before the call is required for safety.

Can static analysis detect integer overflow?

Yes, static analysis tools like Coverity, PVS-Studio, and CodeQL can detect potential integer overflow vulnerabilities, especially when untrusted input flows into size calculations without validation.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #15142

Related Articles

critical

How integer overflow in _wopendir() happens in C Windows dirent and how to fix it

A critical integer overflow vulnerability in `include/compat/dirent_msvc.h` allowed an attacker-controlled directory path length to wrap the `sizeof(wchar_t) * n + 16` allocation calculation, resulting in a dangerously undersized heap buffer. Subsequent writes to that buffer caused a heap overflow, enabling potential memory corruption or code execution on Windows systems. The fix adds a pre-allocation bounds check and proper errno signaling to safely reject overflow-inducing inputs.

critical

How integer overflow in path_join() happens in C and how to fix it

A critical integer overflow vulnerability was discovered in the `__cstl_join` function in `opencstl/filesystem.h` that could allow attackers to trigger a heap buffer overflow by supplying crafted file path strings. The fix adds an explicit overflow check before the size calculation, returning NULL when the combined path lengths would wrap around the `size_type64` maximum value.

critical

How integer overflow in TLS KDF buffer allocation happens in C with OpenSSL and how to fix it

A critical integer overflow vulnerability was discovered in OpenSSL's `tls1_export_keying_material()` function inside `ssl/t1_enc.c`, where attacker-influenced length values could wrap around during arithmetic, causing the `vallen` buffer to be allocated far smaller than needed. The four subsequent `memcpy` calls would then write beyond the heap buffer boundary, enabling potential remote code execution. The fix adds two targeted overflow checks before the arithmetic operations, preventing the al

high

Integer Overflow in PlayerAnimation.cpp memcpy Size Calculations

A critical integer overflow vulnerability was discovered in `animation/PlayerAnimation.cpp` where `vCount * sizeof(float) * 3` calculations could wrap around on 32-bit platforms when processing malicious animation files. An attacker could craft a model file with an oversized vertex count to trigger a heap buffer overflow via memcpy. The fix adds bounds checks against `SIZE_MAX` before all size computations used in memory copy operations.

critical

Integer Overflow in edge_detect.c Heap Allocation Enables Camera-Based Exploit

A critical integer overflow vulnerability in `C/filters/edge_detect.c` allowed an attacker controlling a virtual V4L2 camera device to supply manipulated width/height dimensions that would silently wrap around to zero during multiplication, causing a drastically undersized heap allocation. Subsequent writes to this tiny buffer result in heap corruption, potentially enabling arbitrary code execution. The fix replaces the unsafe `malloc(w * h)` pattern with overflow-safe `calloc((size_t)w, (size_t

high

How cryptographic binding vulnerabilities happen in Rust OpenSSL and how to fix it

CVE-2026-41676 is a high-severity vulnerability in the rust-openssl crate that could allow attackers to exploit cryptographic operations. The fix involves upgrading from version 0.10.63 to 0.10.81, removing unsafe dependency chains, and ensuring proper OpenSSL binding integrity. This vulnerability demonstrates why keeping cryptographic libraries current is critical for production Rust applications.