是否可以读取父堆栈帧的局部变量

Is it possible to read local variables of parent stackframes?

本文关键字:局部变量 堆栈 读取 是否      更新时间:2023-09-26

场景

我正在开发Javascript代码,它允许在web工作环境中运行任意用户提供的代码,类似于这种方法。让我们称双方为主机(启动工作程序)和来宾(工作程序内部)。

现在,我希望能够在worker上同时运行多个脚本,或者至少立即将它们发送给worker,而不必等待上一个脚本完成。减少延迟和开销至关重要,因为一个"会话"可能需要数百万甚至数十亿次脚本运行——几秒钟内我能做的越多越好;是的,我之所以选择Javascript是有充分理由的(性能很重要,但不是最重要的方面)。

然而,这意味着工作人员需要以某种方式可靠地告诉我脚本何时开始和停止。这需要发送一些消息,包括一个唯一的脚本标识符。

代码

我目前正在客人中使用此代码:

onmessage = function(event) {
    var scriptId = event.data.id;
    var cmd = event.data.cmd;
    var args = event.data.args;
    switch (cmd) {
        case "init":
            // code here to lock down the worker and disable everything potentially insecure, except for postMessage
            break;
        case "run":
            if (!workerInitialized()) return;
            // run user given code
            var code = args.code;
            // ...
            postMessage({cmd: "start", args: scriptId});        // signal that script started
            runScript(code);        // runs eval(code) with proper error reporting etc.
            postMessage({cmd: "stop", args: scriptId});         // signal that script stopped
            break;
        // ...
    }
}

问题

如您所见,scriptId存在于eval(code)之上的堆栈帧中。我正在使用一些加密库来生成id,这样用户就不可能猜测id(因为工作人员会因为任何错误的猜测而死亡)。此外,onmessage不能被code覆盖。

当代码在code的作者无法控制的环境中(例如,在节点服务器或不同用户的浏览器中)执行时,我可以通过在堆栈处达到峰值来确保code不会"伪造"停止消息吗?

可能的解决方案

setTimeout中发送"停止"消息,并在闭包中隐藏scriptId,以便在code执行完毕后立即执行。

这个JSFiddle表明它是有效的,但也许我忽略了一些邪恶的边缘案例?

这意味着,我将case "run"代码更改为:

postMessage({cmd: "start", args: scriptId});        // signal that script started
setTimeout((function(scriptId) { return function() { postMessage({cmd: "stop", args: scriptId}); };})(scriptId), 0);         // signal that script stopped, right after this function returned
scriptId = null;
runScript(code);        // runs eval(code) with proper error reporting etc.

code在任何执行环境下都不可能读取scriptId吗?我甚至不在乎它是否能停止计时器,因为工人无论如何都会在固定的超时后被杀死(只要它不能伪造stop消息,它就会停止"超时计时器"!)。

代码在任何执行环境中都不可能读取scriptId吗?

是的。代码只能访问其当前作用域(执行上下文)中的变量,而不能访问调用堆栈上作用域中的本地变量。

但是,eval'd代码可以访问并可能覆盖/截获全局postMessageonmessage函数。如果你阻止了这种情况,他们仍然可以呼叫他们,所以你需要验证这些呼叫是否来自你的员工经理,而不是来自自定义代码。如果你已经确保了,一切都会好起来的。

请注意,当您在同一个"工作会话"中运行多个脚本时,您可能需要在每次运行后清理全局作用域(或者预先防止任何修改)。否则,独立的脚本可能能够相互通信。