Skip to content

Possible crash in GroupCallManager::get_recent_speakers during leaveGroupCall #3709

Description

@sosmx

TDLib may crash with SIGABRT while leaving a group call.

The crash happens inside:

td::GroupCallManager::get_recent_speakers()

during the following flow:

leaveGroupCall
-> send_update_group_call
-> get_recent_speakers
-> process_check_error
-> abort()

This causes the entire process using TDLib to terminate.

Crash stack trace (simplified)

td::process_fatal_error()
td::LogInterface::append()
td::Logger::~Logger()
td::detail::process_check_error()
td::GroupCallManager::get_recent_speakers()
td::GroupCallManager::send_update_group_call()
td::GroupCallManager::leave_group_call()
td::Requests::on_request()
td::Td::run_request()

Environment

  • TDLib version: 1.8.65 / 1.8.66
  • Platform: Linux x86_64
  • Usage: long-running application with multiple TDLib clients and group calls

Observed behavior

The crash was reproduced multiple times with the same stack trace.

The failure occurs while processing leaveGroupCall.

The application itself does not intentionally call internal TDLib functions involved in the crash path; the flow can also be triggered by TDLib internal state updates.

Suspected location

td/telegram/GroupCallManager.cpp

Function:

vector<td_api::object_ptr<td_api::groupCallRecentSpeaker>> GroupCallManager::get_recent_speakers(
const GroupCall *group_call, bool for_update)

The function contains assumptions enforced by CHECK:

CHECK(group_call != nullptr && group_call->is_inited);

and:

CHECK(recent_speakers != nullptr);

A failure of either condition results in SIGABRT.

Temporary workaround

Replacing the fatal checks with defensive returns prevents the whole process from terminating:

if (group_call == nullptr || !group_call->is_inited) {
return Auto();
}
if (recent_speakers == nullptr) {
return Auto();
}

After applying this change, the application continues running instead of aborting.

Question

Could this indicate an internal TDLib state synchronization issue around:

  • leave_group_call()
  • on_call_state_updated()
  • group_call_recent_speakers_

Should these cases be handled gracefully instead of using fatal CHECK assertions?

Any guidance on the expected invariant or a proper fix would be appreciated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions