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 +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 +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 +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 +--- + 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 +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