summaryrefslogtreecommitdiff
path: root/res
diff options
context:
space:
mode:
authorRichard Mudgett <rmudgett@digium.com>2018-02-22 13:53:20 -0600
committerRichard Mudgett <rmudgett@digium.com>2018-02-22 17:15:25 -0600
commitbb9c1938a019744ffa02dcd2779b97cd9240e182 (patch)
treed02549a0f4cb07d6e11ee1d3f53e523966edc827 /res
parentd4fc1bdad67c26eb340624e06e81f86ab55aaa20 (diff)
res_pjsip_refer.c: Fix attended transfer race condition crash.
The transferrer's session channel was destroyed by the transferrer's serializer thread in a race condition with the transfer target's serializer thread during an attended transfer. The transfer target's serializer was attempting to clean up a deferred end status on behalf of the transferrer's channel when it should have passed the action to the transferrer's serializer. When the transfer target's serializer lost the race then both threads wind up trying to end the transferrer's session. * Push the ast_sip_session_end_if_deferred() call onto the transferrer's serializer to avoid a race condition that results in a crash. The session_end() function that could be called by ast_sip_session_end_if_deferred() really must be executed by the transferrer's serializer to avoid this kind of crash. ASTERISK-27568 Change-Id: Iacda724e7cb24d7520e49b2fd7e504aa398d7238
Diffstat (limited to 'res')
-rw-r--r--res/res_pjsip_refer.c32
1 files changed, 24 insertions, 8 deletions
diff --git a/res/res_pjsip_refer.c b/res/res_pjsip_refer.c
index 5e0141b09..7d892f653 100644
--- a/res/res_pjsip_refer.c
+++ b/res/res_pjsip_refer.c
@@ -468,10 +468,20 @@ static struct refer_attended *refer_attended_alloc(struct ast_sip_session *trans
return attended;
}
-static int defer_termination_cancel(void *data)
+static int session_end_if_deferred_task(void *data)
{
struct ast_sip_session *session = data;
+ ast_sip_session_end_if_deferred(session);
+ ao2_ref(session, -1);
+ return 0;
+}
+
+static int defer_termination_cancel_task(void *data)
+{
+ struct ast_sip_session *session = data;
+
+ ast_sip_session_end_if_deferred(session);
ast_sip_session_defer_termination_cancel(session);
ao2_ref(session, -1);
return 0;
@@ -513,6 +523,7 @@ static int refer_attended_task(void *data)
{
struct refer_attended *attended = data;
int response;
+ int (*task_cb)(void *data);
if (attended->transferer_second->channel) {
ast_debug(3, "Performing a REFER attended transfer - Transferer #1: %s Transferer #2: %s\n",
@@ -543,13 +554,18 @@ static int refer_attended_task(void *data)
}
}
- ast_sip_session_end_if_deferred(attended->transferer);
- if (response != 200) {
- if (!ast_sip_push_task(attended->transferer->serializer,
- defer_termination_cancel, attended->transferer)) {
- /* Gave the ref to the pushed task. */
- attended->transferer = NULL;
- }
+ if (response == 200) {
+ task_cb = session_end_if_deferred_task;
+ } else {
+ task_cb = defer_termination_cancel_task;
+ }
+ if (!ast_sip_push_task(attended->transferer->serializer,
+ task_cb, attended->transferer)) {
+ /* Gave the ref to the pushed task. */
+ attended->transferer = NULL;
+ } else {
+ /* Do this anyway even though it is the wrong serializer. */
+ ast_sip_session_end_if_deferred(attended->transferer);
}
ao2_ref(attended, -1);