From 69a27a4917fb757298b3addabccc6239029761eb Mon Sep 17 00:00:00 2001
From: Yalu Zhang <yalu.zhang@iopsys.eu>
Date: Tue, 21 Jan 2025 16:42:52 +0100
Subject: [PATCH] Fix segmentation fault caused by NULL pointer access, REF
 16142

---
 src/channels/chan_voicemngr.c | 115 +++++++++++++++-------------------
 1 file changed, 52 insertions(+), 63 deletions(-)

diff --git a/src/channels/chan_voicemngr.c b/src/channels/chan_voicemngr.c
index eee75f6..6d8ebf5 100644
--- a/src/channels/chan_voicemngr.c
+++ b/src/channels/chan_voicemngr.c
@@ -1390,54 +1390,44 @@ __ret:
 
 static int chan_voicemngr_call(struct ast_channel *chan, const char *dest, int timeout)
 {
-	struct chan_voicemngr_pvt *p;
-	struct chan_voicemngr_subchannel *sub;
-	struct chan_voicemngr_subchannel *sub_peer;
-
-	struct timeval UtcTime = ast_tvnow();
-	struct ast_tm tm;
-	sub = ast_channel_tech_pvt(chan);
+	struct chan_voicemngr_subchannel *sub = ast_channel_tech_pvt(chan);
+	struct chan_voicemngr_subchannel *sub_peer = chan_voicemngr_subchannel_get_peer(sub);
+	struct chan_voicemngr_pvt *p = sub->parent;
 	int sip_client_id = -1;
 
 	if (!sub || !sub->parent) {
-	    ast_log(LOG_ERROR, "Failed to get peer subchannel\n");
-	    return -1;
+		ast_log(LOG_ERROR, "Invalid subchannel\n");
+		return -1;
 	}
 
-	ast_debug(1, "line %d\n", sub->parent->line_id);
-	sscanf(sub->parent->context, "sip%d", &sip_client_id);
-	ast_localtime(&UtcTime, &tm, NULL);
-
-	if ((ast_channel_state(chan) != AST_STATE_DOWN) && (ast_channel_state(chan) != AST_STATE_RESERVED)) {
-		ast_log(LOG_WARNING, "chan_voicemngr_call called on %s, neither down nor reserved\n", ast_channel_name(chan));
+	if (ast_channel_state(chan) != AST_STATE_DOWN && ast_channel_state(chan) != AST_STATE_RESERVED) {
+		ast_log(LOG_WARNING, "called on channel %s, neither down nor reserved\n", ast_channel_name(chan));
 		return -1;
 	}
 
-	//ast_mutex_lock(&sub->parent->lock);
-	pvt_lock(sub->parent, "chan_voicemngr_call");
-	p = sub->parent;
-	sub_peer = chan_voicemngr_subchannel_get_peer(sub);
-	ast_debug(1, "channel state: %s, peer channel state: %s\n", state2str(sub->channel_state), state2str(sub_peer->channel_state));
+	ast_debug(1, "line %d, sub channel state %s, peer state %s\n", p->line_id, state2str(sub->channel_state),
+			state2str(sub_peer->channel_state));
+	sscanf(p->context, "sip%d", &sip_client_id);
+
+	pvt_lock(p, "chan_voicemngr_call");
+
 	if (!channel_config[p->line_id].enabled) {
 		ast_debug(1, "tel_line %d disabled!\n", p->line_id);
 		ast_channel_hangupcause_set(chan, AST_CAUSE_CALL_REJECTED);
 		ast_queue_control(chan, AST_CONTROL_BUSY);
 		chan_voicemngr_send_ubus_event("CALL_REJECTED",p->line_id);
-	}
-	else if (channel_config[p->line_id].do_not_disturb) {
+	} else if (channel_config[p->line_id].do_not_disturb) {
 		ast_debug(1, "Do not disturbed\n");
 		ast_channel_hangupcause_set(chan, AST_CAUSE_USER_BUSY);
 		chan_voicemngr_send_ubus_event("USER_BUSY",p->line_id);
 		ast_queue_control(chan, AST_CONTROL_BUSY);
-	}
-	else if(p->emergency) {
+	} else if(p->emergency) {
 		ast_debug(1, "Emergency Call Ongoing\n");
 		ast_channel_hangupcause_set(chan, AST_CAUSE_USER_BUSY);
 		chan_voicemngr_send_ubus_event("USER_BUSY",p->line_id);
 		ast_queue_control(chan, AST_CONTROL_BUSY);
-	}
-	else if (chan_voicemngr_in_call(p) &&                          		// a call is established
-		is_call_waiting_enabled(p->context)) {			// call waiting enabled
+	} else if (chan_voicemngr_in_call(p) &&  // a call is established
+		is_call_waiting_enabled(p->context)) { // call waiting enabled
 		int cw_delay = 100;
 
 		ast_debug(1, "Call waiting\n");
@@ -1452,9 +1442,8 @@ static int chan_voicemngr_call(struct ast_channel *chan, const char *dest, int t
 		sub->cw_timer_id = ast_sched_add(sched, cwtimeout_ms, cwtimeout_cb, sub);
 		ast_setstate(chan, AST_STATE_RINGING_CW);
 		ast_queue_control(chan, AST_CONTROL_RINGING_CW);
-	}
-	else if (!chan_voicemngr_subchannel_is_idle(sub_peer) ||
-		(has_call_in_sip_client(p->context) && sip_line_callmode[sip_client_id] == LINE_CALL_MODE_SINGLE) ) {
+	} else if (!chan_voicemngr_subchannel_is_idle(sub_peer) ||
+		(has_call_in_sip_client(p->context) && sip_line_callmode[sip_client_id] == LINE_CALL_MODE_SINGLE)) {
 		ast_debug(1, "Line is busy\n");
 		ast_channel_hangupcause_set(chan, AST_CAUSE_USER_BUSY);
 		ast_queue_control(chan, AST_CONTROL_BUSY);
@@ -1462,24 +1451,7 @@ static int chan_voicemngr_call(struct ast_channel *chan, const char *dest, int t
 		if(sub->channel_state == ALLOCATED)
 			sub->channel_state = ONHOOK;
 		chan_voicemngr_send_ubus_event("USER_BUSY",p->line_id);
-
-		struct chan_voicemngr_pvt *tmp = p;
-		struct chan_voicemngr_subchannel *s = NULL;
-		int con_id = -1;
-
-		while(tmp) {
-			s = chan_voicemngr_get_active_subchannel(tmp);
-			con_id = s->connection_id;
-			// for local call we need to find the other party and change its subchannel state to CALLENDED
-			// so the extensionCallStatus is properly set to Disconnected in chan_voicemngr_answer()
-			if (con_id > 0 && (strncmp(tmp->extensionCallStatus, "Dialing", strlen(tmp->extensionCallStatus)) == 0)) {
-				s->channel_state = CALLENDED;
-				break;
-			}
-			tmp = chan_voicemngr_get_next_pvt(tmp);
-		}
-	}
-	else {
+	} else {
 		ast_debug(1, "Not call waiting\n");
 		/*We can move status to Alerting ,since we are ringing now */
 		strncpy(p->extensionCallStatus, "Alerting", CALL_STATUS_MAX_LEN);
@@ -1507,7 +1479,7 @@ static int chan_voicemngr_call(struct ast_channel *chan, const char *dest, int t
 
 		chan_voicemngr_send_ubus_event("RINGING",p->line_id);
 	}
-	//ast_mutex_unlock(&sub->parent->lock);
+
 	pvt_unlock(sub->parent);
 
 	return 0;
@@ -2169,8 +2141,10 @@ struct chan_voicemngr_subchannel* chan_voicemngr_get_active_subchannel(const str
 	struct chan_voicemngr_subchannel *sub = NULL;
 	int i;
 
-	if(!p)
+	if(!p) {
+		ast_log(LOG_ERROR, "Invalid parameter\n");
 		return NULL;
+	}
 
 	for (i=0; i<NUM_SUBCHANNELS; i++) {
 		switch (p->sub[i]->channel_state) {
@@ -2203,12 +2177,15 @@ struct chan_voicemngr_subchannel* chan_voicemngr_get_active_subchannel(const str
 				break;
 
 			default:
-				ast_log(LOG_WARNING, "Unhandled channel state %d\n", p->sub[i]->channel_state);
+				ast_log(LOG_WARNING, "Unhandled channel state %s(%d) on line %d sub-channel %d\n",
+					state2str(p->sub[i]->channel_state), p->sub[i]->channel_state, p->line_id, i);
 				break;
 		}
 	}
 
-	ast_assert(sub != NULL);
+	if(!sub) {
+		ast_log(LOG_WARNING, "There is no active sub-channel found on line %d\n", p->line_id);
+	}
 	return sub;
 }
 
@@ -2230,10 +2207,15 @@ static int setup_conference_call_cb( const void *data)
 {
 	struct chan_voicemngr_subchannel *sub_peer;
 	struct ast_channel *peer_owner = NULL;
+	struct chan_voicemngr_pvt *p = (struct chan_voicemngr_pvt *)data;
+	struct chan_voicemngr_subchannel *sub = chan_voicemngr_get_active_subchannel(p);
+
+	if (!sub) {
+		return -1;
+	}
 
 	ast_log(LOG_NOTICE ,"Set up a conference call\n");
-	struct chan_voicemngr_pvt *p = (struct chan_voicemngr_pvt *) data;
-	struct chan_voicemngr_subchannel *sub = chan_voicemngr_get_active_subchannel(p);
+
 	sub_peer = chan_voicemngr_subchannel_get_peer(sub);
 	pvt_lock(sub->parent, "setup_conference_call_cb");
 	if (sub_peer->owner) {
@@ -2451,9 +2433,12 @@ static int onholdhanguptimeout_cb(const void *data)
 
 static int handle_busy_internal_call_timeout(const void *data)
 {
-	struct chan_voicemngr_pvt *p = (struct chan_voicemngr_pvt *) data;
+	struct chan_voicemngr_pvt *p = (struct chan_voicemngr_pvt *)data;
 	struct chan_voicemngr_subchannel *sub = chan_voicemngr_get_active_subchannel(p);
 
+	if (!sub)
+		return -1;
+
 	ast_debug(9, "busy internal timeout, state:%d, dialtone: %d\n", sub->channel_state, p->dialtone);
 
 	pvt_lock(p, "busy timeout");
@@ -2527,6 +2512,9 @@ static int handle_audio_announcement_timeout(const void *data)
 	struct chan_voicemngr_subchannel *sub = chan_voicemngr_get_active_subchannel(p);
 	unsigned int old_state;
 
+	if (!sub)
+		return -1;
+
 	pvt_lock(p, "audio announcement");
 	p->audio_announcement_timer_id = -1;
 
@@ -2588,7 +2576,8 @@ static int handle_interdigit_timeout(const void *data)
 	pvt_lock(p, "interdigit callback");
 	p->interdigit_timer_id = -1;
 	struct chan_voicemngr_subchannel *sub = chan_voicemngr_get_active_subchannel(p);
-	if (channel_config[p->line_id].minimumnumberdigits  &&
+
+	if (sub && channel_config[p->line_id].minimumnumberdigits &&
 		strlen(p->dtmfbuf) >= channel_config[p->line_id].minimumnumberdigits) {
 		if (ast_exists_extension(NULL, p->invoke_context, p->dtmfbuf, 1, p->cid_num)) {
 			//We have at least one matching extension in "normal" context,
@@ -2596,11 +2585,11 @@ static int handle_interdigit_timeout(const void *data)
 			//Asterisk will select the best matching extension if there are more than one possibility.
 			ast_debug(9, "Interdigit timeout, extension(s) matching %s found\n", p->dtmfbuf);
 			chan_voicemngr_start_calling(sub, p->invoke_context);
-	   }
-    }
+		}
+	}
 
 	//if no limit on minimum digits check extension exits
-	if(!channel_config[p->line_id].minimumnumberdigits) {
+	if(sub && !channel_config[p->line_id].minimumnumberdigits) {
 		if (ast_exists_extension(NULL, p->invoke_context, p->dtmfbuf, 1, p->cid_num)) {
 			//We have at least one matching extension in "normal" context,
 			//and interdigit timeout has passed, so have asterisk start calling.
@@ -2650,7 +2639,7 @@ static int handle_autodial_timeout(const void *data)
 	struct chan_voicemngr_subchannel *sub = chan_voicemngr_get_active_subchannel(p);
 	channel_settings *s = &channel_config[p->line_id];
 
-	if (ast_exists_extension(NULL, p->invoke_context, s->autodial_ext, 1, p->cid_num)) {
+	if (sub && ast_exists_extension(NULL, p->invoke_context, s->autodial_ext, 1, p->cid_num)) {
 		chan_voicemngr_stop_dialtone(p);
 		ast_copy_string(p->dtmfbuf, s->autodial_ext, sizeof(p->dtmfbuf));
 		ast_debug(9, "Autodialing extension: %s\n", p->dtmfbuf);
@@ -2723,7 +2712,7 @@ static int handle_emergency_registration(const void *data)
 	p->emergency_registration_timer_count++;
 	struct chan_voicemngr_subchannel *sub = chan_voicemngr_get_active_subchannel(p);
 
-	if (is_sip_account_registered(p->invoke_context) || p->emergency_registration_timer_count > 15) {
+	if (sub && (is_sip_account_registered(p->invoke_context) || p->emergency_registration_timer_count > 15)) {
 		p->emergency_registration_timer_count = 0;
 		chan_voicemngr_start_calling(sub, p->invoke_context_direct);
 	} else {
@@ -5906,7 +5895,7 @@ static int chan_voicemngr_signal_ringing(struct chan_voicemngr_pvt *p)
 {
 	if (channel_config[p->line_id].ringsignal) {
 		struct chan_voicemngr_subchannel *sub = chan_voicemngr_get_active_subchannel(p);
-		if (sub->is_internal == true) {
+		if (sub && sub->is_internal == true) {
 			endpt_signal(p->line_id, "ringing_int", "on", NULL);
 		} else {
 			endpt_signal(p->line_id, "ringing", "on", NULL);
@@ -5931,7 +5920,7 @@ static int chan_voicemngr_signal_ringing_callerid_pending(struct chan_voicemngr_
 {
 	if (channel_config[p->line_id].ringsignal) {
 		struct chan_voicemngr_subchannel *sub = chan_voicemngr_get_active_subchannel(p);
-		if (sub->is_internal == true) {
+		if (sub && sub->is_internal == true) {
 			endpt_signal(p->line_id, "callid_ringing_int", "on", NULL);
 		} else {
 			endpt_signal(p->line_id, "callid_ringing", "on", NULL);
-- 
GitLab