laverdet / isolated-vm

Secure & isolated JS environments for nodejs
ISC License
2.19k stars 154 forks source link

Minimal representation of isolate code hanging the entire process #451

Open mifopen opened 10 months ago

mifopen commented 10 months ago

For me, the following code hangs the outer node process in 60% of cases. In other cases, it finishes with a top-level error about the unhandled promise. By slightly modifying the code, I can also come up with a version that crashes the outer process with the same top-level error without an opportunity to catch it.

import ivm from 'isolated-vm';

const isolate = new ivm.Isolate();
const context = isolate.createContextSync();

context.evalClosureSync(
    `
    globalThis.fn = async () => {
        await new Promise(res => {
            $1.applySync(undefined, [new $0.Reference(res), 0]);
        });
        new Promise((res, rej) => { rej(new Error("from promise")); });
    };
`,
    [
        /* $0 */
        ivm,

        /* $1 setTimeout */
        new ivm.Reference((cb, ms) => {
            setTimeout(() => {
                cb.apply(undefined, []);
            }, ms);
        }),
    ],
);

const code = `fn()`;

console.log('before first');
try {
    await context.eval(code, { promise: true });
} catch (e) {
    console.log('error first');
}
console.log('after first');

console.log('before second');
try {
    await context.eval(code, { promise: true });
} catch (e) {
    console.log('error second');
}

console.log('after second');

console.log('before third');
try {
    await context.eval(code, { promise: true });
} catch (e) {
    console.log('error third');
}
console.log('after third');

console.log('before fourth');
try {
    await context.eval(code, { promise: true });
} catch (e) {
    console.log('error fourth');
}
console.log('after fourth');