CVE-2022-24786 (published 2022-04-06T14:15Z)

PJSIP is a free and open source multimedia communication library written in C. PJSIP versions 2.12 and prior do not parse incoming RTCP feedback RPSI (Reference Picture Selection Indication) packet, but any app that directly uses pjmedia_rtcp_fb_parse_rpsi() will be affected. A patch is available in the `master` branch of the `pjsip/pjproject` GitHub repository. There are currently no known workarounds.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-125
cwe_name: Out-of-bounds Read
description: The product reads data past the end, or before the beginning, of the intended buffer.
extended_description: 
            
            
             
			 
         
         
cwe_id: CWE-787
cwe_name: Out-of-bounds Write
description: The product writes data past the end, or before the beginning, of the intended buffer.
extended_description: 
            
            
             
			 
         
         

Fix information:
repo_url: https://github.com/pjsip/pjproject
commit_details: repo_url: https://github.com/pjsip/pjproject
author: Riza Sulistyo
author_date: 2022-04-06 10:40:09+07:00
msg: Merge pull request from GHSA-vhxv-phmx-g52q

* Prevent OOB read/write when parsing RTCP FB RPSI

* Add log information

* Modification based on comments.
repository: repo_url: https://github.com/pjsip/pjproject
repo_name: pjsip/pjproject
description: PJSIP project
repo_language: C
owner: pjsip
file_changes: filename: rtcp.h
change_type: ModificationType.MODIFY
diff: @@ -115,6 +115,15 @@ typedef struct pjmedia_rtcp_common
 } pjmedia_rtcp_common;
 
 
+/**
+ * RTCP feedback common header.
+ */
+typedef struct pjmedia_rtcp_fb_common
+{
+    pjmedia_rtcp_common rtcp_common;
+    pj_uint32_t	    ssrc_src;	/**< SSRC media source	    */
+} pjmedia_rtcp_fb_common;
+
 /**
  * This structure declares default RTCP packet (SR) that is sent by pjmedia.
  * Incoming RTCP packet may have different format, and must be parsed
@@ -234,6 +243,8 @@ typedef struct pjmedia_rtcp_session
     char		   *name;	/**< Name identification.	    */
     pjmedia_rtcp_sr_pkt	    rtcp_sr_pkt;/**< Cached RTCP SR packet.	    */
     pjmedia_rtcp_rr_pkt	    rtcp_rr_pkt;/**< Cached RTCP RR packet.	    */
+    pjmedia_rtcp_fb_common  rtcp_fb_com;/**< Cached RTCP feedback common 
+					     header packet.		    */
     
     pjmedia_rtp_seq_session seq_ctrl;	/**< RTCP sequence number control.  */
     unsigned		    rtp_last_ts;/**< Last timestamp in RX RTP pkt.  */

programming_language: C
filename: rtcp.c
change_type: ModificationType.MODIFY
diff: @@ -242,6 +242,11 @@ PJ_DEF(void) pjmedia_rtcp_init2( pjmedia_rtcp_session *sess,
     sess->rtcp_rr_pkt.common.pt = RTCP_RR;
     sess->rtcp_rr_pkt.common.length = pj_htons(7);
 
+    /* Copy to RTCP FB common header */
+    pj_memcpy(&sess->rtcp_fb_com, &sr_pkt->common, 
+	      sizeof(pjmedia_rtcp_common));
+    sess->rtcp_fb_com.ssrc_src = 0;
+
     /* Get time and timestamp base and frequency */
     pj_gettimeofday(&now);
     sess->tv_base = now;

programming_language: C
filename: rtcp_fb.c
change_type: ModificationType.MODIFY
diff: @@ -43,7 +43,7 @@ PJ_DEF(pj_status_t) pjmedia_rtcp_fb_build_nack(
 					unsigned nack_cnt,
 					const pjmedia_rtcp_fb_nack nack[])
 {
-    pjmedia_rtcp_common *hdr;
+    pjmedia_rtcp_fb_common *hdr;
     pj_uint8_t *p;
     unsigned len, i;
 
@@ -54,11 +54,11 @@ PJ_DEF(pj_status_t) pjmedia_rtcp_fb_build_nack(
 	return PJ_ETOOSMALL;
 
     /* Build RTCP-FB NACK header */
-    hdr = (pjmedia_rtcp_common*)buf;
-    pj_memcpy(hdr, &session->rtcp_rr_pkt.common,  sizeof(*hdr));
-    hdr->pt = RTCP_RTPFB;
-    hdr->count = 1; /* FMT = 1 */
-    hdr->length = pj_htons((pj_uint16_t)(len/4 - 1));
+    hdr = (pjmedia_rtcp_fb_common*)buf;
+    pj_memcpy(hdr, &session->rtcp_fb_com, sizeof(*hdr));
+    hdr->rtcp_common.pt = RTCP_RTPFB;
+    hdr->rtcp_common.count = 1; /* FMT = 1 */
+    hdr->rtcp_common.length = pj_htons((pj_uint16_t)(len/4 - 1));
 
     /* Build RTCP-FB NACK FCI */
     p = (pj_uint8_t*)hdr + sizeof(*hdr);
@@ -86,7 +86,7 @@ PJ_DEF(pj_status_t) pjmedia_rtcp_fb_build_pli(
 					void *buf,
 					pj_size_t *length)
 {
-    pjmedia_rtcp_common *hdr;
+    pjmedia_rtcp_fb_common *hdr;
     unsigned len;
 
     PJ_ASSERT_RETURN(session && buf && length, PJ_EINVAL);
@@ -96,11 +96,11 @@ PJ_DEF(pj_status_t) pjmedia_rtcp_fb_build_pli(
 	return PJ_ETOOSMALL;
 
     /* Build RTCP-FB PLI header */
-    hdr = (pjmedia_rtcp_common*)buf;
-    pj_memcpy(hdr, &session->rtcp_rr_pkt.common,  sizeof(*hdr));
-    hdr->pt = RTCP_PSFB;
-    hdr->count = 1; /* FMT = 1 */
-    hdr->length = pj_htons((pj_uint16_t)(len/4 - 1));
+    hdr = (pjmedia_rtcp_fb_common*)buf;
+    pj_memcpy(hdr, &session->rtcp_fb_com, sizeof(*hdr));
+    hdr->rtcp_common.pt = RTCP_PSFB;
+    hdr->rtcp_common.count = 1; /* FMT = 1 */
+    hdr->rtcp_common.length = pj_htons((pj_uint16_t)(len/4 - 1));
 
     /* Finally */
     *length = len;
@@ -119,7 +119,7 @@ PJ_DEF(pj_status_t) pjmedia_rtcp_fb_build_sli(
 					unsigned sli_cnt,
 					const pjmedia_rtcp_fb_sli sli[])
 {
-    pjmedia_rtcp_common *hdr;
+    pjmedia_rtcp_fb_common *hdr;
     pj_uint8_t *p;
     unsigned len, i;
 
@@ -130,11 +130,11 @@ PJ_DEF(pj_status_t) pjmedia_rtcp_fb_build_sli(
 	return PJ_ETOOSMALL;
 
     /* Build RTCP-FB SLI header */
-    hdr = (pjmedia_rtcp_common*)buf;
-    pj_memcpy(hdr, &session->rtcp_rr_pkt.common,  sizeof(*hdr));
-    hdr->pt = RTCP_PSFB;
-    hdr->count = 2; /* FMT = 2 */
-    hdr->length = pj_htons((pj_uint16_t)(len/4 - 1));
+    hdr = (pjmedia_rtcp_fb_common*)buf;
+    pj_memcpy(hdr, &session->rtcp_fb_com, sizeof(*hdr));
+    hdr->rtcp_common.pt = RTCP_PSFB;
+    hdr->rtcp_common.count = 2; /* FMT = 2 */
+    hdr->rtcp_common.length = pj_htons((pj_uint16_t)(len/4 - 1));
 
     /* Build RTCP-FB SLI FCI */
     p = (pj_uint8_t*)hdr + sizeof(*hdr);
@@ -166,7 +166,7 @@ PJ_DEF(pj_status_t) pjmedia_rtcp_fb_build_rpsi(
 					    pj_size_t *length,
 					    const pjmedia_rtcp_fb_rpsi *rpsi)
 {
-    pjmedia_rtcp_common *hdr;
+    pjmedia_rtcp_fb_common *hdr;
     pj_uint8_t *p;
     unsigned bitlen, padlen, len;
 
@@ -179,11 +179,11 @@ PJ_DEF(pj_status_t) pjmedia_rtcp_fb_build_rpsi(
 	return PJ_ETOOSMALL;
 
     /* Build RTCP-FB RPSI header */
-    hdr = (pjmedia_rtcp_common*)buf;
-    pj_memcpy(hdr, &session->rtcp_rr_pkt.common,  sizeof(*hdr));
-    hdr->pt = RTCP_PSFB;
-    hdr->count = 3; /* FMT = 3 */
-    hdr->length = pj_htons((pj_uint16_t)(len/4 - 1));
+    hdr = (pjmedia_rtcp_fb_common*)buf;
+    pj_memcpy(hdr, &session->rtcp_fb_com, sizeof(*hdr));
+    hdr->rtcp_common.pt = RTCP_PSFB;
+    hdr->rtcp_common.count = 3; /* FMT = 3 */
+    hdr->rtcp_common.length = pj_htons((pj_uint16_t)(len/4 - 1));
 
     /* Build RTCP-FB RPSI FCI */
     p = (pj_uint8_t*)hdr + sizeof(*hdr);
@@ -620,18 +620,18 @@ PJ_DEF(pj_status_t) pjmedia_rtcp_fb_parse_nack(
 					unsigned *nack_cnt,
 					pjmedia_rtcp_fb_nack nack[])
 {
-    pjmedia_rtcp_common *hdr = (pjmedia_rtcp_common*) buf;
+    pjmedia_rtcp_fb_common *hdr = (pjmedia_rtcp_fb_common*) buf;
     pj_uint8_t *p;
     unsigned cnt, i;
 
     PJ_ASSERT_RETURN(buf && nack_cnt && nack, PJ_EINVAL);
-    PJ_ASSERT_RETURN(length >= sizeof(pjmedia_rtcp_common), PJ_ETOOSMALL);
+    PJ_ASSERT_RETURN(length >= sizeof(pjmedia_rtcp_fb_common), PJ_ETOOSMALL);
 
     /* Generic NACK uses pt==RTCP_RTPFB and FMT==1 */
-    if (hdr->pt != RTCP_RTPFB || hdr->count != 1)
+    if (hdr->rtcp_common.pt != RTCP_RTPFB || hdr->rtcp_common.count != 1)
 	return PJ_ENOTFOUND;
 
-    cnt = pj_ntohs((pj_uint16_t)hdr->length);
+    cnt = pj_ntohs((pj_uint16_t)hdr->rtcp_common.length);
     if (cnt > 2) cnt -= 2; else cnt = 0;
     if (length < (cnt+3)*4)
 	return PJ_ETOOSMALL;
@@ -661,7 +661,7 @@ PJ_DEF(pj_status_t) pjmedia_rtcp_fb_parse_pli(
 					const void *buf,
 					pj_size_t length)
 {
-    pjmedia_rtcp_common *hdr = (pjmedia_rtcp_common*) buf;
+    pjmedia_rtcp_fb_common *hdr = (pjmedia_rtcp_fb_common*) buf;
 
     PJ_ASSERT_RETURN(buf, PJ_EINVAL);
 
@@ -669,7 +669,7 @@ PJ_DEF(pj_status_t) pjmedia_rtcp_fb_parse_pli(
     	return PJ_ETOOSMALL;
 
     /* PLI uses pt==RTCP_PSFB and FMT==1 */
-    if (hdr->pt != RTCP_PSFB || hdr->count != 1)
+    if (hdr->rtcp_common.pt != RTCP_PSFB || hdr->rtcp_common.count != 1)
 	return PJ_ENOTFOUND;
 
     return PJ_SUCCESS;
@@ -686,18 +686,18 @@ PJ_DEF(pj_status_t) pjmedia_rtcp_fb_parse_sli(
 					unsigned *sli_cnt,
 					pjmedia_rtcp_fb_sli sli[])
 {
-    pjmedia_rtcp_common *hdr = (pjmedia_rtcp_common*) buf;
+    pjmedia_rtcp_fb_common *hdr = (pjmedia_rtcp_fb_common*) buf;
     pj_uint8_t *p;
     unsigned cnt, i;
 
     PJ_ASSERT_RETURN(buf && sli_cnt && sli, PJ_EINVAL);
-    PJ_ASSERT_RETURN(length >= sizeof(pjmedia_rtcp_common), PJ_ETOOSMALL);
+    PJ_ASSERT_RETURN(length >= sizeof(pjmedia_rtcp_fb_common), PJ_ETOOSMALL);
 
     /* PLI uses pt==RTCP_PSFB and FMT==2 */
-    if (hdr->pt != RTCP_PSFB || hdr->count != 2)
+    if (hdr->rtcp_common.pt != RTCP_PSFB || hdr->rtcp_common.count != 2)
 	return PJ_ENOTFOUND;
 
-    cnt = pj_ntohs((pj_uint16_t)hdr->length) - 2;
+    cnt = pj_ntohs((pj_uint16_t)hdr->rtcp_common.length) - 2;
     if (length < (cnt+3)*4)
 	return PJ_ETOOSMALL;
 
@@ -730,24 +730,43 @@ PJ_DEF(pj_status_t) pjmedia_rtcp_fb_parse_rpsi(
 					pj_size_t length,
 					pjmedia_rtcp_fb_rpsi *rpsi)
 {
-    pjmedia_rtcp_common *hdr = (pjmedia_rtcp_common*) buf;
+    pjmedia_rtcp_fb_common *hdr = (pjmedia_rtcp_fb_common*) buf;
     pj_uint8_t *p;
     pj_uint8_t padlen;
     pj_size_t rpsi_len;
 
     PJ_ASSERT_RETURN(buf && rpsi, PJ_EINVAL);
-    PJ_ASSERT_RETURN(length >= sizeof(pjmedia_rtcp_common), PJ_ETOOSMALL);
+    PJ_ASSERT_RETURN(length >= sizeof(pjmedia_rtcp_fb_common), PJ_ETOOSMALL);
 
     /* RPSI uses pt==RTCP_PSFB and FMT==3 */
-    if (hdr->pt != RTCP_PSFB || hdr->count != 3)
+    if (hdr->rtcp_common.pt != RTCP_PSFB || hdr->rtcp_common.count != 3)
 	return PJ_ENOTFOUND;
 
-    rpsi_len = (pj_ntohs((pj_uint16_t)hdr->length)-2) * 4;
+    if (hdr->rtcp_common.length < 3) {    
+        PJ_PERROR(3, (THIS_FILE, PJ_ETOOSMALL,
+                      "Failed parsing FB RPSI, invalid header length"));
+	return PJ_ETOOSMALL;
+    }
+
+    rpsi_len = (pj_ntohs((pj_uint16_t)hdr->rtcp_common.length)-2) * 4;
     if (length < rpsi_len + 12)
 	return PJ_ETOOSMALL;
 
     p = (pj_uint8_t*)hdr + sizeof(*hdr);
     padlen = *p++;
+
+    if (padlen >= 32) {
+        PJ_PERROR(3, (THIS_FILE, PJ_ETOOBIG,
+                      "Failed parsing FB RPSI, invalid RPSI padding len"));
+	return PJ_ETOOBIG;
+    }
+
+    if ((rpsi_len * 8) < (unsigned)(16 + padlen)) {
+        PJ_PERROR(3, (THIS_FILE, PJ_ETOOSMALL,
+                      "Failed parsing FB RPSI, invalid RPSI bit len"));
+	return PJ_ETOOSMALL;
+    }
+
     rpsi->pt = (*p++ & 0x7F);
     rpsi->rpsi_bit_len = rpsi_len*8 - 16 - padlen;
     pj_strset(&rpsi->rpsi, (char*)p, (rpsi->rpsi_bit_len + 7)/8);

programming_language: C