CVE-2023-27585: 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.13 and prior affects applications that use PJSIP DNS resolver. It doesn't affect PJSIP users who do not utilise PJSIP DNS resolver. This vulnerability is related to CVE-2022-24793. The difference is that this issue is in parsing the query record `parse_query()`, while the issue in CVE-2022-24793 is in `parse_rr()`. A patch is available as commit `d1c5e4d` in the `master` branch. A workaround is to disable DNS resolution in PJSIP config (by setting `nameserver_count` to zero) or use an external resolver implementation 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: 2023-03-14T17:15Z
Repository: https://github.com/pjsip/pjproject
Language: C
Files changed: pjlib-util/src/pjlib-util/dns.c
Commit date: 2023-03-14 09:45:23 +0800
Commit message: Merge pull request from GHSA-q9cp-8wcq-7pfr

* Prevent heap buffer overflow when parsing DNS packet

* Fixed incorrect check in get_name*()
Diff: diff --git a/pjlib-util/src/pjlib-util/dns.c b/pjlib-util/src/pjlib-util/dns.c
index fc96632587..56a54c9973 100644
--- a/pjlib-util/src/pjlib-util/dns.c
+++ b/pjlib-util/src/pjlib-util/dns.c
@@ -125,10 +125,13 @@ static pj_status_t get_name_len(int rec_counter, const pj_uint8_t *pkt,
     if (rec_counter > 10) {
         /* Too many name recursion */
         return PJLIB_UTIL_EDNSINNAMEPTR;
     }
 
+    if (start >= max)
+        return PJLIB_UTIL_EDNSINNAMEPTR;
+
     *name_len = *parsed_len = 0;
     p = start;
     while (*p) {
         if ((*p & 0xc0) == 0xc0) {
             /* Compression is found! */
@@ -197,10 +200,13 @@ static pj_status_t get_name(int rec_counter, const pj_uint8_t *pkt,
     if (rec_counter > 10) {
         /* Too many name recursion */
         return PJLIB_UTIL_EDNSINNAMEPTR;
     }
 
+    if (start >= max)
+        return PJLIB_UTIL_EDNSINNAMEPTR;
+
     p = start;
     while (*p) {
         if ((*p & 0xc0) == 0xc0) {
             /* Compression is found! */
             pj_uint16_t offset;
@@ -357,14 +363,18 @@ static pj_status_t parse_rr(pj_dns_parsed_rr *rr, pj_pool_t *pool,
     if (p + rr->rdlength > max)
         return PJLIB_UTIL_EDNSINSIZE;
 
     /* Parse some well known records */
     if (rr->type == PJ_DNS_TYPE_A) {
+        if (p + 4 > max)
+            return PJLIB_UTIL_EDNSINSIZE;
         pj_memcpy(&rr->rdata.a.ip_addr, p, 4);
         p += 4;
 
     } else if (rr->type == PJ_DNS_TYPE_AAAA) {
+        if (p + 16 > max)
+            return PJLIB_UTIL_EDNSINSIZE;
         pj_memcpy(&rr->rdata.aaaa.ip_addr, p, 16);
         p += 16;
 
     } else if (rr->type == PJ_DNS_TYPE_CNAME ||
                rr->type == PJ_DNS_TYPE_NS ||
@@ -386,10 +396,12 @@ static pj_status_t parse_rr(pj_dns_parsed_rr *rr, pj_pool_t *pool,
             return status;
 
         p += name_part_len;
 
     } else if (rr->type == PJ_DNS_TYPE_SRV) {
+        if (p + 6 > max)
+            return PJLIB_UTIL_EDNSINSIZE;
 
         /* Priority */
         pj_memcpy(&rr->rdata.srv.prio, p, 2);
         rr->rdata.srv.prio = pj_ntohs(rr->rdata.srv.prio);
         p += 2;

Fixed code:     if (start >= max)
        return PJLIB_UTIL_EDNSINNAMEPTR;

    if (start >= max)
        return PJLIB_UTIL_EDNSINNAMEPTR;

        if (p + 4 > max)
            return PJLIB_UTIL_EDNSINSIZE;
        if (p + 16 > max)
            return PJLIB_UTIL_EDNSINSIZE;
        if (p + 6 > max)
            return PJLIB_UTIL_EDNSINSIZE;