如何检测CSharpScript.EvaluateAsync执行的代码是否陷入死循环?
检测并处理CSharpScript执行的无限运行代码
要避免CSharpScript.EvaluateAsync执行无限代码导致应用挂起,可从超时控制、静态代码分析和取消逻辑注入三个维度入手,以下是具体实现方案:
1. 异步超时+取消令牌(最实用的运行时检测)
直接同步调用.Result会阻塞线程,改用带超时的异步等待,结合CancellationTokenSource在超时后终止脚本执行,从根本上避免挂起。
using System; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Scripting; public static async Task<string> ExecuteScriptWithTimeout(string scriptContent, int timeoutMs) { using var cancellationTokenSource = new CancellationTokenSource(timeoutMs); try { // 传入取消令牌,超时后自动终止脚本 return await CSharpScript.EvaluateAsync<string>( scriptContent, cancellationToken: cancellationTokenSource.Token ); } catch (OperationCanceledException) { throw new InvalidOperationException("脚本执行超时,疑似存在无限运行逻辑"); } catch (Exception ex) { throw new InvalidOperationException("脚本执行失败", ex); } } // 调用示例 public static void TestScriptExecution() { var endlessScript = @"string Run() { while(true){} return ""1"";} Run()"; try { // 设置3秒超时 var result = ExecuteScriptWithTimeout(endlessScript, 3000).GetAwaiter().GetResult(); Console.WriteLine($"执行结果:{result}"); } catch (InvalidOperationException ex) { Console.WriteLine($"错误:{ex.Message}"); // 输出:脚本执行超时,疑似存在无限运行逻辑 } }
2. 静态代码分析(提前检测潜在死循环)
通过Roslyn语法分析器,提前扫描脚本中的无条件循环(如while(true)),在执行前预判风险。这种方式只能检测简单的静态死循环,无法识别依赖运行时变量的动态死循环,适合作为前置校验。
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; public static bool HasPotentialInfiniteLoop(string scriptContent) { var syntaxTree = CSharpSyntaxTree.ParseText(scriptContent); var rootNode = syntaxTree.GetRoot(); var loopDetector = new InfiniteLoopSyntaxDetector(); loopDetector.Visit(rootNode); return loopDetector.DetectedInfiniteLoop; } // 自定义语法遍历器,检测无条件循环 private class InfiniteLoopSyntaxDetector : CSharpSyntaxWalker { public bool DetectedInfiniteLoop { get; private set; } public override void VisitWhileStatement(WhileStatementSyntax node) { base.VisitWhileStatement(node); // 检查while条件是否为常量true if (node.Condition is LiteralExpressionSyntax literal && literal.IsKind(SyntaxKind.TrueLiteralExpression)) { DetectedInfiniteLoop = true; } } public override void VisitForStatement(ForStatementSyntax node) { base.VisitForStatement(node); // 检查for循环是否无终止条件(无判断条件或条件恒为true) if (node.Condition == null || (node.Condition is LiteralExpressionSyntax literal && literal.IsKind(SyntaxKind.TrueLiteralExpression))) { DetectedInfiniteLoop = true; } } } // 调用示例 var riskyScript = @"string Run() { while(true){} return ""1"";} Run()"; if (HasPotentialInfiniteLoop(riskyScript)) { Console.WriteLine("警告:脚本包含潜在无限循环,禁止执行"); }
3. 脚本注入取消逻辑(可选,需修改脚本)
如果允许修改脚本内容,可以在脚本中注入取消令牌检查逻辑,让脚本主动响应终止信号。这种方式需要动态修改用户脚本,适合可控场景:
// 注入取消检查的脚本模板 var templateScript = @" using System.Threading; string Run(CancellationToken token) { while(true) { token.ThrowIfCancellationRequested(); // 定期检查取消信号 // 原有逻辑 } return ""1""; } Run(CancellationToken)"; // 执行时传入取消令牌 var result = await CSharpScript.EvaluateAsync<string>( templateScript, globals: new { CancellationToken = cancellationTokenSource.Token } );
实际场景中,推荐静态分析前置校验+运行时超时控制的组合方案,既可以提前拦截明显的死循环脚本,又能应对动态产生的无限运行逻辑。
内容的提问来源于stack exchange,提问作者lucidgold
相关产品推荐
相关产品推荐

