CVE-2022-23537: Heap-based Buffer Overflow (CWE-122)
value: PJSIP is a free and open source multimedia communication library written in C language implementing standard based protocols such as SIP, SDP, RTP, STUN, TURN, and ICE. Buffer overread is possible when parsing a specially crafted STUN message with unknown attribute. The vulnerability affects applications that uses STUN including PJNATH and PJSUA-LIB. The patch is available as a commit in the master branch (2.13.1).
CWE description: A heap overflow condition is a buffer overflow, where the buffer that can be overwritten is allocated in the heap portion of memory, generally meaning that the buffer was allocated using a routine such as malloc().
CVSS v3 base score: 9.8
Published: 2022-12-20T19:15Z
Repository: https://github.com/pjsip/pjproject
Language: C
Files changed: pjnath/include/pjnath/stun_msg.h, pjnath/src/pjnath/stun_msg.c
Commit date: 2022-12-20 12:39:12 +0800
Commit message: Merge pull request from GHSA-9pfh-r8x4-w26w

* Fix buffer overread in STUN message decoder

* Updates based on comments
Diff: diff --git a/pjnath/include/pjnath/stun_msg.h b/pjnath/include/pjnath/stun_msg.h
index b52f95c586..e49f096f3a 100644
--- a/pjnath/include/pjnath/stun_msg.h
+++ b/pjnath/include/pjnath/stun_msg.h
@@ -440,10 +440,11 @@ typedef enum pj_stun_status
                                                                        |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
    \endverbatim
  */
+#pragma pack(1)
 typedef struct pj_stun_msg_hdr
 {
     /**
      * STUN message type, which the first two bits must be zeroes.
      */
@@ -471,10 +472,11 @@ typedef struct pj_stun_msg_hdr
      * distributed between 0 and 2**96 - 1. 
      */
     pj_uint8_t          tsx_id[12];
 
 } pj_stun_msg_hdr;
+#pragma pack()
 
 
 /**
  * This structre describes STUN attribute header. Each attribute is
  * TLV encoded, with a 16 bit type, 16 bit length, and variable value.
@@ -488,10 +490,11 @@ typedef struct pj_stun_msg_hdr
        |         Type                  |            Length             |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 
    \endverbatim
  */
+#pragma pack(1)
 typedef struct pj_stun_attr_hdr
 {
     /**
      * STUN attribute type.
      */
@@ -504,10 +507,11 @@ typedef struct pj_stun_attr_hdr
      * attribute prior to padding - i.e., the useful content.
      */
     pj_uint16_t         length;
 
 } pj_stun_attr_hdr;
+#pragma pack()
 
 
 /**
  * This structure describes STUN generic IP address attribute, used for
  * example to represent STUN MAPPED-ADDRESS attribute.
diff --git a/pjnath/src/pjnath/stun_msg.c b/pjnath/src/pjnath/stun_msg.c
index 3def6b3eac..e904a0ba47 100644
--- a/pjnath/src/pjnath/stun_msg.c
+++ b/pjnath/src/pjnath/stun_msg.c
@@ -744,11 +744,11 @@ PJ_DEF(int) pj_stun_set_padding_char(int chr)
 //////////////////////////////////////////////////////////////////////////////
 
 
 #define INIT_ATTR(a,t,l)    (a)->hdr.type=(pj_uint16_t)(t), \
                             (a)->hdr.length=(pj_uint16_t)(l)
-#define ATTR_HDR_LEN        4
+#define ATTR_HDR_LEN        sizeof(pj_stun_attr_hdr)
 
 static pj_uint16_t GETVAL16H(const pj_uint8_t *buf, unsigned pos)
 {
     return (pj_uint16_t) ((buf[pos + 0] << 8) | \
                           (buf[pos + 1] << 0));
@@ -2325,10 +2325,18 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool,
     /* Check if this is a STUN message, if necessary */
     if (options & PJ_STUN_CHECK_PACKET) {
         status = pj_stun_msg_check(pdu, pdu_len, options);
         if (status != PJ_SUCCESS)
             return status;
+    } else {
+        /* For safety, verify packet length at least */
+        pj_uint32_t msg_len = GETVAL16H(pdu, 2) + 20;
+        if (msg_len > pdu_len ||
+            ((options & PJ_STUN_IS_DATAGRAM) && msg_len != pdu_len))
+        {
+            return PJNATH_EINSTUNMSGLEN;
+        }
     }
 
     /* Create the message, copy the header, and convert to host byte order */
     msg = PJ_POOL_ZALLOC_T(pool, pj_stun_msg);
     pj_memcpy(&msg->hdr, pdu, sizeof(pj_stun_msg_hdr));
@@ -2343,11 +2351,11 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool,
     /* No need to create response if this is not a request */
     if (!PJ_STUN_IS_REQUEST(msg->hdr.type))
         p_response = NULL;
 
     /* Parse attributes */
-    while (pdu_len >= 4) {
+    while (pdu_len >= ATTR_HDR_LEN) {
         unsigned attr_type, attr_val_len;
         const struct attr_desc *adesc;
 
         /* Get attribute type and length. If length is not aligned
          * to 4 bytes boundary, add padding.
@@ -2355,11 +2363,11 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool,
         attr_type = GETVAL16H(pdu, 0);
         attr_val_len = GETVAL16H(pdu, 2);
         attr_val_len = (attr_val_len + 3) & (~3);
 
         /* Check length */
-        if (pdu_len < attr_val_len) {
+        if (pdu_len < attr_val_len + ATTR_HDR_LEN) {
             pj_str_t err_msg;
             char err_msg_buf[80];
 
             err_msg.ptr = err_msg_buf;
             err_msg.slen = pj_ansi_snprintf(err_msg_buf, sizeof(err_msg_buf),

Vulnerable code: #define ATTR_HDR_LEN        4
    while (pdu_len >= 4) {
        if (pdu_len < attr_val_len) {
Fixed code: #pragma pack(1)
#pragma pack()
#pragma pack(1)
#pragma pack()
#define ATTR_HDR_LEN        sizeof(pj_stun_attr_hdr)
    } else {
        /* For safety, verify packet length at least */
        pj_uint32_t msg_len = GETVAL16H(pdu, 2) + 20;
        if (msg_len > pdu_len ||
            ((options & PJ_STUN_IS_DATAGRAM) && msg_len != pdu_len))
        {
            return PJNATH_EINSTUNMSGLEN;
        }
    while (pdu_len >= ATTR_HDR_LEN) {
        if (pdu_len < attr_val_len + ATTR_HDR_LEN) {