Skip to content

Commit b0cc8cc

Browse files
committed
fix: tsfn use after free
1 parent e2ba931 commit b0cc8cc

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
@@ -22,6 +22,7 @@ const enum State {
2222
* ```
2323
*/
2424
export const emnapiTSFN = {
25+
_liveSet: {} as Set<number>,
2526
offset: {
2627
__size__: 0,
2728
/* napi_ref */ resource: 0,
@@ -49,6 +50,7 @@ export const emnapiTSFN = {
4950
/* int32_t */ cond: 0
5051
},
5152
init () {
53+
emnapiTSFN._liveSet = new Set<number>()
5254
// #if MEMORY64
5355
emnapiTSFN.offset.__size__ = NapiTSFNOffset64.__size__
5456
emnapiTSFN.offset.resource = NapiTSFNOffset64.async_resource_resource
@@ -514,6 +516,7 @@ export const emnapiTSFN = {
514516
}
515517
},
516518
destroy (func: number) {
519+
emnapiTSFN._liveSet.delete(func)
517520
emnapiTSFN.destroyQueue(func)
518521
emnapiTSFN.releaseResources(func)
519522
_free(to64('func') as number)
@@ -735,6 +738,9 @@ export const emnapiTSFN = {
735738
// actual TSFN drain after nearby Send/Signal calls have had a chance to
736739
// collapse into the shared AsyncProgressWorker state.
737740
emnapiCtx.feature.setImmediate(() => {
741+
if (!emnapiTSFN._liveSet.has(func)) {
742+
return
743+
}
738744
if (Atomics.load(i32a, pending >>> 2) === 0) {
739745
Atomics.store(i32a, scheduled >>> 2, 0)
740746
return
@@ -747,13 +753,20 @@ export const emnapiTSFN = {
747753
if (Atomics.exchange(i32a, pending >>> 2, 0) === 0) {
748754
return
749755
}
756+
// After destroy(), the func address is freed. Skip dispatch
757+
// to avoid use-after-free (JS-side lifecycle check).
758+
if (!emnapiTSFN._liveSet.has(func)) {
759+
return
760+
}
750761
emnapiTSFN.dispatch(func)
751762
} finally {
752763
// Allow a later wakeup to schedule a new drain chain. If another
753764
// worker-thread send raced with this drain, enqueue one more turn.
754-
Atomics.store(i32a, scheduled >>> 2, 0)
755-
if (Atomics.load(i32a, pending >>> 2) !== 0) {
756-
emnapiTSFN.enqueue(func)
765+
if (emnapiTSFN._liveSet.has(func)) {
766+
Atomics.store(i32a, scheduled >>> 2, 0)
767+
if (Atomics.load(i32a, pending >>> 2) !== 0) {
768+
emnapiTSFN.enqueue(func)
769+
}
757770
}
758771
}
759772
})
@@ -882,6 +895,7 @@ export function napi_create_threadsafe_function (
882895
makeSetValue('tsfn', 'emnapiTSFN.offset.finalize_cb', 'thread_finalize_cb', '*')
883896
makeSetValue('tsfn', 'emnapiTSFN.offset.call_js_cb', 'call_js_cb', '*')
884897
emnapiCtx.addCleanupHook(envObject, emnapiTSFN.cleanup, tsfn)
898+
emnapiTSFN._liveSet.add(tsfn as number)
885899
envObject.ref()
886900

887901
_emnapi_runtime_keepalive_push()

0 commit comments

Comments
 (0)