Skip to content

audiobridge: fix double free of buffered packet in participant thread - #3

Open
spscream wants to merge 1 commit into
express/1.4.2-03082026from
fix/audiobridge-bpkt-double-free
Open

audiobridge: fix double free of buffered packet in participant thread#3
spscream wants to merge 1 commit into
express/1.4.2-03082026from
fix/audiobridge-bpkt-double-free

Conversation

@spscream

@spscream spscream commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Что чинит

Падение janus 1.4.2 (сборка d7811fe) с SIGSEGV внутри аллокатора musl. Разбор core dump: креш в janus_audiobridge_participant_thread на g_free() из janus_audiobridge_buffer_packet_destroy() — двойное освобождение буферизованного пакета.

Механика

jitter_buffer_get() записывает packet->data только при успешном чтении (jitter.c:601). На всех неуспешных путях (в частности jitter.c:500-504, когда буфер сброшен и в нём ничего нет) поле data остаётся нетронутым — там лежит указатель на пакет, освобождённый в одной из предыдущих итераций цикла, потому что bpkt после destroy нигде не обнулялся.

Дальше PLC-ветка, увидев participant->decoder == NULL, звала janus_audiobridge_buffer_packet_destroy(bpkt) по этому висячему указателю. Условие decoder == NULL возникает ровно тогда, когда параллельно идёт очистка участника (janus_audiobridge_hangup_media_internal), так что ветка гарантированно попадает в гонку.

Состояние в core, подтверждающее сценарий:

  • jbp = {data = <освобождённый указатель>, len = 0, timestamp = 0, span = 0}span = 0 невозможен при успешном чтении (там было бы 960);
  • jitter->reset_state = 1, все packets[i].data == NULL, lost_count = 0 — последнее чтение ушло в ветку сброса;
  • participant->decoder == NULL, room == NULL, muted == TRUE, encoder == NULL, display == NULL — участник в состоянии после hangup;
  • lost_packets_gap = 14 — PLC-ветка отработала ~280 мс подряд.

Изменения

  • убран вызов janus_audiobridge_buffer_packet_destroy(bpkt) в PLC-ветке — освобождать там нечего по построению;
  • bpkt = NULL после каждого destroy;
  • jbp.data = NULL перед каждым jitter_buffer_get().

Баг присутствует и в upstream master (4602fcc5), там не исправлен.

jitter_buffer_get() only assigns jbp.data when it actually returns a
packet, so a failed read leaves there the pointer to the buffered packet
that was already freed at the end of a previous iteration. The PLC branch
then passed that dangling bpkt to janus_audiobridge_buffer_packet_destroy()
whenever it saw participant->decoder == NULL, i.e. exactly when the
participant was being cleaned up concurrently: the packet got freed a
second time and the process crashed inside the allocator.

There is nothing to free in that branch, so drop the call; clear bpkt
after every destroy and reset jbp.data before each read so that a stale
pointer cannot be used again.
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.

1 participant