Lua 5.2中的沙盒技术

5

我正在学习罗伯托·伊鲁萨利姆希的《Lua编程》,在书中发现沙箱示例使用setfenv()函数来更改给定函数的环境,但在lua 5.2中,此函数不再可用。

我尝试从文件(配置文件)加载一些值到表中的字段中,但在lua 5.2中,我不能使用setfenv(所以无法在给定环境中加载值)。阅读了一些关于lua 5.2的文章后,我发现每个函数可能有(也可能没有)一个名为_ENV的upvalue,用作环境。因此,我尝试了以下代码:

function sandbox(sb_func, sb_env)
    if not sb_func then return nil, "sandbox function not valid" end
    sb_orig_env = _ENV
    _ENV = sb_env -- yes, replaces the global _ENV
    pcall_res, message = pcall( sb_func )
    local modified_env = _ENV -- gets the environment that was used in the pcall( sb_func )
    _ENV = sb_orig_env
    return true, modified_env
end

function readFile(filename)
    code = loadfile(filename)
    res, table = sandbox(code, {})
    if res then
        --[[ Use table (modified_env) ]]--
    else
        print("Code not valid")
end

将 'sandbox' 函数中的 _ENV 替换掉可以正常工作(无法访问常规字段),但是当执行 'code' 时,它似乎忽略了我替换的 _ENV,仍然可以访问常规字段(如 print、loadfile、dofile 等)。

阅读更多资料后,我发现 lua 5.2 提供了一个用于此目的的函数,该函数是 loadin(env, chunk),其在给定的环境中运行给定的代码块,但是当我尝试将此函数添加到我的代码时,该函数不存在(不在全局 _G 字段中)。

希望能得到一些帮助。

1个回答

7
当你在sandbox中从_ENV分配时,你并没有覆盖全局环境 - 你是替换当前正在运行代码的_ENV upvalue。添加调用print(_ENV)可能会帮助你更好地理解涉及到的表的身份。
例如:
function print_env()
  print(_ENV)
end

function sandbox()
  print(_ENV) -- prints: "table: 0x100100610"
  -- need to keep access to a few globals:
  _ENV = { print = print, print_env = print_env, debug = debug, load = load }
  print(_ENV) -- prints: "table: 0x100105140"
  print_env() -- prints: "table: 0x100105140"
  local code1 = load('print(_ENV)')
  code1()     -- prints: "table: 0x100100610"
  debug.setupvalue(code1, 1, _ENV) -- set our modified env
  code1()     -- prints: "table: 0x100105140"
  local code2 = load('print(_ENV)', nil, nil, _ENV) -- pass 'env' arg
  code2()     -- prints: "table: 0x100105140"
end

loadin函数曾出现在Lua 5.2的一些预发布版本中,但在最终版发布之前被删除。相反,Lua 5.2的loadloadfile函数接受一个env参数。您还可以使用debug.setupvalue修改另一个函数的_ENV


1
谢谢!解决方案很简单。我认为在loadloadfile中更改环境的这种方法更好,因为setfenv主要用于加载的代码,所以...谢谢!我已按预期进行了沙盒处理。 - Daniel Rivas
@Miles 当 print_env() 被定义时,它不是接收全局 _ENV 作为 upvalue 吗?那么当 print_env() 在 sandbox() 中被调用时,它是如何改变的呢? - Tiago Costa
1
@TiagoCosta upvalues 是外部局部变量。在这种情况下,_ENV 局部于代码块;对 _ENV 的赋值会影响该代码块的局部变量,这是两个闭包共享的上值。 - Miles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接