CVE-2022-24793: Buffer Copy without Checking Size of Input ('Classic Buffer Overflow') (CWE-120)
value: PJSIP is a free and open source multimedia communication library written in C. A buffer overflow vulnerability in versions 2.12 and prior affects applications that use PJSIP DNS resolution. It doesn't affect PJSIP users who utilize an external resolver. This vulnerability is related to CVE-2023-27585. The difference is that this issue is in parsing the query record `parse_rr()`, while the issue in CVE-2023-27585 is in `parse_query()`. A patch is available in the `master` branch of the `pjsip/pjproject` GitHub repository. A workaround is to disable DNS resolution in PJSIP config (by setting `nameserver_count` to zero) or use an external resolver instead.
CWE 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.
CVSS v3 base score: 7.5
Published: 2022-04-06T14:15Z
Repository: https://github.com/pjsip/pjproject
Language: C
Files changed: pjlib-util/src/pjlib-util/dns.c
Commit date: 2022-04-06 11:49:47 +0800
Commit message: Merge pull request from GHSA-p6g5-v97c-w5q4

* Prevent heap buffer overflow when parsing DNS packets

* Make sure packet parsing doesn't advance beyond max/end

* Update checks

* Remove  check

Co-authored-by: sauwming <ming@teluu.com>
Diff: diff --git a/pjlib-util/src/pjlib-util/dns.c b/pjlib-util/src/pjlib-util/dns.c
index df47c83b9d..1f712eeced 100644
--- a/pjlib-util/src/pjlib-util/dns.c
+++ b/pjlib-util/src/pjlib-util/dns.c
@@ -157,24 +157,26 @@ static pj_status_t get_name_len(int rec_counter, const pj_uint8_t *pkt,
 
 	    return PJ_SUCCESS;
 	} else {
 	    unsigned label_len = *p;
 
-	    /* Check that label length is valid */
-	    if (pkt+label_len > max)
+	    /* Check that label length is valid.
+	     * Each label consists of an octet length (of size 1) followed
+	     * by the octet of the specified length (label_len). Then it
+	     * must be followed by either another label's octet length or
+	     * a zero length octet (that terminates the sequence).
+	     */
+	    if (p+1+label_len+1 > max)
 		return PJLIB_UTIL_EDNSINNAMEPTR;
 
 	    p += (label_len + 1);
 	    *parsed_len += (label_len + 1);
 
 	    if (*p != 0)
 		++label_len;
 	    
 	    *name_len += label_len;
-
-	    if (p >= max)
-		return PJLIB_UTIL_EDNSINSIZE;
 	}
     }
     ++p;
     (*parsed_len)++;
 
@@ -220,25 +222,27 @@ static pj_status_t get_name(int rec_counter, const pj_uint8_t *pkt,
 
 	    return PJ_SUCCESS;
 	} else {
 	    unsigned label_len = *p;
 
-	    /* Check that label length is valid */
-	    if (pkt+label_len > max)
+	    /* Check that label length is valid.
+	     * Each label consists of an octet length (of size 1) followed
+	     * by the octet of the specified length (label_len). Then it
+	     * must be followed by either another label's octet length or
+	     * a zero length octet (that terminates the sequence).
+	     */
+	    if (p+1+label_len+1 > max)
 		return PJLIB_UTIL_EDNSINNAMEPTR;
 
 	    pj_memcpy(name->ptr + name->slen, p+1, label_len);
 	    name->slen += label_len;
 
 	    p += label_len + 1;
 	    if (*p != 0) {
 		*(name->ptr + name->slen) = '.';
 		++name->slen;
 	    }
-
-	    if (p >= max)
-		return PJLIB_UTIL_EDNSINSIZE;
 	}
     }
 
     return PJ_SUCCESS;
 }
@@ -267,10 +271,14 @@ static pj_status_t parse_query(pj_dns_parsed_query *q, pj_pool_t *pool,
     if (status != PJ_SUCCESS)
 	return status;
 
     p = (start + name_part_len);
 
+    /* Check the size can accomodate next few fields. */
+    if (p + 4 > max)
+    	return PJLIB_UTIL_EDNSINSIZE;
+
     /* Get the type */
     pj_memcpy(&q->type, p, 2);
     q->type = pj_ntohs(q->type);
     p += 2;
 

Vulnerable code: 	    /* Check that label length is valid */
	    if (pkt+label_len > max)

	    if (p >= max)
		return PJLIB_UTIL_EDNSINSIZE;
	    /* Check that label length is valid */
	    if (pkt+label_len > max)

	    if (p >= max)
		return PJLIB_UTIL_EDNSINSIZE;
Fixed code: 	    /* Check that label length is valid.
	     * Each label consists of an octet length (of size 1) followed
	     * by the octet of the specified length (label_len). Then it
	     * must be followed by either another label's octet length or
	     * a zero length octet (that terminates the sequence).
	     */
	    if (p+1+label_len+1 > max)
	    /* Check that label length is valid.
	     * Each label consists of an octet length (of size 1) followed
	     * by the octet of the specified length (label_len). Then it
	     * must be followed by either another label's octet length or
	     * a zero length octet (that terminates the sequence).
	     */
	    if (p+1+label_len+1 > max)
    /* Check the size can accomodate next few fields. */
    if (p + 4 > max)
    	return PJLIB_UTIL_EDNSINSIZE;