Fix crash in FinalizationRegistry when the observed object is GC'd

In the pathological case shown in
https://github.com/quickjs-ng/quickjs/issues/367 both the object and the
registry will be destroyed as part of the GC phase of JS_FreeRuntime.
When the GC sweep happens it's possible we are holding on to a corpse so
avoid calling the registry callback in that case.

This is similar to how Weak{Map,Set} deal with iterators being freed as
part of a cycle.

Fixes: https://github.com/quickjs-ng/quickjs/issues/367
This commit is contained in:
Saúl Ibarra Corretgé 2024-04-11 10:57:31 +02:00
parent 325ce95c5e
commit 38fa7d7cf6

View file

@ -52141,11 +52141,16 @@ static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref)
fre = wr->u.fin_rec_entry; fre = wr->u.fin_rec_entry;
JSFinalizationRegistryData *frd = JS_GetOpaque(fre->obj, JS_CLASS_FINALIZATION_REGISTRY); JSFinalizationRegistryData *frd = JS_GetOpaque(fre->obj, JS_CLASS_FINALIZATION_REGISTRY);
assert(frd != NULL); assert(frd != NULL);
/**
* During the GC sweep phase the held object might be collected first.
*/
if (JS_IsLiveObject(frd->ctx->rt, fre->held_val)) {
JSValue func = js_dup(frd->cb); JSValue func = js_dup(frd->cb);
JSValue ret = JS_Call(frd->ctx, func, JS_UNDEFINED, 1, &fre->held_val); JSValue ret = JS_Call(frd->ctx, func, JS_UNDEFINED, 1, &fre->held_val);
JS_FreeValueRT(rt, func); JS_FreeValueRT(rt, func);
JS_FreeValueRT(rt, ret); JS_FreeValueRT(rt, ret);
JS_FreeValueRT(rt, fre->held_val); JS_FreeValueRT(rt, fre->held_val);
}
JS_FreeValueRT(rt, fre->token); JS_FreeValueRT(rt, fre->token);
js_free_rt(rt, fre); js_free_rt(rt, fre);
break; break;