Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Run FinalizationRegistry callback in the job queue #500

Merged
merged 1 commit into from
Sep 9, 2024

Conversation

saghul
Copy link
Contributor

@saghul saghul commented Sep 6, 2024

The spec says HostMakeJobCallback has to be used on the callback: https://tc39.es/ecma262/multipage/managing-memory.html#sec-finalization-registry-cleanup-callback

That makes the following (arguably contrived) example run forever until memory is exhausted.

let count = 0;
function main() {
    console.log(`main! ${++count}`);
    const registry = new FinalizationRegistry(() => {
        globalThis.foo = main();
    });
    registry.register([]);
    registry.register([]);
    return registry;
}
main();

console.log(count);

That is unlike V8, which runs 0 times. This can be explained by the difference in GC implementations and since FinRec makes GC observable, here we are!

Fixes: #432

@saghul saghul force-pushed the finrec-enqueue-job branch 5 times, most recently from 96f3f72 to 59d1dda Compare September 9, 2024 08:41
@saghul
Copy link
Contributor Author

saghul commented Sep 9, 2024

@bnoordhuis THis one should be ready now. I had to dig deep, so pl let me know what you think!

The spec says HostMakeJobCallback has to be used on the callback: https://tc39.es/ecma262/multipage/managing-memory.html#sec-finalization-registry-cleanup-callback

That makes the following (arguably contrived) example run forever until
memory is exhausted.

```js
let count = 0;
function main() {
    console.log(`main! ${++count}`);
    const registry = new FinalizationRegistry(() => {
        globalThis.foo = main();
    });
    registry.register([]);
    registry.register([]);
    return registry;
}
main();

console.log(count);
```

That is unlike V8, which runs 0 times. This can be explained by the
difference in GC implementations and since FinRec makes GC observable,
here we are!

Fixes: #432
@saghul saghul force-pushed the finrec-enqueue-job branch from 59d1dda to 5cb2c21 Compare September 9, 2024 09:05
Copy link
Contributor

@bnoordhuis bnoordhuis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@saghul saghul merged commit 61c8fe6 into master Sep 9, 2024
50 checks passed
@saghul saghul deleted the finrec-enqueue-job branch September 9, 2024 09:32
Comment on lines +53325 to 53332
if (!rt->in_free && (!JS_IsObject(fre->held_val) || JS_IsLiveObject(rt, fre->held_val))) {
JSValue args[2];
args[0] = frd->cb;
args[1] = fre->held_val;
JS_EnqueueJob(frd->ctx, js_finrec_job, 2, args);
}
JS_FreeValueRT(rt, fre->token);
js_free_rt(rt, fre);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to cause #648 and now that I'm looking at it more closely, I believe a spot a bug:

If the branch is not taken (i.e. no job is enqueued), shouldn't it JS_FreeValueRT(rt, fre->held_val)? fre is freed here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly yeah. The object might not be live so I think we need to check for that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Figured it out just now: it should call JS_FreeValueRT(rt, fre->held_val) because JS_EnqueueJob increments the ref count, and js_finrec_job should not call JS_FreeValue(ctx, argv[1]). Pull request incoming.

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Heap-use-after-free in delete_finrec_weakref
2 participants