From 8b8a3dfe660cea125bec1c2f89765ae00a1ab87b Mon Sep 17 00:00:00 2001
From: Simon Morlat <simon.morlat@linphone.org>
Date: Mon, 17 Mar 2025 18:25:31 +0100
Subject: [PATCH] Fix a crash when a handling CallSession's pendingActions, fix
 a crash when linphone_core_get_user_agent() is called while Core is in
 stopped state.

---
 coreapi/linphonecore.c                   |  4 ++++
 src/call/call.cpp                        | 15 +++++++++------
 src/call/call.h                          |  6 +++---
 src/conference/session/call-session.cpp  | 17 +++++++++++++----
 src/conference/session/media-session.cpp |  9 ++++++---
 tester/call_ice_tester.cpp               |  4 ++--
 6 files changed, 37 insertions(+), 18 deletions(-)

diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c
index ec4fd0b755..8bc21c4a09 100644
--- a/coreapi/linphonecore.c
+++ b/coreapi/linphonecore.c
@@ -4157,6 +4157,10 @@ void linphone_core_set_user_agent(LinphoneCore *lc, const char *name, const char
 	}
 }
 const char *linphone_core_get_user_agent(LinphoneCore *lc) {
+	CoreLogContextualizer logContextualizer(lc);
+	if (!lc->sal) {
+		return linphone_config_get_string(lc->config, "sip", "user_agent", "");
+	}
 	return lc->sal->getUserAgent().c_str();
 }
 
diff --git a/src/call/call.cpp b/src/call/call.cpp
index 4b698114b5..a0580811cc 100644
--- a/src/call/call.cpp
+++ b/src/call/call.cpp
@@ -1229,20 +1229,23 @@ const string &Call::getReferTo() const {
 	return getActiveSession()->getReferTo();
 }
 
-const std::shared_ptr<Address> Call::getReferToAddress() const {
-	return getActiveSession()->getReferToAddress();
+std::shared_ptr<Address> Call::getReferToAddress() const {
+	auto session = getActiveSession();
+	return session ? session->getReferToAddress() : nullptr;
 }
 
 std::shared_ptr<const Address> Call::getReferredBy() const {
 	return getActiveSession()->getReferredBy();
 }
 
-const std::shared_ptr<Address> Call::getRemoteAddress() const {
-	return getActiveSession()->getRemoteAddress();
+std::shared_ptr<Address> Call::getRemoteAddress() const {
+	auto session = getActiveSession();
+	return session ? session->getRemoteAddress() : nullptr;
 }
 
-const std::shared_ptr<Address> Call::getRemoteContactAddress() const {
-	return getActiveSession()->getRemoteContactAddress();
+std::shared_ptr<Address> Call::getRemoteContactAddress() const {
+	auto session = getActiveSession();
+	return session ? session->getRemoteContactAddress() : nullptr;
 }
 
 const string &Call::getRemoteContact() const {
diff --git a/src/call/call.h b/src/call/call.h
index 0103215d21..cdaa80f1cc 100644
--- a/src/call/call.h
+++ b/src/call/call.h
@@ -170,10 +170,10 @@ public:
 	float getRecordVolume() const;
 	std::shared_ptr<Call> getReferer() const;
 	const std::string &getReferTo() const;
-	const std::shared_ptr<Address> getReferToAddress() const;
+	std::shared_ptr<Address> getReferToAddress() const;
 	std::shared_ptr<const Address> getReferredBy() const;
-	const std::shared_ptr<Address> getRemoteAddress() const;
-	const std::shared_ptr<Address> getRemoteContactAddress() const;
+	std::shared_ptr<Address> getRemoteAddress() const;
+	std::shared_ptr<Address> getRemoteContactAddress() const;
 	const std::string &getRemoteContact() const;
 	const MediaSessionParams *getRemoteParams() const;
 	const std::string &getRemoteUserAgent();
diff --git a/src/conference/session/call-session.cpp b/src/conference/session/call-session.cpp
index 6e71a5aae8..b6045f1bb3 100644
--- a/src/conference/session/call-session.cpp
+++ b/src/conference/session/call-session.cpp
@@ -217,7 +217,12 @@ void CallSessionPrivate::setState(CallSession::State newState, const string &mes
 void CallSessionPrivate::onCallStateChanged(BCTBX_UNUSED(LinphoneCall *call),
                                             BCTBX_UNUSED(LinphoneCallState state),
                                             BCTBX_UNUSED(const std::string &message)) {
-	this->executePendingActions();
+	L_Q();
+	auto zis = q->getSharedFromThis();
+	q->getCore()->doLater([zis, this] {
+		(void)zis;
+		this->executePendingActions();
+	});
 }
 
 void CallSessionPrivate::executePendingActions() {
@@ -226,19 +231,23 @@ void CallSessionPrivate::executePendingActions() {
 	if (networkReachable && (state != CallSession::State::End) && (state != CallSession::State::Released) &&
 	    (state != CallSession::State::Error)) {
 		std::queue<std::function<LinphoneStatus()>> unsuccessfulActions;
-		auto copyPendingActions = pendingActions;
+		std::queue<std::function<LinphoneStatus()>> copyPendingActions;
+		copyPendingActions.swap(pendingActions);
+
 		while (copyPendingActions.empty() == false) {
 			// Store std::function in a temporary variable in order to take it out of the queue before executing it
 			const auto f = copyPendingActions.front();
 			copyPendingActions.pop();
-			pendingActions.pop();
 			// Execute method
 			const auto result = f();
 			if (result != 0) {
 				unsuccessfulActions.push(f);
 			}
 		}
-		pendingActions = unsuccessfulActions;
+		while (!unsuccessfulActions.empty()) {
+			pendingActions.push(unsuccessfulActions.front());
+			unsuccessfulActions.pop();
+		}
 	}
 }
 
diff --git a/src/conference/session/media-session.cpp b/src/conference/session/media-session.cpp
index 932dff77cf..6bd8a71543 100644
--- a/src/conference/session/media-session.cpp
+++ b/src/conference/session/media-session.cpp
@@ -4610,9 +4610,12 @@ bool MediaSession::initiateOutgoing(const string &subject, const std::shared_ptr
 				        << " because ICE candidates must be gathered first";
 				d->queueIceGatheringTask([this, subject, content]() {
 					L_D();
-					if (d->state != CallSession::State::End) // Call has been terminated while gathering: avoid to
-					                                         // update descriptions.
-						d->updateLocalMediaDescriptionFromIce(d->localIsOfferer);
+					if (d->state != CallSession::State::OutgoingInit) {
+						lInfo() << "Ice gathering done but call is now in [" << Utils::toString(d->state)
+						        << "], nothing to do.";
+						return 0;
+					}
+					d->updateLocalMediaDescriptionFromIce(d->localIsOfferer);
 					startInvite(nullptr, subject, content);
 					return 0;
 				});
diff --git a/tester/call_ice_tester.cpp b/tester/call_ice_tester.cpp
index 782b48f8eb..f626fd4541 100644
--- a/tester/call_ice_tester.cpp
+++ b/tester/call_ice_tester.cpp
@@ -120,8 +120,8 @@ static void _early_media_call_with_ice(bool_t callee_has_ice) {
 	pauline_call = linphone_core_invite_address(pauline->lc, marie->identity);
 
 	BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallIncomingReceived, 1, 10000));
-	BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallIncomingEarlyMedia, 1, 3000));
-	BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallOutgoingEarlyMedia, 1, 3000));
+	BC_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallIncomingEarlyMedia, 1, 10000));
+	BC_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallOutgoingEarlyMedia, 1, 10000));
 	BC_ASSERT_TRUE(linphone_call_get_all_muted(pauline_call));
 
 	marie_call = linphone_core_get_current_call(marie->lc);
-- 
GitLab