CVE-2021-41141: Improper Locking (CWE-667)
value: PJSIP is a free and open source multimedia communication library written in the C language implementing standard based protocols such as SIP, SDP, RTP, STUN, TURN, and ICE. In various parts of PJSIP, when error/failure occurs, it is found that the function returns without releasing the currently held locks. This could result in a system deadlock, which cause a denial of service for the users. No release has yet been made which contains the linked fix commit. All versions up to an including 2.11.1 are affected. Users may need to manually apply the patch.
CWE description: The product does not properly acquire or release a lock on a resource, leading to unexpected resource state changes and behaviors.
CVSS v3 base score: 7.5
Published: 2022-01-04T19:15Z
Repository: https://github.com/pjsip/pjproject
Language: C
Files changed: pjmedia/src/pjmedia-codec/and_aud_mediacodec.cpp, pjmedia/src/pjmedia-codec/ipp_codecs.c, pjmedia/src/pjmedia-codec/opus.c, pjmedia/src/pjmedia-codec/passthrough.c, pjmedia/src/pjmedia-codec/speex_codec.c, pjmedia/src/pjmedia/vid_conf.c
Commit date: 2022-01-04 16:58:02 +0800
Commit message: Merge pull request from GHSA-8fmx-hqw7-6gmc


Diff: diff --git a/pjmedia/src/pjmedia-codec/and_aud_mediacodec.cpp b/pjmedia/src/pjmedia-codec/and_aud_mediacodec.cpp
index 2793c6af70..db14b9b456 100644
--- a/pjmedia/src/pjmedia-codec/and_aud_mediacodec.cpp
+++ b/pjmedia/src/pjmedia-codec/and_aud_mediacodec.cpp
@@ -752,10 +752,11 @@ static pj_status_t and_media_alloc_codec(pjmedia_codec_factory *factory,
 	    break;
 	}
     }
     if (idx == -1) {
 	*p_codec = NULL;
+	pj_mutex_unlock(and_media_factory.mutex);
 	return PJMEDIA_CODEC_EFAILED;
     }
 
     /* Create pool for codec instance */
     pool = pjmedia_endpt_create_pool(and_media_factory.endpt, "andmedaud%p",
diff --git a/pjmedia/src/pjmedia-codec/ipp_codecs.c b/pjmedia/src/pjmedia-codec/ipp_codecs.c
index ea5789da37..ccedc9249c 100644
--- a/pjmedia/src/pjmedia-codec/ipp_codecs.c
+++ b/pjmedia/src/pjmedia-codec/ipp_codecs.c
@@ -937,10 +937,11 @@ static pj_status_t ipp_alloc_codec( pjmedia_codec_factory *factory,
 	    break;
 	}
     }
     if (idx == -1) {
 	*p_codec = NULL;
+	pj_mutex_unlock(ipp_factory.mutex);
 	return PJMEDIA_CODEC_EFAILED;
     }
 
     /* Create pool for codec instance */
     pool = pjmedia_endpt_create_pool(ipp_factory.endpt, "IPPcodec", 512, 512);
diff --git a/pjmedia/src/pjmedia-codec/opus.c b/pjmedia/src/pjmedia-codec/opus.c
index d0db1e6fc3..b3ff74ab8e 100644
--- a/pjmedia/src/pjmedia-codec/opus.c
+++ b/pjmedia/src/pjmedia-codec/opus.c
@@ -721,10 +721,11 @@ static pj_status_t  codec_open( pjmedia_codec *codec,
 			    opus_data->cfg.sample_rate,
 			    attr->info.channel_cnt,
 			    OPUS_APPLICATION_VOIP);
     if (err != OPUS_OK) {
 	PJ_LOG(2, (THIS_FILE, "Unable to create encoder"));
+	pj_mutex_unlock (opus_data->mutex);
 	return PJMEDIA_CODEC_EFAILED;
     }
     
     /* Set signal type */
     opus_encoder_ctl(opus_data->enc, OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
@@ -765,10 +766,11 @@ static pj_status_t  codec_open( pjmedia_codec *codec,
     err = opus_decoder_init (opus_data->dec,
 			     opus_data->cfg.sample_rate,
 			     attr->info.channel_cnt);
     if (err != OPUS_OK) {
 	PJ_LOG(2, (THIS_FILE, "Unable to initialize decoder"));
+	pj_mutex_unlock (opus_data->mutex);
 	return PJMEDIA_CODEC_EFAILED;
     }
 
     /* Initialize temporary decode frames used for FEC */
     opus_data->dec_frame[0].type = PJMEDIA_FRAME_TYPE_NONE;
diff --git a/pjmedia/src/pjmedia-codec/passthrough.c b/pjmedia/src/pjmedia-codec/passthrough.c
index 551a848305..4eeb35177b 100644
--- a/pjmedia/src/pjmedia-codec/passthrough.c
+++ b/pjmedia/src/pjmedia-codec/passthrough.c
@@ -623,10 +623,11 @@ static pj_status_t alloc_codec( pjmedia_codec_factory *factory,
 	    break;
 	}
     }
     if (idx == -1) {
 	*p_codec = NULL;
+	pj_mutex_unlock(codec_factory.mutex);
 	return PJMEDIA_CODEC_EUNSUP;
     }
 
     /* Create pool for codec instance */
     pool = pjmedia_endpt_create_pool(codec_factory.endpt, "passthroughcodec",
diff --git a/pjmedia/src/pjmedia-codec/speex_codec.c b/pjmedia/src/pjmedia-codec/speex_codec.c
index 9a35b3767b..113e84f9cd 100644
--- a/pjmedia/src/pjmedia-codec/speex_codec.c
+++ b/pjmedia/src/pjmedia-codec/speex_codec.c
@@ -377,10 +377,11 @@ PJ_DEF(pj_status_t) pjmedia_codec_speex_deinit(void)
     /* Get the codec manager. */
     codec_mgr = pjmedia_endpt_get_codec_mgr(spx_factory.endpt);
     if (!codec_mgr) {
 	pj_pool_release(spx_factory.pool);
 	spx_factory.pool = NULL;
+	pj_mutex_unlock(spx_factory.mutex);
 	return PJ_EINVALIDOP;
     }
 
     /* Unregister Speex codec factory. */
     status = pjmedia_codec_mgr_unregister_factory(codec_mgr,
diff --git a/pjmedia/src/pjmedia/vid_conf.c b/pjmedia/src/pjmedia/vid_conf.c
index 6fa5348507..bf46ce1052 100644
--- a/pjmedia/src/pjmedia/vid_conf.c
+++ b/pjmedia/src/pjmedia/vid_conf.c
@@ -302,20 +302,22 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_add_port( pjmedia_vid_conf *vid_conf,
 	vfi = pjmedia_get_video_format_info(NULL, port->info.fmt.id);
 	if (!vfi) {
 	    PJ_LOG(4,(THIS_FILE, "pjmedia_vid_conf_add_port(): "
 				 "unrecognized format %04X",
 				 port->info.fmt.id));
+	    pj_mutex_unlock(vid_conf->mutex);
 	    return PJMEDIA_EBADFMT;
 	}
 
 	pj_bzero(&vafp, sizeof(vafp));
 	vafp.size = port->info.fmt.det.vid.size;
 	status = (*vfi->apply_fmt)(vfi, &vafp);
 	if (status != PJ_SUCCESS) {
 	    PJ_LOG(4,(THIS_FILE, "pjmedia_vid_conf_add_port(): "
 				 "Failed to apply format %04X",
 				 port->info.fmt.id));
+	    pj_mutex_unlock(vid_conf->mutex);
 	    return status;
 	}
 	if (port->put_frame) {
 	    cport->put_buf_size = vafp.framebytes;
 	    cport->put_buf = pj_pool_zalloc(cport->pool, cport->put_buf_size);
@@ -329,32 +331,45 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_add_port( pjmedia_vid_conf *vid_conf,
     /* Create listener array */
     cport->listener_slots = (unsigned*)
 			    pj_pool_zalloc(pool,
 					   vid_conf->opt.max_slot_cnt *
 					   sizeof(unsigned));
-    PJ_ASSERT_RETURN(cport->listener_slots, PJ_ENOMEM);
+    if (!cport->listener_slots) {	
+	pj_mutex_unlock(vid_conf->mutex);
+	return PJ_ENOMEM;
+    }
 
     /* Create transmitter array */
     cport->transmitter_slots = (unsigned*)
 			       pj_pool_zalloc(pool,
 					      vid_conf->opt.max_slot_cnt *
-					      sizeof(unsigned));
-    PJ_ASSERT_RETURN(cport->transmitter_slots, PJ_ENOMEM);
+					      sizeof(unsigned));    
+    if (!cport->transmitter_slots) {
+	pj_mutex_unlock(vid_conf->mutex);
+	return PJ_ENOMEM;
+    }
 
     /* Create pointer-to-render_state array */
     cport->render_states = (render_state**)
 			   pj_pool_zalloc(pool,
 					  vid_conf->opt.max_slot_cnt *
 					  sizeof(render_state*));
-    PJ_ASSERT_RETURN(cport->render_states, PJ_ENOMEM);
+
+    if (!cport->render_states) {
+	pj_mutex_unlock(vid_conf->mutex);
+	return PJ_ENOMEM;
+    }
 
     /* Create pointer-to-render-pool array */
     cport->render_pool = (pj_pool_t**)
 			 pj_pool_zalloc(pool,
 					vid_conf->opt.max_slot_cnt *
-					sizeof(pj_pool_t*));
-    PJ_ASSERT_RETURN(cport->render_pool, PJ_ENOMEM);
+					sizeof(pj_pool_t*));    
+    if (!cport->render_pool) {
+	pj_mutex_unlock(vid_conf->mutex);
+	return PJ_ENOMEM;
+    }
 
     /* Register the conf port. */
     vid_conf->ports[index] = cport;
     vid_conf->port_cnt++;
 
@@ -418,10 +433,11 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_remove_port( pjmedia_vid_conf *vid_conf,
 
 	/* Warning: will stuck if this is called from the clock thread */
 	status = pjmedia_clock_stop(vid_conf->clock);
 	if (status != PJ_SUCCESS) {
 	    PJ_PERROR(4, (THIS_FILE, status, "Failed to stop clock"));
+	    pj_mutex_unlock(vid_conf->mutex);
 	    return status;
 	}
     }
 
     pj_mutex_unlock(vid_conf->mutex);
@@ -582,10 +598,11 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_connect_port(
 	if (vid_conf->connect_cnt == 1) {
 	    pj_status_t status;
 	    status = pjmedia_clock_start(vid_conf->clock);
 	    if (status != PJ_SUCCESS) {
 		PJ_PERROR(4, (THIS_FILE, status, "Failed to start clock"));
+		pj_mutex_unlock(vid_conf->mutex);
 		return status;
 	    }
 	}
 
 	PJ_LOG(4,(THIS_FILE,"Port %d (%.*s) transmitting to port %d (%.*s)",
@@ -671,10 +688,11 @@ PJ_DEF(pj_status_t) pjmedia_vid_conf_disconnect_port(
 	    pj_status_t status;
 	    /* Warning: will stuck if this is called from the clock thread */
 	    status = pjmedia_clock_stop(vid_conf->clock);
 	    if (status != PJ_SUCCESS) {
 		PJ_PERROR(4, (THIS_FILE, status, "Failed to stop clock"));
+		pj_mutex_unlock(vid_conf->mutex);
 		return status;
 	    }
 	}
 
 	PJ_LOG(4,(THIS_FILE,

Vulnerable code:     PJ_ASSERT_RETURN(cport->listener_slots, PJ_ENOMEM);
					      sizeof(unsigned));
    PJ_ASSERT_RETURN(cport->transmitter_slots, PJ_ENOMEM);
    PJ_ASSERT_RETURN(cport->render_states, PJ_ENOMEM);
					sizeof(pj_pool_t*));
    PJ_ASSERT_RETURN(cport->render_pool, PJ_ENOMEM);
Fixed code: 	pj_mutex_unlock(and_media_factory.mutex);
	pj_mutex_unlock(ipp_factory.mutex);
	pj_mutex_unlock (opus_data->mutex);
	pj_mutex_unlock (opus_data->mutex);
	pj_mutex_unlock(codec_factory.mutex);
	pj_mutex_unlock(spx_factory.mutex);
	    pj_mutex_unlock(vid_conf->mutex);
	    pj_mutex_unlock(vid_conf->mutex);
    if (!cport->listener_slots) {	
	pj_mutex_unlock(vid_conf->mutex);
	return PJ_ENOMEM;
    }
					      sizeof(unsigned));    
    if (!cport->transmitter_slots) {
	pj_mutex_unlock(vid_conf->mutex);
	return PJ_ENOMEM;
    }

    if (!cport->render_states) {
	pj_mutex_unlock(vid_conf->mutex);
	return PJ_ENOMEM;
    }
					sizeof(pj_pool_t*));    
    if (!cport->render_pool) {
	pj_mutex_unlock(vid_conf->mutex);
	return PJ_ENOMEM;
    }
	    pj_mutex_unlock(vid_conf->mutex);
		pj_mutex_unlock(vid_conf->mutex);
		pj_mutex_unlock(vid_conf->mutex);