CVE-2026-40614
Description
PJSIP is a free and open source multimedia communication library written in C. In 2.16 and earlier, there is a buffer overflow when decoding Opus audio frames due to insufficient buffer size validation in the Opus codec decode path. The FEC decode buffers (dec_frame[].buf) were allocated based on a PCM-derived formula: (sample_rate/1000) * 60 * channel_cnt * 2. At 8 kHz mono this yields only 960 bytes, but codec_parse() can output encoded frames up to MAX_ENCODED_PACKET_SIZE (1280) bytes via opus_repacketizer_out_range(). The three pj_memcpy() calls in codec_decode() copied input->size bytes without bounds checking, causing a heap buffer overflow.
Affected products
1Patches
117897e835818Merge commit from fork
1 file changed · +32 −9
pjmedia/src/pjmedia-codec/opus.c+32 −9 modified@@ -145,6 +145,7 @@ struct opus_data unsigned dec_ptime_denum; pjmedia_frame dec_frame[2]; int dec_frame_index; + pj_size_t dec_frame_buf_size; }; /* Codec factory instance */ @@ -518,10 +519,14 @@ static pj_status_t factory_default_attr( pjmedia_codec_factory *factory, attr->setting.complexity = opus_cfg.complexity; attr->setting.cbr = opus_cfg.cbr; - /* Set max RX frame size to 1275 (max Opus frame size) to anticipate - * possible ptime change on the fly. + /* Set max RX frame size. When codec_parse() splits a multi-frame + * packet via opus_repacketizer_out_range(), each single-frame output + * is a complete Opus packet (TOC + frame data + optional padding). + * Per the Opus API docs the worst-case size is 1277 bytes per frame. + * Use MAX_ENCODED_PACKET_SIZE (1280) for consistency with codec_parse() + * and a small safety margin. */ - attr->info.max_rx_frame_size = 1275; + attr->info.max_rx_frame_size = MAX_ENCODED_PACKET_SIZE; generate_fmtp(attr); @@ -800,15 +805,20 @@ static pj_status_t codec_open( pjmedia_codec *codec, return PJMEDIA_CODEC_EFAILED; } - /* Initialize temporary decode frames used for FEC */ + /* Initialize temporary decode frames used for FEC. + * These buffers hold encoded input data, so they must be at least + * MAX_ENCODED_PACKET_SIZE to accept any frame from codec_parse(). + */ + opus_data->dec_frame_buf_size = (opus_data->cfg.sample_rate / 1000) + * 60 * attr->info.channel_cnt * 2 /* bytes per sample */; + if (opus_data->dec_frame_buf_size < MAX_ENCODED_PACKET_SIZE) + opus_data->dec_frame_buf_size = MAX_ENCODED_PACKET_SIZE; opus_data->dec_frame[0].type = PJMEDIA_FRAME_TYPE_NONE; - opus_data->dec_frame[0].buf = pj_pool_zalloc(opus_data->pool, - (opus_data->cfg.sample_rate / 1000) - * 60 * attr->info.channel_cnt * 2 /* bytes per sample */); + opus_data->dec_frame[0].buf = pj_pool_zalloc(opus_data->pool, + opus_data->dec_frame_buf_size); opus_data->dec_frame[1].type = PJMEDIA_FRAME_TYPE_NONE; opus_data->dec_frame[1].buf = pj_pool_zalloc(opus_data->pool, - (opus_data->cfg.sample_rate / 1000) - * 60 * attr->info.channel_cnt * 2 /* bytes per sample */); + opus_data->dec_frame_buf_size); opus_data->dec_frame_index = -1; /* Initialize the repacketizers */ @@ -1101,6 +1111,19 @@ static pj_status_t codec_decode( pjmedia_codec *codec, pj_mutex_lock (opus_data->mutex); + /* Validate input size against decode buffer capacity before any copy. + * This is a safety check to ensure the pj_memcpy() calls below + * cannot overflow dec_frame[].buf. + */ + if (input->size > opus_data->dec_frame_buf_size) { + PJ_LOG(4, (THIS_FILE, "Opus decode: input size (%u) exceeds decode " + "buffer (%u), dropping frame", + (unsigned)input->size, + (unsigned)opus_data->dec_frame_buf_size)); + pj_mutex_unlock (opus_data->mutex); + return PJMEDIA_CODEC_EFRMTOOSHORT; + } + if (opus_data->dec_frame_index == -1) { /* First packet, buffer it. */ opus_data->dec_frame[0].type = input->type;
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2News mentions
0No linked articles in our index yet.