From 679e18f35058d4b4fcf227f463c9dc2ea8d68966 Mon Sep 17 00:00:00 2001 From: LautaroPetaccio Date: Wed, 1 Apr 2026 10:32:03 -0300 Subject: [PATCH 1/3] fix: clear pending timeout in AudioSource.close() --- packages/livekit-rtc/src/audio_source.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/livekit-rtc/src/audio_source.ts b/packages/livekit-rtc/src/audio_source.ts index 16096b87..059adce1 100644 --- a/packages/livekit-rtc/src/audio_source.ts +++ b/packages/livekit-rtc/src/audio_source.ts @@ -145,6 +145,12 @@ export class AudioSource { } async close() { + // Clear any pending playout timeout so its callback doesn't fire after + // the handle is disposed, which would reference freed native state. + if (this.timeout) { + clearTimeout(this.timeout); + this.timeout = undefined; + } this.ffiHandle.dispose(); this.closed = true; } From d8529eaf847faf7ec873269844e6eb41c1fd7091 Mon Sep 17 00:00:00 2001 From: LautaroPetaccio Date: Wed, 1 Apr 2026 11:37:04 -0300 Subject: [PATCH 2/3] chore: add changeset --- .changeset/neat-pens-yawn.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/neat-pens-yawn.md diff --git a/.changeset/neat-pens-yawn.md b/.changeset/neat-pens-yawn.md new file mode 100644 index 00000000..a7434921 --- /dev/null +++ b/.changeset/neat-pens-yawn.md @@ -0,0 +1,5 @@ +--- +'@livekit/rtc-node': patch +--- + +Clear pending timeout in AudioSource.close() to prevent use-after-free From 3c58cf82836443b51bb5fb7683ba552db5af8860 Mon Sep 17 00:00:00 2001 From: LautaroPetaccio Date: Wed, 1 Apr 2026 11:45:48 -0300 Subject: [PATCH 3/3] fix: call release() in close() to unblock pending waitForPlayout() --- packages/livekit-rtc/src/audio_source.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/livekit-rtc/src/audio_source.ts b/packages/livekit-rtc/src/audio_source.ts index 059adce1..7ffe9dbc 100644 --- a/packages/livekit-rtc/src/audio_source.ts +++ b/packages/livekit-rtc/src/audio_source.ts @@ -151,6 +151,8 @@ export class AudioSource { clearTimeout(this.timeout); this.timeout = undefined; } + // Resolve any pending waitForPlayout() promise so callers don't hang. + this.release(); this.ffiHandle.dispose(); this.closed = true; }