summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Harwell <kharwell@digium.com>2017-10-19 13:35:16 -0500
committerGeorge Joseph <gjoseph@digium.com>2017-11-08 05:48:37 -0700
commit6032fb973b8f9d7e52105656c877be525f7317f8 (patch)
treecc1409b0d82eb4f21c9cd4b5a08ab2097c193968
parent9c7c441a0fa70b4a5cbec4588f8a658c717849e5 (diff)
AST-2017-011 - res_pjsip_session: session leak when a call is rejected
A previous commit made it so when an invite session transitioned into a disconnected state destruction of the Asterisk pjsip session object was postponed until either a transport error occurred or the event timer expired. However, if a call was rejected (for instance a 488) before the session was fully established the event timer may not have been initiated, or it was canceled without triggering either of the session finalizing states mentioned above. Really the only time destruction of the session should be delayed is when a BYE is being transacted. This is because it's possible in some cases for the session to be disconnected, but the BYE is still transacting. This patch makes it so the session object always gets released (no more memory leak) when the pjsip session is in a disconnected state. Except when the method is a BYE. Then it waits until a transport error occurs or an event timeout. ASTERISK-27345 #close Reported by: Corey Farrell Change-Id: I1e724737b758c20ac76d19d3611e3d2876ae10ed
-rw-r--r--res/res_pjsip_session.c80
1 files changed, 42 insertions, 38 deletions
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 808477c0f..8ad6d3ce4 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -2686,6 +2686,36 @@ static void session_inv_on_new_session(pjsip_inv_session *inv, pjsip_event *e)
/* XXX STUB */
}
+static int session_end_if_disconnected(int id, pjsip_inv_session *inv)
+{
+ struct ast_sip_session *session;
+
+ if (inv->state != PJSIP_INV_STATE_DISCONNECTED) {
+ return 0;
+ }
+
+ /*
+ * We are locking because ast_sip_dialog_get_session() needs
+ * the dialog locked to get the session by other threads.
+ */
+ pjsip_dlg_inc_lock(inv->dlg);
+ session = inv->mod_data[id];
+ inv->mod_data[id] = NULL;
+ pjsip_dlg_dec_lock(inv->dlg);
+
+ /*
+ * Pass the session ref held by session->inv_session to
+ * session_end_completion().
+ */
+ 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 1;
+}
+
static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e)
{
ast_sip_session_response_cb cb;
@@ -2710,6 +2740,17 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
/* The session has ended. Ignore the transaction change. */
return;
}
+
+ /*
+ * If the session is disconnected really nothing else to do unless currently transacting
+ * a BYE. If a BYE then hold off destruction until the transaction timeout occurs. This
+ * has to be done for BYEs because sometimes the dialog can be in a disconnected
+ * state but the BYE request transaction has not yet completed.
+ */
+ if (tsx->method.id != PJSIP_BYE_METHOD && session_end_if_disconnected(id, inv)) {
+ return;
+ }
+
switch (e->body.tsx_state.type) {
case PJSIP_EVENT_TX_MSG:
/* When we create an outgoing request, we do not have access to the transaction that
@@ -2832,49 +2873,12 @@ static void session_inv_on_tsx_state_changed(pjsip_inv_session *inv, pjsip_trans
}
break;
case PJSIP_EVENT_TRANSPORT_ERROR:
- if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
- /*
- * Clear the module data now to block session_inv_on_state_changed()
- * from calling session_end() if it hasn't already done so.
- */
- inv->mod_data[id] = NULL;
-
- /*
- * Pass the session ref held by session->inv_session to
- * session_end_completion().
- */
- 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;
case PJSIP_EVENT_TIMER:
/*
* The timer event is run by the pjsip monitor thread and not
* by the session serializer.
*/
- if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
- /*
- * We are locking because ast_sip_dialog_get_session() needs
- * the dialog locked to get the session by other threads.
- */
- pjsip_dlg_inc_lock(inv->dlg);
- session = inv->mod_data[id];
- inv->mod_data[id] = NULL;
- pjsip_dlg_dec_lock(inv->dlg);
-
- /*
- * Pass the session ref held by session->inv_session to
- * session_end_completion().
- */
- 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);
- }
+ if (session_end_if_disconnected(id, inv)) {
return;
}
break;