jurassic
jurassic copied to clipboard
Allow to track loop iterations using a user defined code call
The idea is that this delegate that we inject allow us to verify that the scripts isn't doing BAD THINGS.
See fuller discussion of this and the implications of what it enables here:
https://ayende.com/blog/179457/optimizing-javascript-and-solving-the-halting-problem-part-i?key=cf628fb3c9ae4f4d9dec980d04886248
https://ayende.com/blog/179458/optimizing-javascript-and-solving-the-halting-problem-part-ii?key=c9ab77877d474eb694fc4b901965177f
Very interesting, thanks. Being robust to badly behaving and actively malicious scripts is definitely of interest to quite a few people, but I've resisted doing anything about it because the only truly robust method is to run the malicious code in a separate OS process. Unfortunately, this solution is difficult and in a lot of cases (e.g. UWP) it's not even possible. So a solution like yours would definitely be useful as a kind of middle ground, even if it's not 100% robust.
@paulbartrum We actually tested a bunch of other stuff, including injecting our code on every branch instruction, but that seemed too intrusive. I can't think of a way that this + recursion limit + no CLR calls gives you a way to damage the parent process.
Note that we assume that actually handling the policy such as timeout / # of allocations is done by the supplied delegate.
Can you think about a way this will still open us some issues?
You're hooking into the built-in loop statements, but there are many other ways to execute lots of code.
Some I can think of:
- Add tons of code to a string variable and
eval()
it. - Use
Array.prototype.forEach
. - Call methods that take a long time to complete (like
Array.prototype.sort
for example).
Hi,
note that I did similar changes in my branch implementScriptTimeout3
- see #85: At the beginning of every loop (and at the beginning of user-defined functions) I check a flag, and if it is true
, a ScriptCanceledException
will be thrown in order to cancel the script (in this case, finally
blocks aren't executed any more as they now will only be executed if the exception could have been caught by JS code (if it is a JavaScriptException
).
(I originally created code of the ScriptTimeoutHelper
in the wiki page as that approach seemed to work for me, until I found that it does not work for JS code in finally
clauses where a ThreadAbortException
will not be thrown.)
I think that you should also check the delegate when user-defined functions are invoked, in order to handle the Array.prototype.forEach
case that @paulbartrum mentioned, and because you can construct functions without a loop that run lots of operations (without exceeding a reasonable recursion depth limit), e.g.
function func(n) {
if (n < 15) {
func(n + 1);
func(n + 1);
func(n + 1);
func(n + 1);
func(n + 1);
func(n + 1);
}
}
func(1);