Commit 500525c289 for asterisk.org

commit 500525c289aab2d67ce0034d52b0d3f7d781e06d
Author: Mike Bradeen <mbradeen@sangoma.com>
Date:   Mon Mar 23 14:45:30 2026 -0600

    res_pjsip: Address pjproject security vulnerabilities

    Address the following pjproject security vulnerabilities

    [GHSA-j29p-pvh2-pvqp - Buffer overflow in ICE with long username](https://github.com/pjsip/pjproject/security/advisories/GHSA-j29p-pvh2-pvqp)
    [GHSA-8fj4-fv9f-hjpc - Heap use-after-free in PJSIP presense subscription termination header](https://github.com/pjsip/pjproject/security/advisories/GHSA-8fj4-fv9f-hjpc)
    [GHSA-g88q-c2hm-q7p7 - ICE session use-after-free race conditions](https://github.com/pjsip/pjproject/security/advisories/GHSA-g88q-c2hm-q7p7)
    [GHSA-x5pq-qrp4-fmrj - Out-of-bounds read in SIP multipart parsing](https://github.com/pjsip/pjproject/security/advisories/GHSA-x5pq-qrp4-fmrj)

    Resolves: #1833

diff --git a/third-party/pjproject/patches/0020-buf-overflow-ice-long-username.patch b/third-party/pjproject/patches/0020-buf-overflow-ice-long-username.patch
new file mode 100644
index 0000000000..ae38a30899
--- /dev/null
+++ b/third-party/pjproject/patches/0020-buf-overflow-ice-long-username.patch
@@ -0,0 +1,58 @@
+From 063b3a155f163cc5a9a1df2c56b6720fd3a0dbb0 Mon Sep 17 00:00:00 2001
+From: Nanang Izzuddin <nanang@teluu.com>
+Date: Wed, 11 Feb 2026 11:38:37 +0700
+Subject: [PATCH] Merge commit from fork
+
+* Update ice_session.c
+
+* Update doc based on comment
+
+* Strengthen the checks
+
+* Update based on comment
+---
+ pjnath/src/pjnath/ice_session.c | 24 +++++++++++++++++++++++-
+ 1 file changed, 23 insertions(+), 1 deletion(-)
+
+diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
+index f1dc70d6b7..9d733d6fb3 100644
+--- a/pjnath/src/pjnath/ice_session.c
++++ b/pjnath/src/pjnath/ice_session.c
+@@ -2102,7 +2102,8 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
+                               const pj_ice_sess_cand rem_cand[])
+ {
+     pj_ice_sess_checklist *clist;
+-    char buf[128];
++    enum { MAX_USERNAME_LEN = 512 };
++    char buf[MAX_USERNAME_LEN];
+     pj_str_t username;
+     timer_data *td;
+     pj_status_t status;
+@@ -2117,6 +2118,27 @@ PJ_DEF(pj_status_t) pj_ice_sess_create_check_list(
+         return PJ_SUCCESS;
+     }
+
++    /* Verify credentials lengths:
++     * - The ufrag must be at least 4 bytes, passwd at least 22 bytes.
++     * - Combined usernames and +1 for colon must not exceed MAX_USERNAME_LEN.
++     */
++    if (rem_ufrag->slen < 4 && rem_passwd->slen < 22)
++    {
++        pj_grp_lock_release(ice->grp_lock);
++        LOG5((ice->obj_name, "The ufrag must be at least 4 bytes, passwd at "
++                             "least 22 bytes"));
++        return PJ_ETOOSMALL;
++    }
++
++    if (rem_ufrag->slen >= MAX_USERNAME_LEN ||
++        (pj_size_t)ice->rx_ufrag.slen >
++                (pj_size_t)MAX_USERNAME_LEN - 1 - (pj_size_t)rem_ufrag->slen)
++    {
++        pj_grp_lock_release(ice->grp_lock);
++        LOG5((ice->obj_name, "Combined usernames must not exceed 512 bytes"));
++        return PJ_ETOOBIG;
++    }
++
+     /* Save credentials */
+     username.ptr = buf;
+
diff --git a/third-party/pjproject/patches/0030-ice-sess-use-after-free.patch b/third-party/pjproject/patches/0030-ice-sess-use-after-free.patch
new file mode 100644
index 0000000000..2c2662b87b
--- /dev/null
+++ b/third-party/pjproject/patches/0030-ice-sess-use-after-free.patch
@@ -0,0 +1,63 @@
+From c9caceddabda7f18337b2a82d25d65f6224b450a Mon Sep 17 00:00:00 2001
+From: sauwming <ming@teluu.com>
+Date: Wed, 11 Mar 2026 07:18:49 +0800
+Subject: [PATCH] Merge commit from fork
+
+---
+ pjnath/src/pjnath/ice_session.c | 14 +++++++++++++-
+ 1 file changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c
+index 39129761b0..e1513b94f9 100644
+--- a/pjnath/src/pjnath/ice_session.c
++++ b/pjnath/src/pjnath/ice_session.c
+@@ -3667,7 +3667,10 @@ PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice,
+     transport_id = cand->transport_id;
+     pj_sockaddr_cp(&addr, &comp->valid_check->rcand->addr);
+
+-    /* Release the mutex now to avoid deadlock (see ticket #1451). */
++    /* Release the mutex now to avoid deadlock (see ticket #1451),
++     * but add ref first to avoid premature destruction in the cb.
++     */
++    pj_grp_lock_add_ref(ice->grp_lock);
+     pj_grp_lock_release(ice->grp_lock);
+
+     PJ_RACE_ME(5);
+@@ -3677,6 +3680,8 @@ PJ_DEF(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice,
+                                   &addr,
+                                   pj_sockaddr_get_len(&addr));
+
++    pj_grp_lock_dec_ref(ice->grp_lock);
++
+ on_return:
+     return status;
+ }
+@@ -3743,7 +3748,9 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
+     } else {
+         /* Not a STUN packet. Call application's callback instead, but release
+          * the mutex now or otherwise we may get deadlock.
++         * Add ref first to avoid race with session destruction.
+          */
++        pj_grp_lock_add_ref(ice->grp_lock);
+         pj_grp_lock_release(ice->grp_lock);
+
+         PJ_RACE_ME(5);
+@@ -3800,6 +3807,9 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
+                          "component [%d] because source addr %s unrecognized "
+                          "or unchecked",
+                          comp_id, paddr));
++
++                pj_grp_lock_dec_ref(ice->grp_lock);
++
+                 return PJ_SUCCESS;
+             }
+         }
+@@ -3807,6 +3817,8 @@ PJ_DEF(pj_status_t) pj_ice_sess_on_rx_pkt(pj_ice_sess *ice,
+         (*ice->cb.on_rx_data)(ice, comp_id, transport_id, pkt, pkt_size,
+                               src_addr, src_addr_len);
+         status = PJ_SUCCESS;
++
++        pj_grp_lock_dec_ref(ice->grp_lock);
+     }
+
+     return status;
diff --git a/third-party/pjproject/patches/0040-presence-sub-use-after-free.patch b/third-party/pjproject/patches/0040-presence-sub-use-after-free.patch
new file mode 100644
index 0000000000..91f7760288
--- /dev/null
+++ b/third-party/pjproject/patches/0040-presence-sub-use-after-free.patch
@@ -0,0 +1,77 @@
+From e06ff6c64741cc1675fd3296615910f532f6b1a1 Mon Sep 17 00:00:00 2001
+From: Nanang Izzuddin <nanang@teluu.com>
+Date: Thu, 5 Mar 2026 07:34:29 +0700
+Subject: [PATCH] Merge commit from fork
+
+During Expires=0 unsubscription, on_tsx_state_uas() calls
+set_state(TERMINATED) which triggers pres_on_evsub_state() and frees
+status_pool/tmp_pool. Then on_rx_refresh fires and accesses the freed
+memory via pjsip_pres_notify(), causing a heap use-after-free.
+
+Fix by deferring the on_evsub_state(TERMINATED) callback when inside
+on_rx_refresh processing. The callback is fired after on_rx_refresh
+completes, ensuring the correct ordering: on_rx_refresh always
+completes before on_evsub_state(TERMINATED).
+
+This is a centralized fix in evsub.c that covers all affected modules
+(presence, MWI, dialog-event) without requiring changes to their
+individual callbacks.
+
+Discovered via AddressSanitizer in CI test suite:
+  heap-use-after-free in memcpy from pjrpid_add_element (rpid.c:128)
+
+Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
+---
+ pjsip/src/pjsip-simple/evsub.c | 25 +++++++++++++++++++++++--
+ 1 file changed, 23 insertions(+), 2 deletions(-)
+
+diff --git a/pjsip/src/pjsip-simple/evsub.c b/pjsip/src/pjsip-simple/evsub.c
+index 9ed5793613..d7718af9f2 100644
+--- a/pjsip/src/pjsip-simple/evsub.c
++++ b/pjsip/src/pjsip-simple/evsub.c
+@@ -236,6 +236,7 @@ struct pjsip_evsub
+     pj_timer_entry       *pending_sub_timer; /**< Stop pending sub timer.   */
+     pjsip_tx_data        *pending_notify;/**< Pending NOTIFY to be sent.    */
+     pj_bool_t             calling_on_rx_refresh;/**< Inside on_rx_refresh()?*/
++    pj_bool_t             deferred_state_notify;/**< Deferred TERMINATED notify */
+     pj_grp_lock_t        *grp_lock;     /* Session group lock       */
+
+     void                 *mod_data[PJSIP_MAX_MODULE];   /**< Module data.   */
+@@ -626,8 +627,15 @@ static void set_state( pjsip_evsub *sub, pjsip_evsub_state state,
+         event = &dummy_event;
+     }
+
+-    if (sub->user.on_evsub_state && sub->call_cb)
+-        (*sub->user.on_evsub_state)(sub, event);
++    if (sub->user.on_evsub_state && sub->call_cb) {
++        if (state == PJSIP_EVSUB_STATE_TERMINATED &&
++            sub->calling_on_rx_refresh)
++        {
++            sub->deferred_state_notify = PJ_TRUE;
++        } else {
++            (*sub->user.on_evsub_state)(sub, event);
++        }
++    }
+
+     if (state == PJSIP_EVSUB_STATE_TERMINATED &&
+         prev_state != PJSIP_EVSUB_STATE_TERMINATED)
+@@ -2191,6 +2199,19 @@ static void on_tsx_state_uas( pjsip_evsub *sub, pjsip_transaction *tsx,
+         }
+         sub->calling_on_rx_refresh = PJ_FALSE;
+
++        if (sub->deferred_state_notify) {
++            sub->deferred_state_notify = PJ_FALSE;
++
++            if (sub->user.on_evsub_state && sub->call_cb)
++                (*sub->user.on_evsub_state)(sub, event);
++
++            if (sub->state == PJSIP_EVSUB_STATE_TERMINATED &&
++                sub->pending_tsx == 0)
++            {
++                evsub_destroy(sub);
++            }
++        }
++
+         /* Application MUST specify final response! */
+         PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
+
diff --git a/third-party/pjproject/patches/0050-oob-read-sip-multipart.patch b/third-party/pjproject/patches/0050-oob-read-sip-multipart.patch
new file mode 100644
index 0000000000..a729bb8d36
--- /dev/null
+++ b/third-party/pjproject/patches/0050-oob-read-sip-multipart.patch
@@ -0,0 +1,39 @@
+From f0fa32a226df5f87a9903093e5d145ebb69734db Mon Sep 17 00:00:00 2001
+From: sauwming <ming@teluu.com>
+Date: Tue, 17 Mar 2026 13:07:47 +0800
+Subject: [PATCH] Merge commit from fork
+
+* Fixed OOB read in SIP multipart parsing
+
+* Fixed too strict CRLF expectation
+---
+ pjsip/src/pjsip/sip_multipart.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/pjsip/src/pjsip/sip_multipart.c b/pjsip/src/pjsip/sip_multipart.c
+index 0db8d118d2..b4cf378f2a 100644
+--- a/pjsip/src/pjsip/sip_multipart.c
++++ b/pjsip/src/pjsip/sip_multipart.c
+@@ -835,7 +835,7 @@ PJ_DEF(pjsip_msg_body*) pjsip_multipart_parse(pj_pool_t *pool,
+
+         /* Eat the boundary */
+         curptr += delim.slen;
+-        if (*curptr=='-' && curptr<endptr-1 && *(curptr+1)=='-') {
++        if (curptr+1 < endptr && *curptr=='-' && *(curptr+1)=='-') {
+             /* Found the closing delimiter */
+             curptr += 2;
+             break;
+@@ -843,8 +843,12 @@ PJ_DEF(pjsip_msg_body*) pjsip_multipart_parse(pj_pool_t *pool,
+         /* Optional whitespace after delimiter */
+         while (curptr!=endptr && IS_SPACE(*curptr)) ++curptr;
+         /* Mandatory CRLF */
++        if (curptr == endptr) {
++            PJ_LOG(2, (THIS_FILE, "Unexpected end of buffer after boundary"));
++            return NULL;
++        }
+         if (*curptr=='\r') ++curptr;
+-        if (*curptr!='\n') {
++        if (curptr == endptr || *curptr!='\n') {
+             /* Expecting a newline here */
+             PJ_LOG(2, (THIS_FILE, "Failed to find newline"));
+