Commit f616affc2a for asterisk.org

commit f616affc2af658fd5149374e10efcb23081339b1
Author: Charles Langlois <charles.langlois@wazo.io>
Date:   Thu Apr 16 16:02:54 2026 -0400

    chan_pjsip: Fix deadlock when endpoint set_var uses PJSIP_HEADER

    When a PJSIP endpoint is configured with set_var invoking a dialplan
    function (e.g. PJSIP_HEADER(add,...)), chan_pjsip_new() calls
    pbx_builtin_setvar_helper() while holding the channel lock.
    For function-style variables, this dispatches to ast_func_write()
    which, in the case of PJSIP_HEADER, calls
    ast_sip_push_task_wait_serializer() -- blocking synchronously while
    the channel lock is held.

    If a concurrent operation (ARI, AMI, rtp_check_timeout) traverses
    the channels container via ast_channel_get_by_name(), it acquires
    the container lock then tries to lock individual channels in the
    iteration callback (by_uniqueid_cb/by_name_cb). When the serializer
    thread also needs the container lock, a circular dependency forms:

      channel_lock -> serializer_wait -> container_lock -> channel_lock

    This causes a complete Asterisk freeze. In the observed case, 36
    threads were blocked on the container lock until res_freeze_check
    triggered SIGABRT after its 30-second timeout.

    Unlock the channel before iterating endpoint channel_vars so that
    dialplan functions can block without holding the channel lock. Re-lock
    the channel for ast_channel_stage_snapshot_done() so the batched
    snapshot is published under lock and captures the full channel state
    including the variables set during the loop.

    Fixes: #1872

diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index c2d44065fa..008f41206b 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -665,15 +665,15 @@ static struct ast_channel *chan_pjsip_new(struct ast_sip_session *session, int s
 		ast_channel_zone_set(chan, zone);
 	}

+	ast_channel_stage_snapshot_done(chan);
+	ast_channel_unlock(chan);
+
 	for (var = session->endpoint->channel_vars; var; var = var->next) {
 		char buf[512];
 		pbx_builtin_setvar_helper(chan, var->name, ast_get_encoded_str(
 					var->value, buf, sizeof(buf)));
 	}

-	ast_channel_stage_snapshot_done(chan);
-	ast_channel_unlock(chan);
-
 	set_channel_on_rtp_instance(session, ast_channel_uniqueid(chan));

 	SCOPE_EXIT_RTN_VALUE(chan);