Skip to content

Commit e1b47b4

Browse files
committed
fix: tsfn use after free in 184e438
1 parent 29aab54 commit e1b47b4

1 file changed

Lines changed: 17 additions & 3 deletions

File tree

packages/emnapi/src/threadsafe-function.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ const enum State {
3838
* ```
3939
*/
4040
const emnapiTSFN = {
41+
_liveSet: {} as Set<number>,
4142
offset: {
4243
__size__: 0,
4344
/* napi_ref */ resource: 0,
@@ -65,6 +66,7 @@ const emnapiTSFN = {
6566
/* int32_t */ cond: 0,
6667
},
6768
init () {
69+
emnapiTSFN._liveSet = new Set<number>()
6870
// #if MEMORY64
6971
emnapiTSFN.offset.__size__ = NapiTSFNOffset64.__size__
7072
emnapiTSFN.offset.resource = NapiTSFNOffset64.async_resource_resource
@@ -530,6 +532,7 @@ const emnapiTSFN = {
530532
}
531533
},
532534
destroy (func: number) {
535+
emnapiTSFN._liveSet.delete(func)
533536
emnapiTSFN.destroyQueue(func)
534537
emnapiTSFN.releaseResources(func)
535538
_free(to64('func') as number)
@@ -748,6 +751,9 @@ const emnapiTSFN = {
748751
// actual TSFN drain after nearby Send/Signal calls have had a chance to
749752
// collapse into the shared AsyncProgressWorker state.
750753
emnapiCtx.features.setImmediate(() => {
754+
if (!emnapiTSFN._liveSet.has(func)) {
755+
return
756+
}
751757
if (Atomics.load(i32a, pending >>> 2) === 0) {
752758
Atomics.store(i32a, scheduled >>> 2, 0)
753759
return
@@ -760,13 +766,20 @@ const emnapiTSFN = {
760766
if (Atomics.exchange(i32a, pending >>> 2, 0) === 0) {
761767
return
762768
}
769+
// After destroy(), the func address is freed. Skip dispatch
770+
// to avoid use-after-free (JS-side lifecycle check).
771+
if (!emnapiTSFN._liveSet.has(func)) {
772+
return
773+
}
763774
emnapiTSFN.dispatch(func)
764775
} finally {
765776
// Allow a later wakeup to schedule a new drain chain. If another
766777
// worker-thread send raced with this drain, enqueue one more turn.
767-
Atomics.store(i32a, scheduled >>> 2, 0)
768-
if (Atomics.load(i32a, pending >>> 2) !== 0) {
769-
emnapiTSFN.enqueue(func)
778+
if (emnapiTSFN._liveSet.has(func)) {
779+
Atomics.store(i32a, scheduled >>> 2, 0)
780+
if (Atomics.load(i32a, pending >>> 2) !== 0) {
781+
emnapiTSFN.enqueue(func)
782+
}
770783
}
771784
}
772785
})
@@ -901,6 +914,7 @@ export function napi_create_threadsafe_function (
901914
makeSetValue('tsfn', 'emnapiTSFN.offset.finalize_cb', 'thread_finalize_cb', '*')
902915
makeSetValue('tsfn', 'emnapiTSFN.offset.call_js_cb', 'call_js_cb', '*')
903916
emnapiCtx.addCleanupHook(envObject, emnapiTSFN.cleanup, tsfn as number)
917+
emnapiTSFN._liveSet.add(tsfn as number)
904918
envObject.ref()
905919

906920
__emnapi_runtime_keepalive_push()

0 commit comments

Comments
 (0)