safeify icon indicating copy to clipboard operation
safeify copied to clipboard

在沙箱里调用方法会阻塞主线程,导致 timeout 失效

Open canguser opened this issue 3 years ago • 2 comments

描述

在下面的代码中,在沙箱中调用 add 方法时,通过 while (true) {} 阻塞主进程,按理说只应该阻塞子进程,在 timeout 后报错,但并没有,这段代码会一直卡住,阻塞主进程。

const {Safeify} = require('./lib')

let debug = require('debug');
debug.enable('safeify');

const safeVm = new Safeify({
    timeout: 50,          //超时时间,默认 50ms
    asyncTimeout: 1000,    //包含异步操作的超时时间,默认 500ms
    quantity: 2,          //沙箱进程数量,默认同 CPU 核数
    memoryQuota: 100,     //沙箱最大能使用的内存(单位 m),默认 500m
    cpuQuota: 0.1,        //沙箱的 cpu 资源配额(百分比),默认 50%
});

const context = {
    a: 1,
    b: 1,
    add(a, b) {
        while (true){
            // console.log(b)
        }

        return a + b;
    }
};

(async function f() {

    setTimeout(()=>{
        console.log('????')
    }, 2000)

    const rs = await Promise.all(
        [
            safeVm.run(`return add(a,1)`, context),
            safeVm.run(`return add(a,2)`, context),
            // safeVm.run(`return add(a,2)`, context),
            // safeVm.run(`return add(a,3)`, context),
            // safeVm.run(`return add(a,4)`, context),
            // safeVm.run(`return add(a,5)`, context),
            // safeVm.run(`return add(a,6)`, context),
            // safeVm.run(`return add(a,7)`, context),
            // safeVm.run(`return add(a,8)`, context),
            // safeVm.run(`return add(a,9)`, context),
            // safeVm.run(`return add(a,10)`, context),
            // safeVm.run(`return add(a,11)`, context)
        ]
    )
    console.log('result', rs);

    // 释放资源
    safeVm.destroy();

})();


原因

源码中为了限制方法的使用或者是由于进程通信必须序列化的原因,在沙箱子线程中调用 context 的方法时,会回传给主进程,让主线程调用方法并将结果回传给子进程

建议

暂无比较完善的解决方案,望思考

canguser avatar Jun 09 '21 04:06 canguser

context是给沙箱的上下文,不应该在这里死循环。同时上下文是可选的 run(code: string | Function, sandbox?: any): Promise<any>

另外,线程与进程是两回事。

DefectingCat avatar Jul 02 '21 03:07 DefectingCat

是我表达有误,应该全是进程而不是线程,已修改。二者,虽然上下文是主进程提供的,但是在运行的时候,由于引入了子进程的概念,我们更希望所有代码的执行都放在子进程中运行,这样才能将二者分开,不会因为子进程(恶意地调用某个上下文的方法)导致主进程异常。

例子:

const {Safeify} = require('safeify')

const safeVm = new Safeify({
    timeout: 50,          //超时时间,默认 50ms
    asyncTimeout: 1000,    //包含异步操作的超时时间,默认 500ms
    quantity: 4,          //沙箱进程数量,默认同 CPU 核数
    memoryQuota: 100,     //沙箱最大能使用的内存(单位 m),默认 500m
    cpuQuota: 0.1,        //沙箱的 cpu 资源配额(百分比),默认 50%
});


const context = {
    /**
     * 计算总和 (一个很正常的函数,由主进程提供)
     * @param arr
     * @return {number}
     */
    sum(arr = []) {
        let sum = 0;
        for (let i = 0; i < arr.length; i++) {
            sum += arr[i]
        }
        return sum
    }
};


(async function f() {

    const sum = await safeVm.run(`
        // 恶意代码,恶意调用主进程的方法
        return sum({ length: 10e307 })
    `, context);

    // 会卡住主进程,即便 timeout 也不会强制结束
    console.log('result', sum);

    // 释放资源
    safeVm.destroy();

})();

canguser avatar Jul 02 '21 04:07 canguser