Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

media.cpp: prevent AudioMediaPlayer destructor from doubly removing port #4

Merged
merged 1 commit into from
Mar 9, 2024

Conversation

ecolsen7
Copy link

@ecolsen7 ecolsen7 commented Mar 8, 2024

The AudioMediaPlayer destructor has an issue where it effectively destroys the player's conference port twice, in a thread-unsafe way that leaves an opportunity for another thread to see a conference port used by a player as free-for-use, and reserve it for other purposes before ultimately being destroyed again, causing crashes.

We see this race condition ~1 in 5 times when attempting to disrupt an ongoing call with a talkdown call, where StopTransmit() is interrupted by the PJSIP thread answering the incoming call.

Here's an example of how this can happen:

AudioMediaPlayer::~AudioMediaPlayer()
{
    if (playerId != PJSUA_INVALID_ID) {
        // Example: media player is using slot 2, aka pjsua_var.player[playerId].slot == 2

        unregisterMediaPort(); <----- calls pjsua_conf_remove_port() which removes slot 2, but does not de-init/clear pjsua_var.player[playerId].slot

        // INCOMING CALL THREAD OCCURS HERE - finds any empty conf port and adds a new port for the incoming call, sees slot 2 is free and sets up port there

        pjsua_player_destroy(playerId); <----- also calls pjsua_conf_remove_port() on pjsua_var.player[playerId].slot, which still == 2
        
        // This removes the now-active talkdown port, program crashes on talkdown connect audio
    }
}

The approach taken in this PR to fix this problem is taken from a pattern used elsewhere in media.cpp presumably to prevent this exact problem - invalidating the AudioMedia id such that unregisterMediaPort() does not attempt to remove the port unnecessarily:

DevAudioMedia::~DevAudioMedia()
{
/* Avoid removing this port (conf port id=0) from conference */
this->id = PJSUA_INVALID_ID;
unregisterMediaPort();
}

Relevant segment of unregisterMediaPort():

void AudioMedia::unregisterMediaPort()
{
if (id != PJSUA_INVALID_ID) {
pjsua_conf_remove_port(id);
id = PJSUA_INVALID_ID;
}

This ensures that the only attempt to remove an AudioMediaPlayer's port occurs in pjsua_player_destroy(), therefore guaranteeing that pjsua_player_destroy() does not delete a now-occupied port mistakenly.

@ecolsen7 ecolsen7 requested a review from darshan-verkada March 8, 2024 21:56
@ecolsen7 ecolsen7 merged commit 9b15029 into intercom/release-2-13 Mar 9, 2024
30 of 54 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants