防止 Lua 无限循环

我使用 Lua 接口在我的 C# 程序中支持 Lua,如果用户提交了如下代码,工作线程会冻结:

while true do end

我有一种方法来检测无限循环是否在运行,但我需要一种漂亮的方式从工作线程中退出 DoString 方法。有什么想法吗?

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

编辑:一个特性分支展示了我的 .Close 代码 https://github.com/AndersMalmgren/FreePIE/tree/detect-and-recover-infite-lua-loop

原文链接 https://stackoverflow.com/questions/10352092

点赞
stackoverflow用户312586
stackoverflow用户312586

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

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

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

你处理这个问题的方式将取决于你如何处理 Lua 错误,但你无论如何都必须处理它,所以大多数代码可能已经存在了。

编辑:似乎 Martin James 的建议是你最好的选择。你可以使用 debug.setHook() 每隔大约 100 条 Lua 指令运行一些 Lua 代码,如果同一客户端的代码执行的时间太长,则抛出错误。关于此的详细信息可以在 此邮件列表 上找到,带有示例代码的库可以在 lua-project repo 上找到。

2012-04-27 22:00:19
stackoverflow用户1685701
stackoverflow用户1685701

Lua的沙盒设置

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

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

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

Lua的好处在于,您不需要进行任何复杂且依赖于操作系统的权限设置来进行沙箱设置:您只需要限制时间和内存(合理的——在Windows上有作业对象,Unix有ulimits-相关:Linux资源限制),然后将诸如os.execute、io库的一半以及像luasocket这样的模块远离脚本编写者(非常容易)。

从沙盒代码中恢复错误

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


[我不想让人们在瞥一眼这个线程时就认为调试.sethook足以进行沙盒设置,而stackoverflow也不让我评论(但)]

2012-09-24 23:38:30
stackoverflow用户108238
stackoverflow用户108238

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

另一种方法是设置一个独立的Appdomain。使用这个方法,你也可以设置单独的限制(证据和权限集),详情请见CreateDomain

2014-09-17 19:05:57
stackoverflow用户5697743
stackoverflow用户5697743

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

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

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

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

2018-08-25 09:24:29
stackoverflow用户12230942
stackoverflow用户12230942

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

2020-02-07 06:44:58
stackoverflow用户17088770
stackoverflow用户17088770

如果你想要检测一个循环是否仍在运行,你可以像这样完成。

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
2021-10-06 11:31:27