防止Lua无限循环

本文关键字:无限循环 Lua 防止 | 更新日期: 2023-09-27 18:32:23

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

while true do end

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

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

编辑:显示我的.关闭代码https://github.com/AndersMalmgren/FreePIE/tree/detect-and-recover-infite-lua-loop

防止Lua无限循环

Sandboxing Lua

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

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

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

Lua的好处是你不需要任何复杂的,依赖于操作系统的权限设置来沙盒:你只需要限制时间和内存(合理;在Windows上有作业对象,Unix有ulimits相关:Linux资源限制),然后保持像os.execute这样的东西,一半的io库和像luasocket这样的模块远离脚本器(很容易)。

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

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

<小时 />

[我不希望人们瞥一眼这个线程,认为debug.sethook足以用于沙盒,并且stackoverflow不允许我发表评论(还)]

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

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

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

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

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

编辑:看来马丁詹姆斯的建议是你最好的选择。您可以使用 debug.setHook() 每 100 条左右的 Lua 指令运行一些 lua 代码,如果在同一客户端代码上经过了太多时间,则抛出错误。有关此内容的详细信息可以在此邮件列表中找到,也可以在 lua-project 存储库中找到代码示例。

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

另一种方法是设置一个单独的应用程序域。使用此功能,您还可以设置单独的约束(证据和权限集),有关详细信息,请参阅 CreateDomain。

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

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

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

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

如果你想

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

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