[MBI] V8TaskRunner Audit. Can it run arbitrary JavaScript?

4 views
Skip to first unread message

Minoru Chikamune

unread,
Feb 18, 2021, 7:56:13 AM2/18/21
to scheduler-dev, Blink Isolation discussions, platform-architecture-dev

Hi,


A Chrome renderer process has a process-global V8TaskRunner. It was unclear if the V8TaskRunner was purely for scheduling GC tasks, or if it could run arbitrary JavaScript code which can mutate per-ASG data.


After analysis, it turns out that V8TaskRunner can indeed run arbitrary JavaScript code via FinalizationRegistry API, suggesting that it should be migrated to per-AgentSchedulingGroup.


Please check the document below if you are interested.

https://docs.google.com/document/d/1KmCwYB7dcx4021aw4E7h9uYB1WlxhriGAd-mJzpTXQY/edit#heading=h.ufga5mrz8dlv


Kentaro Hara

unread,
Feb 18, 2021, 8:25:36 AM2/18/21
to Minoru Chikamune, scheduler-dev, Blink Isolation discussions, platform-architecture-dev
Thanks Minoru-san for the doc!

Do you (or does anyone) know how the behavior of FinalizationRegistry callbacks is standardized? Per the spec, what task runner should be used?

I'm asking this because I'm not sure if it's realistic to run FinalizationRegistry callbacks in per-ASG task runners. If we want to do this, V8 needs to repost a task every time it invokes callbacks. The overhead won't be acceptable... Or am I missing something?


--
You received this message because you are subscribed to the Google Groups "Blink Isolation discussions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blink-isolation...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-isolation-dev/CAL4s8FH9ruzbMej%3D7g6ok_OLX3220sDFEnesiFixpYdYcqHGrQ%40mail.gmail.com.


--
Kentaro Hara, Tokyo

Michael Lippautz

unread,
Feb 18, 2021, 8:42:58 AM2/18/21
to Kentaro Hara, s...@chromium.org, ma...@chromium.org, Minoru Chikamune, scheduler-dev, Blink Isolation discussions, platform-architecture-dev
+s...@chromium.org, +ma...@chromium.org for FinalizationRegistry knowledge

On Thu, Feb 18, 2021 at 2:25 PM Kentaro Hara <har...@chromium.org> wrote:
Thanks Minoru-san for the doc!

Do you (or does anyone) know how the behavior of FinalizationRegistry callbacks is standardized? Per the spec, what task runner should be used?

I'm asking this because I'm not sure if it's realistic to run FinalizationRegistry callbacks in per-ASG task runners. If we want to do this, V8 needs to repost a task every time it invokes callbacks. The overhead won't be acceptable... Or am I missing something?


IIUC, callbacks are collected and there's one task per GC cycle that dispatches all of the FinalizationRegistry cleanups in their respective contexts.
 

On Thu, Feb 18, 2021 at 9:56 PM 'Minoru Chikamune' via Blink Isolation discussions <blink-iso...@chromium.org> wrote:

Hi,


A Chrome renderer process has a process-global V8TaskRunner. It was unclear if the V8TaskRunner was purely for scheduling GC tasks, or if it could run arbitrary JavaScript code which can mutate per-ASG data.


After analysis, it turns out that V8TaskRunner can indeed run arbitrary JavaScript code via FinalizationRegistry API, suggesting that it should be migrated to per-AgentSchedulingGroup.


Please check the document below if you are interested.

https://docs.google.com/document/d/1KmCwYB7dcx4021aw4E7h9uYB1WlxhriGAd-mJzpTXQY/edit#heading=h.ufga5mrz8dlv


--
You received this message because you are subscribed to the Google Groups "Blink Isolation discussions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blink-isolation...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/blink-isolation-dev/CAL4s8FH9ruzbMej%3D7g6ok_OLX3220sDFEnesiFixpYdYcqHGrQ%40mail.gmail.com.


--
Kentaro Hara, Tokyo

--
You received this message because you are subscribed to the Google Groups "Blink Isolation discussions" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blink-isolation...@chromium.org.

Kentaro Hara

unread,
Feb 18, 2021, 8:48:24 AM2/18/21
to Michael Lippautz, s...@chromium.org, Marja Hölttä, Minoru Chikamune, scheduler-dev, Blink Isolation discussions, platform-architecture-dev
IIUC, callbacks are collected and there's one task per GC cycle that dispatches all of the FinalizationRegistry cleanups in their respective contexts.

That matches my understanding. V8 only enters v8::Context before invoking the callback.

If we want to make the callback use a correct per-ASG task runner, we need to repost a task per callback. This doesn't sound realistic to me.

I'm curious about the expected behavior in the spec :)


Shu-yu Guo

unread,
Feb 18, 2021, 1:12:27 PM2/18/21
to Kentaro Hara, Michael Lippautz, Marja Hölttä, Minoru Chikamune, scheduler-dev, Blink Isolation discussions, platform-architecture-dev
Hi all,

The JS spec side of posting tasks (called a Job in JS spec-ese) is here. The HTML integration PR that describes task queuing behavior is here. We should get that PR merged, which was blocked on sorting out incumbent settings object stuff, which we finally did. The semantics:
  • The task source is currently named the "garbage collection task source", but from this comment we will rename it to something like "JS engine task source" for all tasks posted by the JS engine and by features specified in ECMAScript. 
  • One task is posted per FinalizationRegistry, not per callback invocation, not one task for all FinalizationRegistries. Each FinalizationRegistry may invoke its callback many times, on each dead cell, in a single task. These callbacks are called in a loop over some or all of the dead cells, and the correct v8::Context should be entered before entering into this loop.
  • Note that the specced one-task-per-FinalizationRegistry behavior is not observable. Even though it looks like the spec posts separate tasks and the relative ordering of those tasks when interleaved with Atomics.waitAsync tasks on the same task source would be observable, the JS spec gives significant leeway for when these finalization tasks are posted. The JS engine can choose to post all FinalizationRegistry tasks back-to-back, which would be observably equivalent to coalescing tasks. That said it doesn't seem desirable to schedule them all within one task in case finalization takes too long.
  • There is no spec guarantee that each task must run the FinalizationRegistry callback on all its dead cells. If a task doesn't want to process all dead cells in a FinalizationRegistry, it should requeue a task. This is to give implementations flexibility in budgeting time for these tasks.

Shu-yu Guo

unread,
Feb 18, 2021, 1:15:01 PM2/18/21
to Kentaro Hara, Michael Lippautz, Marja Hölttä, Minoru Chikamune, scheduler-dev, Blink Isolation discussions, platform-architecture-dev
I misspoke on one point above: the coalescing tasks behavior is observable from a microtask checkpoint perspective. If tasks are coalesced, then a manual microtask checkpoint must be performed between processing each individual FinalizationRegistry.

Kouhei Ueno

unread,
Feb 18, 2021, 7:39:22 PM2/18/21
to Shu-yu Guo, Kentaro Hara, Michael Lippautz, Marja Hölttä, Minoru Chikamune, scheduler-dev, Blink Isolation discussions, platform-architecture-dev
On Fri, Feb 19, 2021 at 3:15 AM Shu-yu Guo <s...@chromium.org> wrote:
I misspoke on one point above: the coalescing tasks behavior is observable from a microtask checkpoint perspective. If tasks are coalesced, then a manual microtask checkpoint must be performed between processing each individual FinalizationRegistry.

On Thu, Feb 18, 2021 at 10:12 AM Shu-yu Guo <s...@chromium.org> wrote:
Hi all,

The JS spec side of posting tasks (called a Job in JS spec-ese) is here. The HTML integration PR that describes task queuing behavior is here. We should get that PR merged, which was blocked on sorting out incumbent settings object stuff, which we finally did. The semantics:
  • The task source is currently named the "garbage collection task source", but from this comment we will rename it to something like "JS engine task source" for all tasks posted by the JS engine and by features specified in ECMAScript. 
  • One task is posted per FinalizationRegistry, not per callback invocation, not one task for all FinalizationRegistries. Each FinalizationRegistry may invoke its callback many times, on each dead cell, in a single task. These callbacks are called in a loop over some or all of the dead cells, and the correct v8::Context should be entered before entering into this loop.
  • Note that the specced one-task-per-FinalizationRegistry behavior is not observable. Even though it looks like the spec posts separate tasks and the relative ordering of those tasks when interleaved with Atomics.waitAsync tasks on the same task source would be observable, the JS spec gives significant leeway for when these finalization tasks are posted. The JS engine can choose to post all FinalizationRegistry tasks back-to-back, which would be observably equivalent to coalescing tasks. That said it doesn't seem desirable to schedule them all within one task in case finalization takes too long.
  • There is no spec guarantee that each task must run the FinalizationRegistry callback on all its dead cells. If a task doesn't want to process all dead cells in a FinalizationRegistry, it should requeue a task. This is to give implementations flexibility in budgeting time for these tasks.

On Thu, Feb 18, 2021 at 5:48 AM Kentaro Hara <har...@chromium.org> wrote:
IIUC, callbacks are collected and there's one task per GC cycle that dispatches all of the FinalizationRegistry cleanups in their respective contexts.

That matches my understanding. V8 only enters v8::Context before invoking the callback.

If we want to make the callback use a correct per-ASG task runner, we need to repost a task per callback. This doesn't sound realistic to me.

Would you elaborate on why? I think reposting tasks per FinalizationRegistry as syg@ mentioned is feasible. Then, we can change the postTask target to its associated ASG V8TaskRunner to ensure all user JavaScript runs on a per-ASG task runner.
 
You received this message because you are subscribed to the Google Groups "scheduler-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scheduler-de...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/scheduler-dev/CAN-e9e--k68EiyuNcgXHDJE%2BbXhz%3D8u7S6z1K9Ywk91TEk0N3w%40mail.gmail.com.


--
kouhei

Kentaro Hara

unread,
Feb 19, 2021, 12:27:01 AM2/19/21
to Kouhei Ueno, Shu-yu Guo, Michael Lippautz, Marja Hölttä, Minoru Chikamune, scheduler-dev, Blink Isolation discussions, platform-architecture-dev
Would you elaborate on why? I think reposting tasks per FinalizationRegistry as syg@ mentioned is feasible. Then, we can change the postTask target to its associated ASG V8TaskRunner to ensure all user JavaScript runs on a per-ASG task runner.

You're right. If we can repost tasks per FinalizationRegistry, it will work. (I was thinking we need to repost tasks per callback, then the performance overhead will be too much.)

One more thing: Any C++ callback called from V8 may invoke any arbitrary JavaScript unless we are really careful. For example, if the C++ callback uses v8_object->Get() and the getter is overridden in the user script, it may invoke any arbitrary JavaScript.


Reply all
Reply to author
Forward
0 new messages