CVE-2026-34235: Heap OOB Read in PJSIP VP9 RTP Unpacketizer
Overview
| CVE | CVE-2026-34235 |
| Advisory | GHSA-pqrm-53pc-wx28 |
| Component | pjmedia/src/pjmedia-codec/vpx_packetizer.c (pjmedia_vpx_unpacketize()) |
| Affected | pjproject < 2.17 |
| Fixed | pjproject 2.17 (f4c7d08) |
| Class | CWE-125 (Out-of-Bounds Read) |
| CVSS v3.1 | 9.1 (Critical, NVD) -- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:H |
| Found by | Lukas Johannes Moeller (@JohannesLks) |
TL;DR
pjproject's VP9 RTP unpacketizer carefully bounds-checks descriptor bytes one at a time, then skips VP9 Scalability Structure resolution data in bulk without checking the new offset. A 3-byte RTP payload can make the parser read 31 bytes past the heap buffer during VP9 media handling.
Root Cause
The parser uses INC_DESC_LEN() for safe single-byte descriptor progress:
#define INC_DESC_LEN() \
desc_len++; \
if (desc_len >= payload_len) \
return PJ_ETOOSMALL;
The vulnerable path handles VP9 Scalability Structure data. N_S is attacker-controlled through three bits and can be 1-8. When the Y bit is set, the code skips N_S * 4 bytes without applying the same invariant:
unsigned N_S = (*q >> 5) + 1;
INC_DESC_LEN();
if (*q & 0x10)
desc_len += N_S * 4; // unchecked bulk advance
if (*q & 0x08) {
unsigned N_G = *(p + desc_len); // OOB read
}
With N_S = 8, the unchecked advance is 32 bytes. On a 3-byte payload, desc_len lands 31 bytes beyond the allocation before the N_G read.
Minimal Trigger
// byte 0: VP9 descriptor with V flag set
// byte 1: N_S=8, Y=1, G=1
// byte 2: one available byte before the unchecked skip
unsigned char payload[] = {0x02, 0xF8, 0x00};
==115140==ERROR: AddressSanitizer: heap-buffer-overflow
READ of size 1 in pjmedia_vpx_unpacketize vpx_packetizer.c
0x502000000052 is located 31 bytes after 3-byte region
Impact
- Remote crash of applications using pjproject VP9 RTP receive paths during an active media session.
- No authentication is implied by the media parser itself; exploitability depends on whether the application accepts attacker-controlled VP9 RTP.
- No code execution primitive was demonstrated. The read result is parser-internal and the practical outcome is denial of service.
Fix
The fix mirrors the parser's existing invariant after the bulk skip:
if (*q & 0x10) {
desc_len += N_S * 4;
if (desc_len >= payload_len)
return PJ_ETOOSMALL;
}
Released in pjproject 2.17 and tracked as GHSA-pqrm-53pc-wx28.