Summary
AsyncSRT.dispose() (which calls Worker.terminate() under the hood — see src/async.js) causes native heap corruption when called on a worker that has active or recently-closed SRT sockets. This reproduces on both v1.5.5 and v1.5.6 of the bundled libSRT — it is unrelated to the version-bump work in #89/#90.
This code path currently has no test coverage: dispose()/worker.terminate() is never called in spec/async_srt_await_spec.js (it's commented out) and is absent from every other spec file.
Reproduction
In a spec exercising AsyncSRT: create socket(s), close them via the async API, then call dispose() on the AsyncSRT instance (which calls Worker.terminate()) before the process exits.
Observed on Linux (node:20-bookworm, arm64, matching CI's toolchain):
and, on other runs of the same scenario:
free(): invalid next size (fast)
and one occurrence of a V8 fatal error during worker teardown:
Check failed: node->IsInUse().
All are consistent with a race between Worker.terminate() tearing down the worker thread/its Realm and the native addon's own in-flight close()/socket-teardown state still running inside that same worker thread — i.e. terminate() doesn't wait for the native binding's cleanup to finish before the thread (and its heap) is torn down.
Why this matters
Heap corruption (free(): invalid pointer / invalid next size) is a use-after-free/double-free class bug — exploitable in principle, not just a crash. Any application using AsyncSRT.dispose() for graceful shutdown (a reasonable, expected use of a public API method) can hit this today, independent of which libSRT version is bundled.
Suggested fix direction
Worker.terminate() is inherently abrupt — it doesn't let in-flight work finish. A safer dispose() likely needs to:
- signal the worker to finish any pending native
close()/socket teardown and acknowledge completion (e.g. a message round-trip) before calling worker.terminate(), or
- avoid
terminate() entirely in favor of a graceful shutdown message the worker acts on and then exits on its own.
Suggested test coverage
Un-comment and fix the existing dispose() call in spec/async_srt_await_spec.js, and add explicit close+dispose coverage to the other async spec files, so this path is exercised by CI going forward.
Found incidentally while investigating #89/#90 (libSRT v1.5.6 CVE fix); filed separately since it's unrelated to that work and affects the current release.
Summary
AsyncSRT.dispose()(which callsWorker.terminate()under the hood — seesrc/async.js) causes native heap corruption when called on a worker that has active or recently-closed SRT sockets. This reproduces on bothv1.5.5andv1.5.6of the bundled libSRT — it is unrelated to the version-bump work in #89/#90.This code path currently has no test coverage:
dispose()/worker.terminate()is never called inspec/async_srt_await_spec.js(it's commented out) and is absent from every other spec file.Reproduction
In a spec exercising
AsyncSRT: create socket(s), close them via the async API, then calldispose()on theAsyncSRTinstance (which callsWorker.terminate()) before the process exits.Observed on Linux (
node:20-bookworm, arm64, matching CI's toolchain):and, on other runs of the same scenario:
and one occurrence of a V8 fatal error during worker teardown:
All are consistent with a race between
Worker.terminate()tearing down the worker thread/itsRealmand the native addon's own in-flightclose()/socket-teardown state still running inside that same worker thread — i.e.terminate()doesn't wait for the native binding's cleanup to finish before the thread (and its heap) is torn down.Why this matters
Heap corruption (
free(): invalid pointer/invalid next size) is a use-after-free/double-free class bug — exploitable in principle, not just a crash. Any application usingAsyncSRT.dispose()for graceful shutdown (a reasonable, expected use of a public API method) can hit this today, independent of which libSRT version is bundled.Suggested fix direction
Worker.terminate()is inherently abrupt — it doesn't let in-flight work finish. A saferdispose()likely needs to:close()/socket teardown and acknowledge completion (e.g. a message round-trip) before callingworker.terminate(), orterminate()entirely in favor of a graceful shutdown message the worker acts on and then exits on its own.Suggested test coverage
Un-comment and fix the existing
dispose()call inspec/async_srt_await_spec.js, and add explicit close+dispose coverage to the other async spec files, so this path is exercised by CI going forward.Found incidentally while investigating #89/#90 (libSRT v1.5.6 CVE fix); filed separately since it's unrelated to that work and affects the current release.