From 4283325bfe9ef9488af76510ee886bc87aa7152f Mon Sep 17 00:00:00 2001
From: George Yang <g.yang@genexis.eu>
Date: Thu, 23 May 2024 08:58:25 +0000
Subject: [PATCH] Fix a deadlock issue triggered by R1

Deadlock happens between threads which respectively call chan_voicemngr_getRtpStats() or
chan_voicemngr_indicate() to lock sub->parent and ast_channel objects.
---
 src/channels/chan_voicemngr.c | 32 ++++++++++----------------------
 1 file changed, 10 insertions(+), 22 deletions(-)

diff --git a/src/channels/chan_voicemngr.c b/src/channels/chan_voicemngr.c
index 73efc1d..3e14c9b 100644
--- a/src/channels/chan_voicemngr.c
+++ b/src/channels/chan_voicemngr.c
@@ -133,7 +133,7 @@ static struct ast_channel *chan_voicemngr_new(struct chan_voicemngr_subchannel *
 		struct ast_format_cap *format);
 static int handle_dialtone_timeout(const void *data);
 static int handle_congestion_timeout(const void *data);
-static int endpt_get_rtp_stats(int line);
+static int endpt_get_rtp_stats(struct chan_voicemngr_subchannel *sub);
 static int is_call_waiting_enabled(const char *sip_account);
 static int has_call_in_sip_client(const char *sip_account);
 static struct ast_format *map_rtpname_to_format(char* name);
@@ -1154,8 +1154,9 @@ static int chan_voicemngr_getRtpStats(struct ast_channel *ast)
 	}
 
 	if (sub->parent) {
+		chan_voicemngr_get_sip_client_id(sub);
 		pvt_lock(sub->parent, "chan_voicemngr_getRtpStats");
-		if (endpt_get_rtp_stats(sub->parent->line_id)) {
+		if (endpt_get_rtp_stats(sub)) {
 			ast_log(LOG_WARNING, "Unable to get RTP statistics\n");
 		}
 		pvt_unlock(sub->parent);
@@ -4948,12 +4949,14 @@ static void ubus_call_answer_rtp_stats(struct ubus_request *req, int type, struc
 		return;
 	}
 
-	sub = chan_voicemngr_get_active_subchannel(p);
+	sub = (struct chan_voicemngr_subchannel *)req->priv;
 	if (!sub) {
-		ast_log(LOG_ERROR, "No active subchannel to write rtp stats!\n");
+		ast_log(LOG_ERROR, "No subchannel found!\n");
 		return;
 	}
 
+	sip_client_id = sub->sip_client_id;
+
 	if (tb[RTP_STATS_LOCAL_BURST_DENSITY])
 		sub->rtp_stats.localBurstDensity = blobmsg_get_u16(tb[RTP_STATS_LOCAL_BURST_DENSITY]);
 	if (tb[RTP_STATS_REMOTE_BURST_DENSITY])
@@ -4990,7 +4993,6 @@ static void ubus_call_answer_rtp_stats(struct ubus_request *req, int type, struc
 		sub->rtp_stats.discarded = blobmsg_get_u32(tb[RTP_STATS_DISCARDED]);
 	if (tb[RTP_STATS_LOST]) {
 		sub->rtp_stats.lost = blobmsg_get_u32(tb[RTP_STATS_LOST]);
-		sip_client_id = chan_voicemngr_get_sip_client_id(sub);
 		if (sip_client_id >= 0 && sip_client_id < MAX_SIP_CLIENTS) {
 			line_stats[sip_client_id].pktslost += sub->rtp_stats.lost;
 		}
@@ -5011,14 +5013,12 @@ static void ubus_call_answer_rtp_stats(struct ubus_request *req, int type, struc
 		sub->rtp_stats.maxJitter = blobmsg_get_u32(tb[RTP_STATS_MAX_JITTER]);
 	if (tb[RTP_STATS_OVERRUNS]) {
 		overruns = blobmsg_get_u16(tb[RTP_STATS_OVERRUNS]);
-		sip_client_id = chan_voicemngr_get_sip_client_id(sub);
 		if (sip_client_id >= 0 && sip_client_id < MAX_SIP_CLIENTS) {
 			line_stats[sip_client_id].total_overruns += overruns;
 		}
 	}
 	if (tb[RTP_STATS_UNDERRUNS]) {
 		underruns = blobmsg_get_u16(tb[RTP_STATS_UNDERRUNS]);
-		sip_client_id = chan_voicemngr_get_sip_client_id(sub);
 		if (sip_client_id >= 0 && sip_client_id < MAX_SIP_CLIENTS) {
 			line_stats[sip_client_id].total_underruns += underruns;
 		}
@@ -5050,29 +5050,17 @@ static void ubus_call_answer_rtp_stats(struct ubus_request *req, int type, struc
 			sub->rtp_stats.localAvgRoundTripDelay, sub->rtp_stats.remoteAvgRoundTripDelay, overruns, underruns);
 }
 
-static int endpt_get_rtp_stats(int line) {
+static int endpt_get_rtp_stats(struct chan_voicemngr_subchannel *sub) {
+	int line = sub->parent->line_id;
 	struct ubus_context *local_ctx;
 	struct blob_buf bb;
 	int ret;
-	struct chan_voicemngr_pvt *p = NULL;
-	struct chan_voicemngr_subchannel *sub = NULL;
 
 	/*
 	 * Reset rtp_stats first because ubus_call_answer_rtp_stats() will not be called if "ubus call endpt rtp_stats" fails,
 	 * e.g. an unanswered incoming call on which the connection is not created. In this case, all RTP statistics counters
 	 * shall be zeros.
 	 */
-	p = chan_voicemngr_get_pvt_from_lineid(iflist, line);
-	if (!p) {
-		ast_log(LOG_ERROR, "No pvt with the line %d found!\n", line);
-		return -1;
-	}
-
-	sub = chan_voicemngr_get_active_subchannel(p);
-	if (!sub) {
-		ast_log(LOG_ERROR, "No active subchannel to get rtp stats!\n");
-		return -1;
-	}
 	memset(&sub->rtp_stats, 0, sizeof(sub->rtp_stats));
 
 	if (!endpt_id) {
@@ -5094,7 +5082,7 @@ static int endpt_get_rtp_stats(int line) {
 	blobmsg_add_u8(&bb, "reset", 1); // always reset RTP stats after get them
 
 	ast_log(LOG_DEBUG, "thread %d: ubus call endpt rtp_stats \"{'line':%d,'reset':true}\"", ast_get_tid(), line);
-	ret = ubus_invoke(local_ctx, endpt_id, "rtp_stats", bb.head, ubus_call_answer_rtp_stats, NULL, 500);
+	ret = ubus_invoke(local_ctx, endpt_id, "rtp_stats", bb.head, ubus_call_answer_rtp_stats, sub, 500);
 
 	blob_buf_free(&bb);
 	ast_ubus_free_context(local_ctx);
-- 
GitLab