From 35865bd90cbbf1d11019ea5f2cf344977338c663 Mon Sep 17 00:00:00 2001
From: Anthony Minessale <anthm@freeswitch.org>
Date: Thu, 9 Mar 2017 13:22:11 -0600
Subject: [PATCH] FS-10118: [freeswitch-core] Race conditions from lack of
 error checking in switch_core_session_read_lock #resolve

---
 .../applications/mod_dptools/mod_dptools.c    | 14 +++--
 src/switch_core_media.c                       | 55 +++++++++++++++----
 src/switch_cpp.cpp                            |  3 +-
 src/switch_ivr_async.c                        |  7 ++-
 src/switch_ivr_bridge.c                       | 12 +++-
 src/switch_ivr_originate.c                    |  8 ++-
 6 files changed, 76 insertions(+), 23 deletions(-)

diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c
index 04f35b538f..6daddfcdc5 100644
--- a/src/mod/applications/mod_dptools/mod_dptools.c
+++ b/src/mod/applications/mod_dptools/mod_dptools.c
@@ -3271,14 +3271,22 @@ struct camping_stake {
 static void *SWITCH_THREAD_FUNC camp_music_thread(switch_thread_t *thread, void *obj)
 {
 	struct camping_stake *stake = (struct camping_stake *) obj;
-	switch_core_session_t *session = stake->session;
-	switch_channel_t *channel = switch_core_session_get_channel(stake->session);
+	switch_core_session_t *session;
+	switch_channel_t *channel;
 	const char *moh = stake->moh, *greet = NULL;
 	switch_input_args_t args = { 0 };
 	char dbuf[2] = "";
 	switch_status_t status = SWITCH_STATUS_FALSE;
 	const char *stop;
 
+	session = stake->session;
+
+	if (switch_core_session_read_lock(session) != SWITCH_STATUS_SUCCESS) {
+		return NULL;
+	}
+
+	channel = switch_core_session_get_channel(stake->session);
+
 	if ((stop = switch_channel_get_variable(channel, "campon_stop_key"))) {
 		*dbuf = *stop;
 	}
@@ -3287,8 +3295,6 @@ static void *SWITCH_THREAD_FUNC camp_music_thread(switch_thread_t *thread, void
 	args.buf = dbuf;
 	args.buflen = sizeof(dbuf);
 
-	switch_core_session_read_lock(session);
-
 	/* don't set this to a local_stream:// or you will not be happy */
 	if ((greet = switch_channel_get_variable(channel, "campon_announce_sound"))) {
 		status = switch_ivr_play_file(session, NULL, greet, &args);
diff --git a/src/switch_core_media.c b/src/switch_core_media.c
index 4a6f14ab21..2205fe2534 100644
--- a/src/switch_core_media.c
+++ b/src/switch_core_media.c
@@ -91,6 +91,7 @@ struct media_helper {
 	switch_mutex_t *file_read_mutex;
 	switch_mutex_t *file_write_mutex;
 	int up;
+	int ready;
 };
 
 typedef enum {
@@ -6720,8 +6721,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_start_audio_write_thread(swi
 static void *SWITCH_THREAD_FUNC text_helper_thread(switch_thread_t *thread, void *obj)
 {
 	struct media_helper *mh = obj;
-	switch_core_session_t *session = mh->session;
-	switch_channel_t *channel = switch_core_session_get_channel(session);
+	switch_core_session_t *session;
+	switch_channel_t *channel;
 	switch_status_t status;
 	switch_frame_t *read_frame = NULL;
 	switch_media_handle_t *smh;
@@ -6729,10 +6730,22 @@ static void *SWITCH_THREAD_FUNC text_helper_thread(switch_thread_t *thread, void
 	unsigned char CR[] = TEXT_UNICODE_LINEFEED;
 	switch_frame_t cr_frame = { 0 };
 
+
+	session = mh->session;
+	
+	if (switch_core_session_read_lock(session) != SWITCH_STATUS_SUCCESS) {
+		mh->ready = -1;
+		return NULL;
+	}
+
+	mh->ready = 1;
+
 	if (!(smh = session->media_handle)) {
 		return NULL;
 	}
 
+	channel = switch_core_session_get_channel(session);
+
 	if (switch_channel_var_true(session->channel, "fire_text_events")) {
 		switch_channel_set_flag(session->channel, CF_FIRE_TEXT_EVENTS);
 	}
@@ -6743,8 +6756,6 @@ static void *SWITCH_THREAD_FUNC text_helper_thread(switch_thread_t *thread, void
 	t_engine = &smh->engines[SWITCH_MEDIA_TYPE_TEXT];
 	t_engine->thread_id = switch_thread_self();
 
-	switch_core_session_read_lock(session);
-
 	mh->up = 1;
 
 	switch_core_media_check_dtls(session, SWITCH_MEDIA_TYPE_TEXT);
@@ -6845,7 +6856,15 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_start_text_thread(switch_cor
 	//switch_mutex_init(&t_engine->mh.file_write_mutex, SWITCH_MUTEX_NESTED, pool);
 	//switch_mutex_init(&smh->read_mutex[SWITCH_MEDIA_TYPE_TEXT], SWITCH_MUTEX_NESTED, pool);
 	//switch_mutex_init(&smh->write_mutex[SWITCH_MEDIA_TYPE_TEXT], SWITCH_MUTEX_NESTED, pool);
-	switch_thread_create(&t_engine->media_thread, thd_attr, text_helper_thread, &t_engine->mh, switch_core_session_get_pool(session));
+
+	t_engine->mh.ready = 0;
+
+	if (switch_thread_create(&t_engine->media_thread, thd_attr, text_helper_thread, &t_engine->mh, 
+							 switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) {
+		while(!t_engine->mh.ready) {
+			switch_cond_next();
+		}
+	}
 
 	switch_mutex_unlock(smh->control_mutex);
 	return SWITCH_STATUS_SUCCESS;
@@ -6854,8 +6873,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_start_text_thread(switch_cor
 static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, void *obj)
 {
 	struct media_helper *mh = obj;
-	switch_core_session_t *session = mh->session;
-	switch_channel_t *channel = switch_core_session_get_channel(session);
+	switch_core_session_t *session;
+	switch_channel_t *channel;
 	switch_status_t status;
 	switch_frame_t *read_frame = NULL;
 	switch_media_handle_t *smh;
@@ -6869,10 +6888,21 @@ static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, voi
 	int buflen = SWITCH_RTP_MAX_BUF_LEN;
 	int blank_enabled = 1;
 
+	session = mh->session;
+	
+	if (switch_core_session_read_lock(session) != SWITCH_STATUS_SUCCESS) {
+		mh->ready = -1;
+		return NULL;
+	}
+
+	mh->ready = 1;
+
 	if (!(smh = session->media_handle)) {
 		return NULL;
 	}
 
+	channel = switch_core_session_get_channel(session);
+
 	switch_core_autobind_cpu();
 
 	if ((var = switch_channel_get_variable(session->channel, "core_video_blank_image"))) {
@@ -6895,8 +6925,6 @@ static void *SWITCH_THREAD_FUNC video_helper_thread(switch_thread_t *thread, voi
 	v_engine = &smh->engines[SWITCH_MEDIA_TYPE_VIDEO];
 	v_engine->thread_id = switch_thread_self();
 
-	switch_core_session_read_lock(session);
-
 	mh->up = 1;
 	switch_mutex_lock(mh->cond_mutex);
 
@@ -7052,7 +7080,14 @@ SWITCH_DECLARE(switch_status_t) switch_core_session_start_video_thread(switch_co
 	switch_mutex_init(&v_engine->mh.file_write_mutex, SWITCH_MUTEX_NESTED, pool);
 	switch_mutex_init(&smh->read_mutex[SWITCH_MEDIA_TYPE_VIDEO], SWITCH_MUTEX_NESTED, pool);
 	switch_mutex_init(&smh->write_mutex[SWITCH_MEDIA_TYPE_VIDEO], SWITCH_MUTEX_NESTED, pool);
-	switch_thread_create(&v_engine->media_thread, thd_attr, video_helper_thread, &v_engine->mh, switch_core_session_get_pool(session));
+	v_engine->mh.ready = 0;
+
+	if (switch_thread_create(&v_engine->media_thread, thd_attr, video_helper_thread, &v_engine->mh, 
+							 switch_core_session_get_pool(session)) == SWITCH_STATUS_SUCCESS) {
+		while(!v_engine->mh.ready) {
+			switch_cond_next();
+		}
+	}
 
 	switch_mutex_unlock(smh->control_mutex);
 	return SWITCH_STATUS_SUCCESS;
diff --git a/src/switch_cpp.cpp b/src/switch_cpp.cpp
index 9cf2df3614..bec23291fb 100644
--- a/src/switch_cpp.cpp
+++ b/src/switch_cpp.cpp
@@ -635,11 +635,10 @@ SWITCH_DECLARE_CONSTRUCTOR CoreSession::CoreSession(switch_core_session_t *new_s
 {
 	init_vars();
 
-	if (new_session) {
+	if (new_session && switch_core_session_read_lock_hangup(new_session) == SWITCH_STATUS_SUCCESS) {
 		session = new_session;
 		channel = switch_core_session_get_channel(session);
 		allocated = 1;
-		switch_core_session_read_lock_hangup(session);
 		uuid = strdup(switch_core_session_get_uuid(session));
 	}
 }
diff --git a/src/switch_ivr_async.c b/src/switch_ivr_async.c
index 103effa575..2c6ed39446 100644
--- a/src/switch_ivr_async.c
+++ b/src/switch_ivr_async.c
@@ -3990,9 +3990,10 @@ static void *SWITCH_THREAD_FUNC bcast_thread(switch_thread_t *thread, void *obj)
 		return NULL;
 	}
 
-	switch_core_session_read_lock(bch->session);
-	switch_ivr_broadcast(switch_core_session_get_uuid(bch->session), bch->app, bch->flags);
-	switch_core_session_rwunlock(bch->session);
+	if (switch_core_session_read_lock(bch->session) == SWITCH_STATUS_SUCCESS) {
+		switch_ivr_broadcast(switch_core_session_get_uuid(bch->session), bch->app, bch->flags);
+		switch_core_session_rwunlock(bch->session);
+	}
 
 	return NULL;
 
diff --git a/src/switch_ivr_bridge.c b/src/switch_ivr_bridge.c
index e229ee7a44..98d446ad8a 100644
--- a/src/switch_ivr_bridge.c
+++ b/src/switch_ivr_bridge.c
@@ -158,8 +158,16 @@ static void video_bridge_thread(switch_core_session_t *session, void *obj)
 
 	vh->up = 1;
 
-	switch_core_session_read_lock(vh->session_a);
-	switch_core_session_read_lock(vh->session_b);
+	if (switch_core_session_read_lock(vh->session_a) != SWITCH_STATUS_SUCCESS) {
+		vh->up = 0;
+		return;
+	}
+
+	if (switch_core_session_read_lock(vh->session_b) != SWITCH_STATUS_SUCCESS) {
+		vh->up = 0;
+		switch_core_session_rwunlock(vh->session_a);
+		return;
+	}
 
 	switch_core_session_request_video_refresh(vh->session_a);
 	switch_core_session_request_video_refresh(vh->session_b);
diff --git a/src/switch_ivr_originate.c b/src/switch_ivr_originate.c
index 9f3f5d099f..6016809fd2 100644
--- a/src/switch_ivr_originate.c
+++ b/src/switch_ivr_originate.c
@@ -1428,10 +1428,14 @@ static void *SWITCH_THREAD_FUNC enterprise_originate_ringback_thread(switch_thre
 {
 	struct ent_originate_ringback *rb_data = (struct ent_originate_ringback *) obj;
 	switch_core_session_t *session = rb_data->session;
-	switch_channel_t *channel = switch_core_session_get_channel(rb_data->session);
+	switch_channel_t *channel;
 	switch_status_t status = SWITCH_STATUS_FALSE;
 
-	switch_core_session_read_lock(session);
+	if (switch_core_session_read_lock(session) != SWITCH_STATUS_SUCCESS) {
+		return NULL;
+	}
+
+	channel = switch_core_session_get_channel(session);
 
 	while (rb_data->running && switch_channel_ready(channel)) {
 		switch_ivr_parse_all_messages(session);