Можно ли читать локальные переменные родительских стековых кадров?

Сценарий

Я работаю над кодом Javascript, который позволяет запускать произвольный пользовательский код в среде веб-рабочего, подобный этому подходу. Позвольте позвонить обеим сторонам (запускает рабочего) и гостя (внутри рабочего).

Теперь я хочу иметь возможность одновременно запускать несколько сценариев на рабочем месте или, по крайней мере, сразу же отправлять их работнику, не дожидаясь окончания предыдущего script. Крайне важно уменьшить задержку и накладные расходы, потому что один сеанс может занять миллионы или даже миллиарды script - чем больше я могу сделать за несколько секунд, тем лучше; и да, есть веская причина, почему я выбираю Javascript (производительность важна, но не самый важный аспект).

Однако это означает, что работник как-то должен с уверенностью сказать мне, когда script запущен и остановлен. Это требует некоторой отправки сообщений с уникальным идентификатором script.

Код

В настоящее время я использую этот код в гостевой системе:

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). Я использую некоторую криптографическую библиотеку для генерации идентификатора, поэтому пользователь не может угадать идентификатор (поскольку рабочий убит при любом неправильном предположении). Кроме того, onmessage нельзя переопределить code.

Могу ли я удостовериться, что code не может "подделать" сообщение остановки, достигнув максимума в стеке, когда код выполняется в среде, которую автор code не может контролировать (например, на сервере node или в браузере другого пользователя)?

Возможное решение:

Отправьте сообщение "stop" в 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, которое остановит этот таймер тайм-аута!).

Ответ 1

невозможно для чтения кода scriptId в любой среде выполнения?

Да. Код может обращаться только к переменным в своей текущей области действия (контекст выполнения), а не локальные переменные из областей в стеке вызовов.

Однако код eval'd может иметь доступ и, возможно, перезаписывать/перехватывать глобальные функции postMessage и onmessage. Если вы это предотвратили, они все равно смогут их вызвать, поэтому вам нужно проверить, что звонки поступали от вашего рабочего менеджера, а не из пользовательского кода. Если вы обеспечили это, все должно быть хорошо.

Обратите внимание, что при запуске нескольких сценариев в одном и том же "рабочем сеансе" вам может понадобиться очистить глобальную область после каждого запуска (или заранее предотвратить любые изменения). В противном случае независимые скрипты могут взаимодействовать друг с другом.