VYPR
High severity8.8NVD Advisory· Published Apr 21, 2026· Updated Apr 23, 2026

CVE-2026-40614

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

1
  • cpe:2.3:a:pjsip:pjsip:*:*:*:*:*:*:*:*
    Range: <2.17

Patches

1
17897e835818

Merge commit from fork

https://github.com/pjsip/pjprojectNanang IzzuddinApr 14, 2026via nvd-ref
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

2

News mentions

0

No linked articles in our index yet.