QuickJS may defer finalization of a Fetch object until after its native
backing pools have been destroyed. Context reuse is not required: a
local JavaScript cycle is sufficient.
function content(r) {
const response = new Response("body");
response.self = response;
r.return(200);
}
With context reuse disabled, the cycle is collected while
JS_FreeContext() destroys the per-request context. At that point the
engine pool has already been destroyed. The Response finalizer accessed
the pool-backed native structure and attempted to destroy its response
chain, causing a use-after-free. Request finalization had the same
lifetime dependency through its cached Headers value.
The fix introduces a QuickJS-runtime-owned wrapper for Fetch Request and
Response objects. It owns the cached Headers value and keeps only a
non-owning pointer to the pool-backed data. Finalizers now release only
runtime-owned state, while response chains continue to be reclaimed in
bulk with the engine pool.