【问题标题】:Prevent Lua infinite loop防止 Lua 无限循环
【发布时间】:2012-05-08 06:54:37
【问题描述】:

我使用 lua 接口在我的 C# 程序中获得 lua 支持,如果用户提交这样的代码,工作线程将冻结

while true do end

我有一种方法可以检测无限循环是否正在运行,但我需要一种从 Worker 线程中退出 DoString 方法的好方法。有什么想法吗?

编辑:@kikito,是的,我正在检测类似的东西。我遇到的问题是我找不到杀死 DoString 方法的干净方法,看起来 Lua 接口主类(Lua)有一些静态依赖项,因为如果我在我的实例上执行 lua.Close(); 它将中止 DoString 方法,但是下次我实例化一个 lua 类 new Lua(); 时,它会崩溃,说一些关于保护性内存的事情

edit:显示我的 .Close 代码的功能分支 https://github.com/AndersMalmgren/FreePIE/tree/detect-and-recover-infite-lua-loop

【问题讨论】:

  • google了30秒后发现LUA支持hook事件:'有四种事件可以触发hook:每次Lua调用函数时都会发生call事件;每次函数返回时都会发生返回事件;当 Lua 开始执行新的代码行时发生 line 事件;和计数事件发生在给定数量的指令之后'。你去吧。对于解释器实例,我通常会在调用解释器的线程捕获的“OnSomething”事件中引发异常。不过,不知道 Lua。
  • 任何人都有针对 C# 的 Lua 交互的解决方案?
  • 最初的 Lua 实现(用 C 语言编写)应该在 close 中存在(它是可重入的,并且状态被完全封装)。如果 C# 没有,请提交错误。 (否则你总会有一些代码遇到崩溃,例如extremely long (decades or more) running string operations,钩子对此无济于事。)
  • 抱歉回复晚了,假期.. C# 包装器不是一个非常活跃的项目:/ 但会提出问题,谢谢
  • 我做了一个功能分支,以便您可以测试代码或查看我是否犯了任何错误github.com/AndersMalmgren/FreePIE/tree/…

标签: c# lua


【解决方案1】:

沙盒 Lua

设置钩子根本不足以防止资源的意外浪费,更不用说滥用了——这里有一个简单的例子(在字符串模式匹配过程中花费的时间——没有调试钩子被调用):

s=('a'):rep(20000):match('.-b')

对一段 Lua 代码强制执行时间/内存限制的唯一可靠方法是在其自己的进程中运行 Lua 解释器,并让您的操作系统监视该进程。

Lua 的好处是您不需要任何复杂的、依赖于操作系统的沙盒权限设置:您只需限制时间和内存(合理;在 Windows 上有 Job Objects,Unix 有 ulimits-相关:@ 987654322@),然后将诸如 os.execute、一半 io-library 和诸如 luasocket 之类的模块之类的东西远离脚本编写者(很容易)。

从沙盒代码中的错误中恢复

你可以处理几乎所有事情(除了违反时间/内存限制)而不会破坏你的 Lua 解释器:只需将用户提供的代码的执行包装在 pcall 中;如果你调用任何可能失败的 Lua-API 函数,你需要将它们包装在一个你也可以 pcall 的函数中(或者设置一个 Lua 恐慌函数并从那里处理它)。


[我不希望看到这个帖子的人认为 debug.sethook 足以用于沙盒,而且 stackoverflow 不允许我发表评论(还)]

【讨论】:

    【解决方案2】:

    将 Lua 循环放在协程中,在每个循环结束时,只需使用Yield return null 等待一帧(允许执行工作线程)。

    【讨论】:

      【解决方案3】:

      我建议像处理任何其他 Lua 错误一样处理它。换句话说,威胁上面的代码就像用户刚刚写的一样

      error("Script execution has been cancelled after stalling for too long")
      

      (我假设您通过假设没有脚本的运行时间不应超过固定时间来检测无限循环。如果不是这种情况,请适当地更改消息)

      您必须处理的方式取决于您如何处理 Lua 错误 - 但无论如何您都必须这样做,所以很可能大部分代码已经存在。

      编辑:马丁詹姆斯的建议似乎是您的最佳选择。您可以使用 debug.setHook() 每 100 条 Lua 指令左右运行一些 lua 代码,如果在相同的客户端代码上花费了太多时间,则抛出错误。可以在this mailing list 上找到有关这方面的详细信息,在lua-project repo 上可以找到代码示例。

      【讨论】:

      • 好吧,你不能这样做,因为 DoString 方法被阻止执行无限循环。我将用更多信息更新我的原始 Q
      • 问题仍然是我无法找到一种方法来中止无限 DoString 调用而不破坏所有未来调用的 lua 引擎
      • 我还没有找到使用 C# 包装器从钩子事件中运行 LUA 代码的方法,您找到方法了吗?
      【解决方案4】:

      我使用了两种技术来解决这个问题。第一种是在单独的任务中调用 DoFile 或 DoString 方法。这样你就可以中止线程。我不知道退出解释器的任何技术(比如从钩子中)。

      另一种方法是设置一个单独的Appdomain。使用它,您还可以设置单独的约束(证据和权限集),详情请参阅CreateDomain

      【讨论】:

        【解决方案5】:

        在运行 lua 代码之前设置 line hook 怎么样?我不知道在 C# 中是否可能,但在原始 C lua 中是可能的。

        你的循环检测器是否已经在不同的 C# 任务中运行?

        • 如果是,请使用带有 C# 钩子的 C# 变量。在这种情况下,您的循环检测器设置并检查 C# 变量,如 terminateLua 并抛出 lua 错误(应该是可能的)。
        • 如果不是,并且您在 lua 端检测到循环(也使用钩子?),设置 lua 变量(使其上值以防止用户欺骗)并抛出错误。

        重要的部分是在您确定要完成任务之前不要重置此(C# 或 lua)变量,因为用户可以通过 pcall 捕获您的错误并尝试处理它。

        【讨论】:

          【解决方案6】:

          如果你想检测循环是否仍在运行,你可以这样做。

          local detect = false
          
          detect == true
          
          goal = 5
          star = 0
          
          function loop()
             while detect == true do
                 print("Still running")
             else
                 print("Loop stopped running")
             end
          end
          
          Spawn(loop)
          
          while true do
             if star > goal then
                detect = false
             end
          end
          

          输出将是:

          Still running
          Still running
          Still running
          Still running
          Still running
          Loop stopped running
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-10-14
            • 2021-01-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多