CVE-2022-39244 (published 2022-10-06T18:16Z)

PJSIP is a free and open source multimedia communication library written in C. In versions of PJSIP prior to 2.13 the PJSIP parser, PJMEDIA RTP decoder, and PJMEDIA SDP parser are affeced by a buffer overflow vulnerability. Users connecting to untrusted clients are at risk. This issue has been patched and is available as commit c4d3498 in the master branch and will be included in releases 2.13 and later. Users are advised to upgrade. There are no known workarounds for this issue.

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-120
cwe_name: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow')
description: The product copies an input buffer to an output buffer without verifying that the size of the input buffer is less than the size of the output buffer, leading to a buffer overflow.
extended_description: A buffer overflow condition exists when a product attempts to put more data in a buffer than it can hold, or when it attempts to put data in a memory area outside of the boundaries of a buffer. The simplest type of error, and the most common cause of buffer overflows, is the "classic" case in which the product copies the buffer without restricting how much is copied. Other variants exist, but the existence of a classic overflow strongly suggests that the programmer is not considering even the most basic of security protections.
         

Fix information:
repo_url: https://github.com/pjsip/pjproject
commit_details: repo_url: https://github.com/pjsip/pjproject
author: sauwming
author_date: 2022-10-03 08:07:22+08:00
msg: Merge pull request from GHSA-fq45-m3f7-3mhj

* Initial patch

* Use 'pj_scan_is_eof(scanner)'

Co-authored-by: Aaron Lichtman <aaronlichtman@gmail.com>

* Use 'pj_scan_is_eof(scanner)'

Co-authored-by: Aaron Lichtman <aaronlichtman@gmail.com>

* Use 'pj_scan_is_eof(scanner)'

Co-authored-by: Aaron Lichtman <aaronlichtman@gmail.com>

* Use `!pj_scan_is_eof` instead of manually checking `scanner->curptr < scanner->end`

Co-authored-by: Maksim Mukosey <mmukosey@gmail.com>

* Update pjlib-util/src/pjlib-util/scanner.c

Co-authored-by: Aaron Lichtman <aaronlichtman@gmail.com>

* Update pjlib-util/src/pjlib-util/scanner.c

Co-authored-by: Aaron Lichtman <aaronlichtman@gmail.com>

* Update pjlib-util/src/pjlib-util/scanner.c

Co-authored-by: Aaron Lichtman <aaronlichtman@gmail.com>

* Revert '>=' back to '>' in pj_scan_stricmp_alnum()

* Fix error compiles.

Co-authored-by: Nanang Izzuddin <nanang@teluu.com>
Co-authored-by: Aaron Lichtman <aaronlichtman@gmail.com>
Co-authored-by: Maksim Mukosey <mmukosey@gmail.com>
repository: repo_url: https://github.com/pjsip/pjproject
repo_name: pjsip/pjproject
description: PJSIP project
repo_language: C
owner: pjsip
file_changes: filename: scanner.c
change_type: ModificationType.MODIFY
diff: @@ -195,7 +195,13 @@ PJ_DEF(void) pj_scan_skip_whitespace( pj_scanner *scanner )
 
 PJ_DEF(void) pj_scan_skip_line( pj_scanner *scanner )
 {
-    char *s = pj_memchr(scanner->curptr, '\n', scanner->end - scanner->curptr);
+    char *s;
+
+    if (pj_scan_is_eof(scanner)) {
+        return;
+    }
+
+    s = pj_memchr(scanner->curptr, '\n', scanner->end - scanner->curptr);
     if (!s) {
 	scanner->curptr = scanner->end;
     } else {
@@ -264,8 +270,7 @@ PJ_DEF(void) pj_scan_get( pj_scanner *scanner,
 
     pj_assert(pj_cis_match(spec,0)==0);
 
-    /* EOF is detected implicitly */
-    if (!pj_cis_match(spec, *s)) {
+    if (pj_scan_is_eof(scanner) || !pj_cis_match(spec, *s)) {
 	pj_scan_syntax_err(scanner);
 	return;
     }
@@ -299,8 +304,7 @@ PJ_DEF(void) pj_scan_get_unescape( pj_scanner *scanner,
     /* Must not match character '%' */
     pj_assert(pj_cis_match(spec,'%')==0);
 
-    /* EOF is detected implicitly */
-    if (!pj_cis_match(spec, *s) && *s != '%') {
+    if (pj_scan_is_eof(scanner) || !pj_cis_match(spec, *s) && *s != '%') {
 	pj_scan_syntax_err(scanner);
 	return;
     }
@@ -436,7 +440,9 @@ PJ_DEF(void) pj_scan_get_n( pj_scanner *scanner,
     
     scanner->curptr += N;
 
-    if (PJ_SCAN_IS_PROBABLY_SPACE(*scanner->curptr) && scanner->skip_ws) {
+    if (!pj_scan_is_eof(scanner) &&
+	PJ_SCAN_IS_PROBABLY_SPACE(*scanner->curptr) && scanner->skip_ws)
+    {
 	pj_scan_skip_whitespace(scanner);
     }
 }
@@ -467,15 +473,16 @@ PJ_DEF(int) pj_scan_get_char( pj_scanner *scanner )
 
 PJ_DEF(void) pj_scan_get_newline( pj_scanner *scanner )
 {
-    if (!PJ_SCAN_IS_NEWLINE(*scanner->curptr)) {
+    if (pj_scan_is_eof(scanner) || !PJ_SCAN_IS_NEWLINE(*scanner->curptr)) {
 	pj_scan_syntax_err(scanner);
 	return;
     }
 
+    /* We have checked scanner->curptr validity above */
     if (*scanner->curptr == '\r') {
 	++scanner->curptr;
     }
-    if (*scanner->curptr == '\n') {
+    if (!pj_scan_is_eof(scanner) && *scanner->curptr == '\n') {
 	++scanner->curptr;
     }
 
@@ -520,7 +527,9 @@ PJ_DEF(void) pj_scan_get_until( pj_scanner *scanner,
 
     scanner->curptr = s;
 
-    if (PJ_SCAN_IS_PROBABLY_SPACE(*s) && scanner->skip_ws) {
+    if (!pj_scan_is_eof(scanner) && PJ_SCAN_IS_PROBABLY_SPACE(*s) &&
+	scanner->skip_ws)
+    {
 	pj_scan_skip_whitespace(scanner);
     }
 }
@@ -544,7 +553,9 @@ PJ_DEF(void) pj_scan_get_until_ch( pj_scanner *scanner,
 
     scanner->curptr = s;
 
-    if (PJ_SCAN_IS_PROBABLY_SPACE(*s) && scanner->skip_ws) {
+    if (!pj_scan_is_eof(scanner) && PJ_SCAN_IS_PROBABLY_SPACE(*s) &&
+	scanner->skip_ws)
+    {
 	pj_scan_skip_whitespace(scanner);
     }
 }
@@ -570,7 +581,9 @@ PJ_DEF(void) pj_scan_get_until_chr( pj_scanner *scanner,
 
     scanner->curptr = s;
 
-    if (PJ_SCAN_IS_PROBABLY_SPACE(*s) && scanner->skip_ws) {
+    if (!pj_scan_is_eof(scanner) && PJ_SCAN_IS_PROBABLY_SPACE(*s) &&
+	scanner->skip_ws)
+    {
 	pj_scan_skip_whitespace(scanner);
     }
 }
@@ -585,7 +598,9 @@ PJ_DEF(void) pj_scan_advance_n( pj_scanner *scanner,
 
     scanner->curptr += N;
 
-    if (PJ_SCAN_IS_PROBABLY_SPACE(*scanner->curptr) && skip_ws) {
+    if (!pj_scan_is_eof(scanner) && 
+	PJ_SCAN_IS_PROBABLY_SPACE(*scanner->curptr) && skip_ws)
+    {
 	pj_scan_skip_whitespace(scanner);
     }
 }
@@ -636,5 +651,3 @@ PJ_DEF(void) pj_scan_restore_state( pj_scanner *scanner,
     scanner->line = state->line;
     scanner->start_line = state->start_line;
 }
-
-

programming_language: C
filename: rtp.c
change_type: ModificationType.MODIFY
diff: @@ -188,6 +188,11 @@ PJ_DEF(pj_status_t) pjmedia_rtp_decode_rtp2(
     /* Payload is located right after header plus CSRC */
     offset = sizeof(pjmedia_rtp_hdr) + ((*hdr)->cc * sizeof(pj_uint32_t));
 
+    /* Check that offset is less than packet size */
+    if (offset >= pkt_len) {
+        return PJMEDIA_RTP_EINLEN;
+    }
+
     /* Decode RTP extension. */
     if ((*hdr)->x) {
         if (offset + sizeof (pjmedia_rtp_ext_hdr) > (unsigned)pkt_len)
@@ -202,8 +207,8 @@ PJ_DEF(pj_status_t) pjmedia_rtp_decode_rtp2(
 	dec_hdr->ext_len = 0;
     }
 
-    /* Check that offset is less than packet size */
-    if (offset > pkt_len)
+    /* Check again that offset is still less than packet size */
+    if (offset >= pkt_len)
 	return PJMEDIA_RTP_EINLEN;
 
     /* Find and set payload. */
@@ -393,5 +398,3 @@ void pjmedia_rtp_seq_update( pjmedia_rtp_seq_session *sess,
 	seq_status->status.value = st.status.value;
     }
 }
-
-

programming_language: C
filename: sdp.c
change_type: ModificationType.MODIFY
diff: @@ -983,13 +983,13 @@ static void parse_version(pj_scanner *scanner,
     ctx->last_error = PJMEDIA_SDP_EINVER;
 
     /* check equal sign */
-    if (*(scanner->curptr+1) != '=') {
+    if (scanner->curptr+1 >= scanner->end || *(scanner->curptr+1) != '=') {
 	on_scanner_error(scanner);
 	return;
     }
 
     /* check version is 0 */
-    if (*(scanner->curptr+2) != '0') {
+    if (scanner->curptr+2 >= scanner->end || *(scanner->curptr+2) != '0') {
 	on_scanner_error(scanner);
 	return;
     }
@@ -1006,7 +1006,7 @@ static void parse_origin(pj_scanner *scanner, pjmedia_sdp_session *ses,
     ctx->last_error = PJMEDIA_SDP_EINORIGIN;
 
     /* check equal sign */
-    if (*(scanner->curptr+1) != '=') {
+    if (scanner->curptr+1 >= scanner->end || *(scanner->curptr+1) != '=') {
 	on_scanner_error(scanner);
 	return;
     }
@@ -1052,7 +1052,7 @@ static void parse_time(pj_scanner *scanner, pjmedia_sdp_session *ses,
     ctx->last_error = PJMEDIA_SDP_EINTIME;
 
     /* check equal sign */
-    if (*(scanner->curptr+1) != '=') {
+    if (scanner->curptr+1 >= scanner->end || *(scanner->curptr+1) != '=') {
 	on_scanner_error(scanner);
 	return;
     }
@@ -1080,7 +1080,7 @@ static void parse_generic_line(pj_scanner *scanner, pj_str_t *str,
     ctx->last_error = PJMEDIA_SDP_EINSDP;
 
     /* check equal sign */
-    if (*(scanner->curptr+1) != '=') {
+    if ((scanner->curptr+1 >= scanner->end) || *(scanner->curptr+1) != '=') {
 	on_scanner_error(scanner);
 	return;
     }
@@ -1149,7 +1149,7 @@ static void parse_media(pj_scanner *scanner, pjmedia_sdp_media *med,
     ctx->last_error = PJMEDIA_SDP_EINMEDIA;
 
     /* check the equal sign */
-    if (*(scanner->curptr+1) != '=') {
+    if (scanner->curptr+1 >= scanner->end || *(scanner->curptr+1) != '=') {
 	on_scanner_error(scanner);
 	return;
     }
@@ -1164,6 +1164,10 @@ static void parse_media(pj_scanner *scanner, pjmedia_sdp_media *med,
     /* port */
     pj_scan_get(scanner, &cs_token, &str);
     med->desc.port = (unsigned short)pj_strtoul(&str);
+    if (pj_scan_is_eof(scanner)) {
+        on_scanner_error(scanner);
+        return;
+    }
     if (*scanner->curptr == '/') {
 	/* port count */
 	pj_scan_get_char(scanner);
@@ -1175,7 +1179,7 @@ static void parse_media(pj_scanner *scanner, pjmedia_sdp_media *med,
     }
 
     if (pj_scan_get_char(scanner) != ' ') {
-	PJ_THROW(SYNTAX_ERROR);
+	on_scanner_error(scanner);
     }
 
     /* transport */
@@ -1183,7 +1187,7 @@ static void parse_media(pj_scanner *scanner, pjmedia_sdp_media *med,
 
     /* format list */
     med->desc.fmt_count = 0;
-    while (*scanner->curptr == ' ') {
+    while (scanner->curptr < scanner->end && *scanner->curptr == ' ') {
 	pj_str_t fmt;
 
 	pj_scan_get_char(scanner);
@@ -1223,7 +1227,7 @@ static pjmedia_sdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner,
     attr = PJ_POOL_ALLOC_T(pool, pjmedia_sdp_attr);
 
     /* check equal sign */
-    if (*(scanner->curptr+1) != '=') {
+    if (scanner->curptr+1 >= scanner->end || *(scanner->curptr+1) != '=') {
 	on_scanner_error(scanner);
 	return NULL;
     }
@@ -1242,7 +1246,7 @@ static pjmedia_sdp_attr *parse_attr( pj_pool_t *pool, pj_scanner *scanner,
 	    pj_scan_get_char(scanner);
 
 	/* get value */
-	if (*scanner->curptr != '\r' && *scanner->curptr != '\n') {
+	if (!pj_scan_is_eof(scanner) && *scanner->curptr != '\r' && *scanner->curptr != '\n') {
 	    pj_scan_get_until_chr(scanner, "\r\n", &attr->value);
 	} else {
 	    attr->value.ptr = NULL;

programming_language: C