isolated-vm icon indicating copy to clipboard operation
isolated-vm copied to clipboard

How to prevent the main thread from getting blocked?

Open BartTactick opened this issue 2 years ago • 5 comments

Hey there,

So i made this script to show off the problem:

const ivm = require("isolated-vm");
const isolate = new ivm.Isolate({ memoryLimit: 128 });

const context = isolate.createContextSync();
const jail = context.global;

jail.setSync("global", jail.derefInto());

context.evalClosureSync(
    `
    globalThis.log = (...args) => {
                return $0.apply(undefined, args, { arguments: { copy: true }, result: { promise:true, copy: true } });
            };
        `,
    [
        (...a) => {
            console.log(a);
            while (true) {}
        },
    ],
    {
        arguments: { reference: true },
    }
);
setInterval(() => {
    console.log(isolate.cpuTime + "");
}, 250);

context.eval('log("hello world")');

The function basicly runs in the main thread so it is blocking the main thread and the CPU time is not showing. How can i make it so the main thread is not blocking? I expect the CPU time to be logged every 250 ms.

BartTactick avatar Jun 29 '22 10:06 BartTactick

@BartTactick Were you able to find a solution?

traviscooper avatar Jul 20 '22 20:07 traviscooper

@traviscooper No solution yet, seems like the code in functions like this are ran in the main thread therefore not counting the CPU time.

BartTactick avatar Jul 22 '22 06:07 BartTactick

I don't think args are provided by the untrusted code, so it makes sense why that function can run outside of the sandbox (if it didn't I guess the console.log would also not work since it would log in the child process). The solution would be not pass random and untrusted arguments (why do that?).

But if you still want your argument to be blocking and not block the main thread, you can always run ivm in separate Node process using child_process. With the IPC however you won't be likely to watch for the CPU time from isolate class in child process as the limitation due to the serialization – at best, you might be able to observe the details about the entire child process using their PID.

SpacingBat3 avatar Jul 27 '22 22:07 SpacingBat3

I don't think args are provided by the untrusted code, so it makes sense why that function can run outside of the sandbox (if it didn't I guess the console.log would also not work since it would log in the child process). The solution would be not pass random and untrusted arguments (why do that?).

But if you still want your argument to be blocking and not block the main thread, you can always run ivm in separate Node process using child_process. With the IPC however you won't be likely to watch for the CPU time from isolate class in child process as the limitation due to the serialization – at best, you might be able to observe the details about the entire child process using their PID.

Thats true, however i dont feel like child_process is a good solution since the CPU time doenst increase either. Do you know a good solution? (for example if i loop 1000 times in an attached function, it wont increase the cpu time of the isolate)

BartTactick avatar Jul 28 '22 06:07 BartTactick

evalClosureSync

All the *Sync method runs in the main thread. While your callback is also defined in the main isolate/thread, it will be run in the main isolate/thread as well.

Only if you run the async version of the evals, as well as the while (true) loop, is defined and run in the other isolate, it will not block the main thread.

kiddkai avatar Jan 16 '23 04:01 kiddkai