Vulnerability: 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 uses PJSIP DNS resolution. It doesn't affect PJSIP users who utilize an external resolver. 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.
Vulnerability ID: CVE-2022-24793
CWE: CWE-120
Severity score: 2.9
Project: https://github.com/pjsip/pjproject
Published: 2022-04-06
File: pjlib-util/src/pjlib-util/dns.c
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>
Commit author: Riza Sulistyo

Vulnerable version (before fix):
 static pj_status_t get_name_len(int rec_counter, const pj_uint8_t *pkt, 	} else { 	    unsigned label_len = *p;  	    /* Check that label length is valid */ 	    if (pkt+label_len > max) 		return PJLIB_UTIL_EDNSINNAMEPTR;  	    p += (label_len + 1); static pj_status_t get_name_len(int rec_counter, const pj_uint8_t *pkt, 		++label_len; 	     	    *name_len += label_len;  	    if (p >= max) 		return PJLIB_UTIL_EDNSINSIZE; 	}     }     ++p; static pj_status_t get_name(int rec_counter, const pj_uint8_t *pkt, 	} else { 	    unsigned label_len = *p;  	    /* Check that label length is valid */ 	    if (pkt+label_len > max) 		return PJLIB_UTIL_EDNSINNAMEPTR;  	    pj_memcpy(name->ptr + name->slen, p+1, label_len); static pj_status_t get_name(int rec_counter, const pj_uint8_t *pkt, 		*(name->ptr + name->slen) = '.'; 		++name->slen; 	    }  	    if (p >= max) 		return PJLIB_UTIL_EDNSINSIZE; 	}     }  static pj_status_t parse_query(pj_dns_parsed_query *q, pj_pool_t *pool,      p = (start + name_part_len);      /* Get the type */     pj_memcpy(&q->type, p, 2);     q->type = pj_ntohs(q->type);

Fixed version (after fix):
 static pj_status_t get_name_len(int rec_counter, const pj_uint8_t *pkt, 	} else { 	    unsigned label_len = *p;  	    /* 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); static pj_status_t get_name_len(int rec_counter, const pj_uint8_t *pkt, 		++label_len; 	     	    *name_len += label_len; 	}     }     ++p; static pj_status_t get_name(int rec_counter, const pj_uint8_t *pkt, 	} else { 	    unsigned label_len = *p;  	    /* 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); static pj_status_t get_name(int rec_counter, const pj_uint8_t *pkt, 		*(name->ptr + name->slen) = '.'; 		++name->slen; 	    } 	}     }  static pj_status_t parse_query(pj_dns_parsed_query *q, pj_pool_t *pool,      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);

Patch:
patch: @@ -159,8 +159,13 @@ static pj_status_t get_name_len(int rec_counter, const pj_uint8_t *pkt,
 	} 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);
@@ -170,9 +175,6 @@ static pj_status_t get_name_len(int rec_counter, const pj_uint8_t *pkt,
 		++label_len;
 	    
 	    *name_len += label_len;
-
-	    if (p >= max)
-		return PJLIB_UTIL_EDNSINSIZE;
 	}
     }
     ++p;
@@ -222,8 +224,13 @@ static pj_status_t get_name(int rec_counter, const pj_uint8_t *pkt,
 	} 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);
@@ -234,9 +241,6 @@ static pj_status_t get_name(int rec_counter, const pj_uint8_t *pkt,
 		*(name->ptr + name->slen) = '.';
 		++name->slen;
 	    }
-
-	    if (p >= max)
-		return PJLIB_UTIL_EDNSINSIZE;
 	}
     }
 
@@ -269,6 +273,10 @@ static pj_status_t parse_query(pj_dns_parsed_query *q, pj_pool_t *pool,
 
     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);
path: pjlib-util/src/pjlib-util/dns.c