简体   繁体   English

如何防止JavaScript中的循环导致浏览器或应用程序崩溃?

[英]How to prevent loops in JavaScript that crash the browser or Apps?

I am creating a live editor in Windows 8.1 App using JavaScript. 我正在使用JavaScript在Windows 8.1 App中创建一个实时编辑器。 Almost done with that, but the problem is whenever I run such bad loops or functions then it automatically hangs or exits. 几乎完成了,但问题是每当我运行这样的错误循环或功能,然后它会自动挂起或退出。

I test it with a loop such as:( It just a example-user may write its loop in its own way..) 我用一个循环测试它,如:(它只是一个例子 - 用户可以用自己的方式编写它的循环..)

for(i=0;i<=50000;i++)
{
   for(j=0;j<5000;j++){
     $('body').append('hey I am a bug<br>');
   }
}

I know that this is a worst condition for any app or browser to handle that kind of loop. 我知道这是任何应用程序或浏览器处理这种循环的最糟糕情况。 So here I want that if user uses such a loop then how I handle it, to produce their output? 所以在这里我希望如果用户使用这样的循环然后我如何处理它,以产生他们的输出?

Or if its not possible to protect my app for that kind of loop, if it is dangerous to my app so I alert the user that: 或者,如果它不能保护我的应用程序的那种循环,如果它对我的应用程序是危险的,所以我提醒用户:

Running this snippet may crash the app! 运行此代码段可能会导致应用崩溃!

I have an idea to check the code by using regular expressions if code have something like for(i=0;i<=5000;i++) then the above alert will show, how to do a Regex for that? 我有一个想法,如果代码有类似for(i=0;i<=5000;i++)东西,使用正则表达式检查代码,然后上面的警告将显示,如何为此做一个正则表达式?

Also able to include C# as back-end . 也可以包含C#作为后端

Unfortunately, without doing some deep and complex code analysis of the edited code, you'll not be able to fully prevent errant JavaScript that kills your application. 遗憾的是,如果不对编辑过的代码进行深入而复杂的代码分析,您将无法完全防止错误的JavaScript杀死您的应用程序。 You could use, for example, a library that builds an abstract syntax tree from JavaScript and not allow code execution if certain patterns are found. 例如,您可以使用从JavaScript构建抽象语法树的库,如果找到某些模式,则不允许代码执行。 But, the number of patterns that could cause an infinite loop are large, so it would not be simple to find, and it's likely to not be robust enough. 但是,可能导致无限循环的模式数量很大,因此查找起来并不简单,而且可能不够强大。

In the for example, you could modify the code to be like this: for例如,您可以修改代码是这样的:

for(i=0;!timeout() && i<=50000;i++)
{
   for(j=0;!timeout() && j<5000;j++){
     $('body').append('hey I am a bug<br>');
   }
}

I've "injected" a call to a function you'd write called timeout . 我已经“注入”了一个你写的函数调用timeout In there, it would need to be able to detect whether the loop should be aborted because the script has been running too long. 在那里,它需要能够检测是否应该中止循环,因为脚本已经运行了太长时间。

But, that could have been written with a do-while , so that type of loop would need to be handled. 但是,这可能是用do-while编写的,因此需要处理这种类型的循环。

The example of using jQuery for example in a tight loop, and modifying the DOM means that solutions that trying to isolate the JavaScript into a Web Worker would be complex, as it's not allowed to manipulate the DOM directly. 例如在紧密循环中使用jQuery并修改DOM意味着尝试将JavaScript隔离到Web Worker的解决方案会很复杂,因为不允许直接操作DOM。 It can only send/receive "string" messages. 它只能发送/接收“字符串”消息。

If you had used the XAML/C# WebView to host (and build) the JavaScript editor, you could have considered using an event that is raised called WebView.LongRunningScriptDetected . 如果您使用XAML / C#WebView来托管(和构建)JavaScript编辑器,那么您可以考虑使用一个名为WebView.LongRunningScriptDetected的事件。 It is raised when a long running script is detected, providing the host the ability to kill the script before the entire application becomes unresponsive and is killed. 检测到长时间运行的脚本时会引发此问题,从而使主机能够在整个应用程序无响应并被终止之前终止脚本。

Unfortunately, this same event is not available in the x-ms-webview control which is available in a WinJS project. 不幸的是,同样的事件在WinJS项目中可用的x-ms-webview控件中不可用。

One idea, but not sure what is your editor is capable of.. 一个想法,但不知道你的编辑是什么能够..

If some how you can understand that this loop may cause problem(like if a loop is more than 200 times then its a issue) and for a loop like that from user if you can change the code to below to provide the output then it will not hang. 如果有些你怎么理解这个循环可能会导致问题(比如一个循环超过200次然后是一个问题),并且如果你可以将代码改为下面提供输出那么来自用户的循环那么它将会不要挂。 But frankly not sure if it will work for you. 但坦率地说不确定它是否适合你。

var j = 0;
var inter = setInterval( function(){
    if( j<5000  ){
      $('#test').append('hey I am a bug<br>');
      ++j;  
    } else {
        clearInterval(inter);
    }
}, 100 );

Perhaps inject timers around for loops and check time at the first line. 也许可以在第一行注入循环和检查时间。 Do this for every loop. 为每个循环执行此操作。

Regex: /for\\([^{]*\\)[\\s]*{/ 正则表达式: /for\\([^{]*\\)[\\s]*{/

Example: 例:

/for\([^{]*\)[\s]*{/.test("for(var i=0; i<length; i++){");
> true

Now, if you use replace and wrap the for in a grouping you can get the result you want. 现在,如果您使用replace并将for包装在分组中,您可以获得所需的结果。

var code = "for(var i=0; i<length; i++){",
    testRegex = /(?:for\([^{]*\)[\s]*{)/g,
    matchReplace = "var timeStarted = new Date().getTime();" +
                   "$1" +
                   "if (new Date().getTime() - timeStarted > maxPossibleTime) {" +
                       "return; // do something here" +
                   "}";

code.replace(textRegex, matchReplace);

You cannot find what user is trying to do with a simple regex. 您无法找到用户尝试使用简单正则表达式执行的操作。 Lets say, the user writes his code like... 可以说,用户写的代码就像......

for(i=0;i<=5;i++)
{
   for(j=0;j<=5;j++){
     if(j>=3){
         i = i * 5000;
         j = j * 5000;
     }
     $('body').append('hey I am a bug<br>');
   }
}

Then with a simple regex you cannot avoid this. 然后用一个简单的正则表达式你无法避免这种情况。 Because the value of i is increased after a time period. 因为i的值在一段时间后增加。 So the best way to solve the problem is to have a benchmark. 因此,解决问题的最佳方法是制定基准。 Say, your app hangs after continuos processing of 3 minutes(Assume, until your app hits 3 minutes of processing time, its running fine). 比如,你的应用程序在连续处理3分钟后挂起(假设,直到你的应用程序达到3分钟的处理时间,它运行正常)。 Then, whatever the code the user tries to run, you just start a timer before the process and if the process takes more than 2.5 minutes, then you just kill that process in your app and raise a popup to the user saying 'Running this snippet may crash the app!'... By doing this way you dont even need a regex or to verify users code if it is bad... 然后,无论用户尝试运行什么代码,您只需在进程之前启动一个计时器,如果该进程需要超过2.5分钟,那么您只需在应用程序中终止该进程并向用户发出弹出窗口,说“运行此代码段”可能会崩溃应用程序!'...通过这种方式你甚至不需要正则表达式或验证用户代码,如果它是坏的...

Try this... Might help... Cheers!!! 试试这个...可能会帮助......干杯!

I've got 2 solutions: 我有2个解决方案:

1. 1。

My first solution would be defining a variable startSeconds=new Date().getSeconds(); 我的第一个解决方案是定义变量startSeconds=new Date().getSeconds(); .

Then, using regex, I'm inserting this piece of code inside the nested loop. 然后,使用正则表达式,我将这段代码插入嵌套循环中。

;if(startSecond < new Date().getSeconds())break;

So, what it does is each time the loop runs, it does two things: 所以它的作用是每次循环运行时,它做两件事:

Checks if startSecond is less than current seconds new Date().getSeconds(); 检查startSecond是否小于当前秒new Date().getSeconds(); .

For example, startSecond may be 22. new Date().getSeconds() may return 24.Now, the if condition succeeds so it breaks the loop. 例如,startSecond可能是22. new Date()。getSeconds()可能返回24.现在, if condition成功,因此它打破了循环。

Mostly, a non dangerous loop should run for about 2 to 3 seconds 大多数情况下,非危险循环应该运行大约2到3秒

Small loops like for(var i=0;i<30;i++){} will run fully, but big loops will run for 3 to 4 seconds, which is perfectly ok. for(var i=0;i<30;i++){}将完全运行,但是大循环将运行3到4秒,这是完全可以的。

My solution uses your own example of 50000*5000, but it doesn't crash! 我的解决方案使用您自己的50000 * 5000示例,但它不会崩溃!

Live demo: http://jsfiddle.net/nHqUj/4 现场演示: http//jsfiddle.net/nHqUj/4

2. 2。

My second solution would be defining two variables start , max . 我的第二个解决方案是定义两个变量startmax

Max should be the maximum number of loops that you are willing to run. Max应该是您愿意运行的最大循环数。 Example 1000. 例1000。

Then, using regex, I'm inserting this piece of code inside the nested loop. 然后,使用正则表达式,我将这段代码插入嵌套循环中。

;start+=1;if(start>max)break;

So, what it does is each time the loop runs, it does two things: 所以它的作用是每次循环运行时,它做两件事:

  1. Increments the value of start by 1. start的值递增1。

  2. Checks whether start is greater than the max . 检查start是否大于max If yes, it breaks the loop. 如果是,它会打破循环。

This solution also uses your own example of 50000*5000, but it doesn't crash! 此解决方案也使用您自己的50000 * 5000示例,但它不会崩溃!

Updated demo: http://jsfiddle.net/nHqUj/3 更新的演示: http//jsfiddle.net/nHqUj/3

Regex I'm using: (?:(for|while|do)\\s*\\([^\\{\\}]*\\))\\s*\\{([^\\{\\}]+)\\} 正则表达式我正在使用(?:(for|while|do)\\s*\\([^\\{\\}]*\\))\\s*\\{([^\\{\\}]+)\\}

Let's assume you are doing this in the window context and not in a worker. 假设您在窗口上下文中而不是在工作者中执行此操作。 Put a function called rocketChair in every single inner loop. 在每个内部循环中放入一个名为rocketChair的函数。 This function is simple. 这个功能很简单。 It increments a global counter and checks the value against a global ceiling. 它递增全局计数器并根据全局上限检查该值。 When the ceiling is reached rocketChair summarily throws "eject from perilous code". 当天花板到达时,rocketChair总是抛出“从危险代码中弹出”。 At this time you can also save to a global state variable any state you wish to preserve. 此时,您还可以将任何要保留的状态保存到全局状态变量。

Wrap your entire app in a single try catch block and when rocket chair ejects you can save the day like the hero you are. 将整个应用程序包裹在一个try catch块中,当火箭椅弹出时,您可以像英雄一样拯救这一天。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM