diff --git a/configure b/configure index 3e5140ddb63766a0375dc52f7adc6ab176f5eef1..886ef6f9fa3fa4a8e876d629a272e9002329e018 100755 --- a/configure +++ b/configure @@ -935,6 +935,10 @@ PBX_POPT POPT_DIR POPT_INCLUDE POPT_LIB +PBX_PJSIP_INV_SESSION_REF +PJSIP_INV_SESSION_REF_DIR +PJSIP_INV_SESSION_REF_INCLUDE +PJSIP_INV_SESSION_REF_LIB PBX_PJSIP_EVSUB_GRP_LOCK PJSIP_EVSUB_GRP_LOCK_DIR PJSIP_EVSUB_GRP_LOCK_INCLUDE @@ -11001,6 +11005,18 @@ PBX_PJSIP_EVSUB_GRP_LOCK=0 +PJSIP_INV_SESSION_REF_DESCRIP="PJSIP INVITE Session Reference Count support" +PJSIP_INV_SESSION_REF_OPTION=pjsip +PJSIP_INV_SESSION_REF_DIR=${PJPROJECT_DIR} + +PBX_PJSIP_INV_SESSION_REF=0 + + + + + + + POPT_DESCRIP="popt" POPT_OPTION="popt" @@ -25269,6 +25285,9 @@ $as_echo "#define HAVE_PJSIP_TLS_TRANSPORT_PROTO 1" >>confdefs.h $as_echo "#define HAVE_PJSIP_EVSUB_GRP_LOCK 1" >>confdefs.h +$as_echo "#define HAVE_PJSIP_INV_SESSION_REF 1" >>confdefs.h + + else if test "x${PBX_PJPROJECT}" != "x1" -a "${USE_PJPROJECT}" != "no"; then @@ -26089,6 +26108,110 @@ _ACEOF fi + +if test "x${PBX_PJSIP_INV_SESSION_REF}" != "x1" -a "${USE_PJSIP_INV_SESSION_REF}" != "no"; then + pbxlibdir="" + # if --with-PJSIP_INV_SESSION_REF=DIR has been specified, use it. + if test "x${PJSIP_INV_SESSION_REF_DIR}" != "x"; then + if test -d ${PJSIP_INV_SESSION_REF_DIR}/lib; then + pbxlibdir="-L${PJSIP_INV_SESSION_REF_DIR}/lib" + else + pbxlibdir="-L${PJSIP_INV_SESSION_REF_DIR}" + fi + fi + pbxfuncname="pjsip_inv_add_ref" + if test "x${pbxfuncname}" = "x" ; then # empty lib, assume only headers + AST_PJSIP_INV_SESSION_REF_FOUND=yes + else + ast_ext_lib_check_save_CFLAGS="${CFLAGS}" + CFLAGS="${CFLAGS} $PJPROJECT_CFLAGS" + as_ac_Lib=`$as_echo "ac_cv_lib_pjsip_${pbxfuncname}" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${pbxfuncname} in -lpjsip" >&5 +$as_echo_n "checking for ${pbxfuncname} in -lpjsip... " >&6; } +if eval \${$as_ac_Lib+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpjsip ${pbxlibdir} $PJPROJECT_LIB $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char ${pbxfuncname} (); +int +main () +{ +return ${pbxfuncname} (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$as_ac_Lib=yes" +else + eval "$as_ac_Lib=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +eval ac_res=\$$as_ac_Lib + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then : + AST_PJSIP_INV_SESSION_REF_FOUND=yes +else + AST_PJSIP_INV_SESSION_REF_FOUND=no +fi + + CFLAGS="${ast_ext_lib_check_save_CFLAGS}" + fi + + # now check for the header. + if test "${AST_PJSIP_INV_SESSION_REF_FOUND}" = "yes"; then + PJSIP_INV_SESSION_REF_LIB="${pbxlibdir} -lpjsip $PJPROJECT_LIB" + # if --with-PJSIP_INV_SESSION_REF=DIR has been specified, use it. + if test "x${PJSIP_INV_SESSION_REF_DIR}" != "x"; then + PJSIP_INV_SESSION_REF_INCLUDE="-I${PJSIP_INV_SESSION_REF_DIR}/include" + fi + PJSIP_INV_SESSION_REF_INCLUDE="${PJSIP_INV_SESSION_REF_INCLUDE} $PJPROJECT_CFLAGS" + if test "xpjsip.h" = "x" ; then # no header, assume found + PJSIP_INV_SESSION_REF_HEADER_FOUND="1" + else # check for the header + ast_ext_lib_check_saved_CPPFLAGS="${CPPFLAGS}" + CPPFLAGS="${CPPFLAGS} ${PJSIP_INV_SESSION_REF_INCLUDE}" + ac_fn_c_check_header_mongrel "$LINENO" "pjsip.h" "ac_cv_header_pjsip_h" "$ac_includes_default" +if test "x$ac_cv_header_pjsip_h" = xyes; then : + PJSIP_INV_SESSION_REF_HEADER_FOUND=1 +else + PJSIP_INV_SESSION_REF_HEADER_FOUND=0 +fi + + + CPPFLAGS="${ast_ext_lib_check_saved_CPPFLAGS}" + fi + if test "x${PJSIP_INV_SESSION_REF_HEADER_FOUND}" = "x0" ; then + PJSIP_INV_SESSION_REF_LIB="" + PJSIP_INV_SESSION_REF_INCLUDE="" + else + if test "x${pbxfuncname}" = "x" ; then # only checking headers -> no library + PJSIP_INV_SESSION_REF_LIB="" + fi + PBX_PJSIP_INV_SESSION_REF=1 + cat >>confdefs.h <<_ACEOF +#define HAVE_PJSIP_INV_SESSION_REF 1 +_ACEOF + + fi + fi +fi + + fi fi diff --git a/configure.ac b/configure.ac index 950cfc5a13776692d7f55cc891b1c4cf94f99259..7fd702fee5786ca480542de0018f379bb8c35c88 100644 --- a/configure.ac +++ b/configure.ac @@ -487,6 +487,7 @@ AST_EXT_LIB_SETUP_OPTIONAL([PJ_SSL_CERT_LOAD_FROM_FILES2], [pj_ssl_cert_load_fro AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_EXTERNAL_RESOLVER], [PJSIP External Resolver Support], [PJPROJECT], [pjsip]) AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_TLS_TRANSPORT_PROTO], [PJSIP TLS Transport proto field support], [PJPROJECT], [pjsip]) AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_EVSUB_GRP_LOCK], [PJSIP EVSUB Group Lock support], [PJPROJECT], [pjsip]) +AST_EXT_LIB_SETUP_OPTIONAL([PJSIP_INV_SESSION_REF], [PJSIP INVITE Session Reference Count support], [PJPROJECT], [pjsip]) AST_EXT_LIB_SETUP([POPT], [popt], [popt]) AST_EXT_LIB_SETUP([PORTAUDIO], [PortAudio], [portaudio]) @@ -2210,6 +2211,7 @@ if test "$USE_PJPROJECT" != "no" ; then CPPFLAGS="${saved_cppflags}" AST_EXT_LIB_CHECK([PJSIP_EVSUB_GRP_LOCK], [pjsip], [pjsip_evsub_add_ref], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS]) + AST_EXT_LIB_CHECK([PJSIP_INV_SESSION_REF], [pjsip], [pjsip_inv_add_ref], [pjsip.h], [$PJPROJECT_LIB], [$PJPROJECT_CFLAGS]) fi fi diff --git a/include/asterisk/autoconfig.h.in b/include/asterisk/autoconfig.h.in index f9a6b46e47ca1f856df605a9f76521450aacca5c..5e7ea7e2ae3b20b10799b6e120295324c9981d8d 100644 --- a/include/asterisk/autoconfig.h.in +++ b/include/asterisk/autoconfig.h.in @@ -598,6 +598,9 @@ /* Define if your system has pjsip_get_dest_info declared. */ #undef HAVE_PJSIP_GET_DEST_INFO +/* Define if your system has PJSIP_INV_SESSION_REF */ +#undef HAVE_PJSIP_INV_SESSION_REF + /* Define if your system has the PJSIP_REPLACE_MEDIA_STREAM headers. */ #undef HAVE_PJSIP_REPLACE_MEDIA_STREAM diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index 315393fdb895e6005eddedfd1c99cce72c2da0e2..f54ee941199718d7fd56926a2e6f7e72cc40f0c1 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -213,6 +213,11 @@ static int handle_incoming_sdp(struct ast_sip_session *session, const pjmedia_sd int i; int handled = 0; + if (session->inv_session && session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) { + ast_log(LOG_ERROR, "Failed to handle incoming SDP. Session has been already disconnected\n"); + return -1; + } + for (i = 0; i < sdp->media_count; ++i) { /* See if there are registered handlers for this media stream type */ char media[20]; @@ -2087,6 +2092,16 @@ static int new_invite(void *data) * so that we will be notified so we can destroy the session properly */ + if (invite->session->inv_session->state == PJSIP_INV_STATE_DISCONNECTED) { + ast_log(LOG_ERROR, "Session already DISCONNECTED [reason=%d (%s)]\n", + invite->session->inv_session->cause, + pjsip_get_status_text(invite->session->inv_session->cause)->ptr); +#ifdef HAVE_PJSIP_INV_SESSION_REF + pjsip_inv_dec_ref(invite->session->inv_session); +#endif + return -1; + } + switch (get_destination(invite->session, invite->rdata)) { case SIP_GET_DEST_EXTEN_FOUND: /* Things worked. Keep going */ @@ -2097,7 +2112,7 @@ static int new_invite(void *data) } else { pjsip_inv_terminate(invite->session->inv_session, 416, PJ_TRUE); } - return 0; + goto end; case SIP_GET_DEST_EXTEN_NOT_FOUND: case SIP_GET_DEST_EXTEN_PARTIAL: default: @@ -2110,7 +2125,7 @@ static int new_invite(void *data) } else { pjsip_inv_terminate(invite->session->inv_session, 404, PJ_TRUE); } - return 0; + goto end; }; if ((sdp_info = pjsip_rdata_get_sdp_info(invite->rdata)) && (sdp_info->sdp_err == PJ_SUCCESS) && sdp_info->sdp) { @@ -2120,7 +2135,7 @@ static int new_invite(void *data) } else { pjsip_inv_terminate(invite->session->inv_session, 488, PJ_TRUE); } - return 0; + goto end; } /* We are creating a local SDP which is an answer to their offer */ local = create_local_sdp(invite->session->inv_session, invite->session, sdp_info->sdp); @@ -2136,7 +2151,7 @@ static int new_invite(void *data) } else { pjsip_inv_terminate(invite->session->inv_session, 500, PJ_TRUE); } - return 0; + goto end; } else { pjsip_inv_set_local_sdp(invite->session->inv_session, local); pjmedia_sdp_neg_set_prefer_remote_codec_order(invite->session->inv_session->neg, PJ_FALSE); @@ -2153,12 +2168,16 @@ static int new_invite(void *data) /* At this point, we've verified what we can, so let's go ahead and send a 100 Trying out */ if (pjsip_inv_initial_answer(invite->session->inv_session, invite->rdata, 100, NULL, NULL, &tdata) != PJ_SUCCESS) { pjsip_inv_terminate(invite->session->inv_session, 500, PJ_TRUE); - return 0; + goto end; } ast_sip_session_send_response(invite->session, tdata); handle_incoming_request(invite->session, invite->rdata); +end: +#ifdef HAVE_PJSIP_INV_SESSION_REF + pjsip_inv_dec_ref(invite->session->inv_session); +#endif return 0; } @@ -2179,6 +2198,20 @@ static void handle_new_invite_request(pjsip_rx_data *rdata) return; } +#ifdef HAVE_PJSIP_INV_SESSION_REF + if (pjsip_inv_add_ref(inv_session) != PJ_SUCCESS) { + ast_log(LOG_ERROR, "Can't increase the session reference counter\n"); + if (inv_session->state != PJSIP_INV_STATE_DISCONNECTED) { + if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) { + pjsip_inv_terminate(inv_session, 500, PJ_FALSE); + } else { + internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata); + } + } + return; + } +#endif + session = ast_sip_session_alloc(endpoint, NULL, inv_session, rdata); if (!session) { if (pjsip_inv_initial_answer(inv_session, rdata, 500, NULL, NULL, &tdata) == PJ_SUCCESS) { @@ -2186,6 +2219,9 @@ static void handle_new_invite_request(pjsip_rx_data *rdata) } else { internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata); } +#ifdef HAVE_PJSIP_INV_SESSION_REF + pjsip_inv_dec_ref(inv_session); +#endif return; } @@ -2196,6 +2232,9 @@ static void handle_new_invite_request(pjsip_rx_data *rdata) } else { internal_pjsip_inv_send_msg(inv_session, endpoint->transport, tdata); } +#ifdef HAVE_PJSIP_INV_SESSION_REF + pjsip_inv_dec_ref(inv_session); +#endif ao2_cleanup(invite); } ao2_ref(session, -1); @@ -2467,8 +2506,9 @@ static void handle_outgoing(struct ast_sip_session *session, pjsip_tx_data *tdat } } -static void session_end(struct ast_sip_session *session) +static int session_end(void *vsession) { + struct ast_sip_session *session = vsession; struct ast_sip_session_supplement *iter; /* Stop the scheduled termination */ @@ -2480,6 +2520,7 @@ static void session_end(struct ast_sip_session *session) iter->session_end(session); } } + return 0; } /*! @@ -2617,7 +2658,10 @@ static void session_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) } if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { - session_end(session); + if (ast_sip_push_task(session->serializer, session_end, session)) { + /* Do it anyway even though this is not the right thread. */ + session_end(session); + } } } @@ -2784,7 +2828,11 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans * Pass the session ref held by session->inv_session to * session_end_completion(). */ - session_end_completion(session); + if (session + && ast_sip_push_task(session->serializer, session_end_completion, session)) { + /* Do it anyway even though this is not the right thread. */ + session_end_completion(session); + } return; } break; @@ -2909,7 +2957,12 @@ static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, stru static const pj_str_t STR_IP6 = { "IP6", 3 }; pjmedia_sdp_session *local; - if (!(local = PJ_POOL_ZALLOC_T(inv->pool_prov, pjmedia_sdp_session))) { + if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { + ast_log(LOG_ERROR, "Failed to create session SDP. Session has been already disconnected\n"); + return NULL; + } + + if (!inv->pool_prov || !(local = PJ_POOL_ZALLOC_T(inv->pool_prov, pjmedia_sdp_session))) { return NULL; } diff --git a/third-party/pjproject/configure.m4 b/third-party/pjproject/configure.m4 index 67ac04d4d869bf59196f696ee38f2c84b09af037..7a079f6577f5da7ae1a06b5fd059a53e2d32b74e 100644 --- a/third-party/pjproject/configure.m4 +++ b/third-party/pjproject/configure.m4 @@ -45,4 +45,5 @@ AC_DEFUN([PJPROJECT_CONFIGURE], PJPROJECT_SYMBOL_CHECK([PJSIP_EXTERNAL_RESOLVER], [pjsip_endpt_set_ext_resolver], [pjsip.h]) AC_DEFINE([HAVE_PJSIP_TLS_TRANSPORT_PROTO], 1, [Define if your system has PJSIP_TLS_TRANSPORT_PROTO]) AC_DEFINE([HAVE_PJSIP_EVSUB_GRP_LOCK], 1, [Define if your system has PJSIP_EVSUB_GRP_LOCK]) + AC_DEFINE([HAVE_PJSIP_INV_SESSION_REF], 1, [Define if your system has PJSIP_INV_SESSION_REF]) ]) diff --git a/third-party/pjproject/patches/0002-r5435-add-pjsip_inv_session-ref_cnt.patch b/third-party/pjproject/patches/0002-r5435-add-pjsip_inv_session-ref_cnt.patch new file mode 100644 index 0000000000000000000000000000000000000000..12ae6a0285f422121021eac057da4fbc73687d14 --- /dev/null +++ b/third-party/pjproject/patches/0002-r5435-add-pjsip_inv_session-ref_cnt.patch @@ -0,0 +1,212 @@ +When a transport error occured on an INVITE session +the stack calls on_tsx_state_changed with new state +PJSIP_INV_STATE_DISCONNECTED and immediately destroys +the INVITE session. +At the same time this INVITE session could being processed +on another thread. This thread could use the session's +memory pools which were already freed, so we get segfault. + +This patch adds a reference counter and new functions: +pjsip_inv_add_ref and pjsip_inv_dec_ref. +The INVITE session is destroyed only when the reference +counter has reached zero. + +To avoid race condition an application should call +pjsip_inv_add_ref/pjsip_inv_dec_ref. + +Index: pjsip/include/pjsip-ua/sip_inv.h +=================================================================== +--- a/pjsip/include/pjsip-ua/sip_inv.h (revision 5434) ++++ b/pjsip/include/pjsip-ua/sip_inv.h (revision 5435) +@@ -383,6 +383,11 @@ + * Other applications that want to use these pools must understand + * that the flip-flop pool's lifetimes are synchronized to the + * SDP offer-answer negotiation. ++ * ++ * The lifetime of this session is controlled by the reference counter in this ++ * structure, which is manipulated by calling #pjsip_inv_add_ref and ++ * #pjsip_inv_dec_ref. When the reference counter has reached zero, then ++ * this session will be destroyed. + */ + struct pjsip_inv_session + { +@@ -412,6 +417,7 @@ + struct pjsip_timer *timer; /**< Session Timers. */ + pj_bool_t following_fork; /**< Internal, following + forked media? */ ++ pj_atomic_t *ref_cnt; /**< Reference counter. */ + }; + + +@@ -631,6 +637,30 @@ + + + /** ++ * Add reference counter to the INVITE session. The reference counter controls ++ * the life time of the session, ie. when the counter reaches zero, then it ++ * will be destroyed. ++ * ++ * @param inv The INVITE session. ++ * @return PJ_SUCCESS if the INVITE session reference counter ++ * was increased. ++ */ ++PJ_DECL(pj_status_t) pjsip_inv_add_ref( pjsip_inv_session *inv ); ++ ++/** ++ * Decrement reference counter of the INVITE session. ++ * When the session is no longer used, it will be destroyed and ++ * caller is informed with PJ_EGONE return status. ++ * ++ * @param inv The INVITE session. ++ * @return PJ_SUCCESS if the INVITE session reference counter ++ * was decreased. A status PJ_EGONE will be returned to ++ * inform that session is destroyed. ++ */ ++PJ_DECL(pj_status_t) pjsip_inv_dec_ref( pjsip_inv_session *inv ); ++ ++ ++/** + * Forcefully terminate and destroy INVITE session, regardless of + * the state of the session. Note that this function should only be used + * when there is failure in the INVITE session creation. After the +Index: pjsip/src/pjsip-ua/sip_inv.c +=================================================================== +--- a/pjsip/src/pjsip-ua/sip_inv.c (revision 5434) ++++ b/pjsip/src/pjsip-ua/sip_inv.c (revision 5435) +@@ -195,6 +195,65 @@ + } + + /* ++ * Add reference to INVITE session. ++ */ ++PJ_DEF(pj_status_t) pjsip_inv_add_ref( pjsip_inv_session *inv ) ++{ ++ PJ_ASSERT_RETURN(inv && inv->ref_cnt, PJ_EINVAL); ++ ++ pj_atomic_inc(inv->ref_cnt); ++ ++ return PJ_SUCCESS; ++} ++ ++static void inv_session_destroy(pjsip_inv_session *inv) ++{ ++ if (inv->last_ack) { ++ pjsip_tx_data_dec_ref(inv->last_ack); ++ inv->last_ack = NULL; ++ } ++ if (inv->invite_req) { ++ pjsip_tx_data_dec_ref(inv->invite_req); ++ inv->invite_req = NULL; ++ } ++ if (inv->pending_bye) { ++ pjsip_tx_data_dec_ref(inv->pending_bye); ++ inv->pending_bye = NULL; ++ } ++ pjsip_100rel_end_session(inv); ++ pjsip_timer_end_session(inv); ++ pjsip_dlg_dec_session(inv->dlg, &mod_inv.mod); ++ ++ /* Release the flip-flop pools */ ++ pj_pool_release(inv->pool_prov); ++ inv->pool_prov = NULL; ++ pj_pool_release(inv->pool_active); ++ inv->pool_active = NULL; ++ ++ pj_atomic_destroy(inv->ref_cnt); ++ inv->ref_cnt = NULL; ++} ++ ++/* ++ * Decrease INVITE session reference, destroy it when the reference count ++ * reaches zero. ++ */ ++PJ_DEF(pj_status_t) pjsip_inv_dec_ref( pjsip_inv_session *inv ) ++{ ++ pj_atomic_value_t ref_cnt; ++ ++ PJ_ASSERT_RETURN(inv && inv->ref_cnt, PJ_EINVAL); ++ ++ ref_cnt = pj_atomic_dec_and_get(inv->ref_cnt); ++ pj_assert( ref_cnt >= 0); ++ if (ref_cnt == 0) { ++ inv_session_destroy(inv); ++ return PJ_EGONE; ++ } ++ return PJ_SUCCESS; ++} ++ ++/* + * Set session state. + */ + static void inv_set_state(pjsip_inv_session *inv, pjsip_inv_state state, +@@ -261,27 +320,7 @@ + if (inv->state == PJSIP_INV_STATE_DISCONNECTED && + prev_state != PJSIP_INV_STATE_DISCONNECTED) + { +- if (inv->last_ack) { +- pjsip_tx_data_dec_ref(inv->last_ack); +- inv->last_ack = NULL; +- } +- if (inv->invite_req) { +- pjsip_tx_data_dec_ref(inv->invite_req); +- inv->invite_req = NULL; +- } +- if (inv->pending_bye) { +- pjsip_tx_data_dec_ref(inv->pending_bye); +- inv->pending_bye = NULL; +- } +- pjsip_100rel_end_session(inv); +- pjsip_timer_end_session(inv); +- pjsip_dlg_dec_session(inv->dlg, &mod_inv.mod); +- +- /* Release the flip-flop pools */ +- pj_pool_release(inv->pool_prov); +- inv->pool_prov = NULL; +- pj_pool_release(inv->pool_active); +- inv->pool_active = NULL; ++ pjsip_inv_dec_ref(inv); + } + } + +@@ -838,6 +877,12 @@ + inv = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_inv_session); + pj_assert(inv != NULL); + ++ status = pj_atomic_create(dlg->pool, 0, &inv->ref_cnt); ++ if (status != PJ_SUCCESS) { ++ pjsip_dlg_dec_lock(dlg); ++ return status; ++ } ++ + inv->pool = dlg->pool; + inv->role = PJSIP_ROLE_UAC; + inv->state = PJSIP_INV_STATE_NULL; +@@ -881,6 +926,7 @@ + pjsip_100rel_attach(inv); + + /* Done */ ++ pjsip_inv_add_ref(inv); + *p_inv = inv; + + pjsip_dlg_dec_lock(dlg); +@@ -1471,6 +1517,12 @@ + inv = PJ_POOL_ZALLOC_T(dlg->pool, pjsip_inv_session); + pj_assert(inv != NULL); + ++ status = pj_atomic_create(dlg->pool, 0, &inv->ref_cnt); ++ if (status != PJ_SUCCESS) { ++ pjsip_dlg_dec_lock(dlg); ++ return status; ++ } ++ + inv->pool = dlg->pool; + inv->role = PJSIP_ROLE_UAS; + inv->state = PJSIP_INV_STATE_NULL; +@@ -1540,6 +1592,7 @@ + } + + /* Done */ ++ pjsip_inv_add_ref(inv); + pjsip_dlg_dec_lock(dlg); + *p_inv = inv; +