gpt4 book ai didi

c++ - 从 C++ 为字符串函数设置 _ENV

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:01:44 25 4
gpt4 key购买 nike

在我的项目中,我正在执行 XML 文件中包含的一些 lua 函数。我从 C++ 读取 XML,解析代码字符串,执行它们并获得结果。我发现的所有相关问题要么使用专用的 .lua 文件,要么直接在 Lua 中完成,但我找不到适合我的案例的解决方案。

我无法修改文件中的函数,它们都具有以下签名:

function() --or function ()
--do stuff
return foo
end

从 C++ 我像这样加载它们:

lua_State *L = luaL_newstate();
luaL_openlibs(L);
std::string code = get_code_from_XML();
std::string wrapped_code = "return " + code;
luaL_loadstring(L, wrapped_code.c_str());
if (lua_pcall(L, 0, 1, 0)){
return 1;
}

argNum = load_arg_number();

if (lua_pcall(L, argNum, 1, 0)){
return 1;
}
return 0;

一切正常,但从 XML 字符串运行任意 Lua 代码似乎不太安全,所以我想设置一个代码可以使用的函数白名单。

正在关注 this lua-users discussion 我创建了我的允许功能列表,例如:

// Of course my list is bigger
std::string whitelist = "sandbox_env = {ipairs = ipairs} _ENV = sandbox_env"

问题是我不明白如何加载它以便在我调用的函数中可用。

我试着像在 lua-users 网站上那样做:

std::string Lua_sandboxed_script_to_run( Lua_sandboxing_script + Lua_script_to_run )

if (luaL_dostring(sandboxed_L, Lua_sandboxed_script_to_run))
{
// error checking
}

但是这样会导致函数加载不正确,出现Lua错误

Trying to execute a string value

我也试过:

luaL_loadstring(L, whitelist.c_str());
lua_getglobal(L, "_ENV");
lua_setupvalue(L, -2, 1);

在执行加载的 XML 函数之前,这不会使程序崩溃,但也不会为被调用的函数设置 _ENV

我发现拥有我想要的东西的唯一方法是用 C++ 搜索 () 解析函数字符串,在它之后插入 whitelist 然后 luaL_loadstringlua_pcall 执行它两次。

像这样:

.
.
size_t n = code.find("()") + 2;
code.insert(n, whitelist);
std::string wrapped_code = "return " + code;
.
.

这有效并为该函数设置了我的自定义 _ENV,但在我看来这是一种非常 hacky 的方式。

如何以更好的方式为从 C 加载的字符串函数设置 _ENV 变量?

如果有一种方法可以为整个 lua_State 保存一次而不是每次我调用一个函数时,就加分。

最佳答案

在深入研究示例之前,让我们解释一下我们如何(以及在​​什么级别)在 Lua 中或多或少地编写沙箱代码:

  1. 全局 - 删除或从不将不需要的模块/函数添加到全局环境中。

  2. Chunk(ly) - 修改 block 的 _ENV 上值。

  3. 本地 - 通过 Lua 脚本中的本地或上值修改 _ENV。它可以通过 upvalue 传播给 child 。

查看问题中提供的示例,您尝试执行 23

我不确定您的需求是什么,但我会尝试为您提供上面列出的每种方法的示例。请注意,这些示例不是最终示例,也不是唯一可能的方法,并且不要采用您的 return function () ... end 包装器。选择任何适合您的。

全局

这个非常简单。如果您不打算使用所提供的所有库...

lua_State * L = luaL_newstate();
luaL_openlibs(L); // <-- Remove this

...然后就不要加载它们了:

lua_State * L = luaL_newstate();
// Instead of luaL_openlibs:
luaL_requiref(L, "_G", luaopen_base, 1);
luaL_requiref(L, LUA_MATHLIBNAME, luaopen_math, 1);
luaL_requiref(L, LUA_TABLIBNAME, luaopen_table, 1);
// luaL_requiref pushes to stack - clean it up:
lua_pop(L, 3);
// Load and execute desired scripts here.

引用6 Standard Librarieslinit.c可用库的列表。另请参阅:luaL_requiref .

除了选择性的库加载之外,您还可以通过将它们设置为 nil 来删除选定的全局变量:

lua_pushnil(L);
lua_setglobal(L, "print");

你为 lua_State 设置一次。

大块(ly)

让我们将自己限制在一个非常基本的操作上,即更改加载的主 block 的第一个上值。此类 block 的第一个上值预计为 _ENV(引用下文)。

请注意,我说的是主要 block 。通常,您很少会加载期望除 _ENV 以外的东西作为其第一个上值的 block ,但记住这种情况总是好的。

无论如何,请考虑以下示例:

lua_State * L = luaL_newstate();
luaL_openlibs(L);
luaL_loadstring(L, "print2 \"Hello there\"");
// (A) Create a table with desired environment and place it at the top of the stack.
// Replace this comment with any of the options described below
lua_setupvalue(L, -2, 1);
lua_call(L, 0, 0);

您可以通过多种方式实现 (A)。您可以使用 Lua C API:

lua_newtable(L);
lua_pushliteral(L, "print2");
lua_getglobal(L, "print");
lua_settable(L, -3);

或者你可以通过 dostring 来完成:

lua_dostring(L, "return { print2 = print }");

或者您可以尝试另一种方法,类似于问题中的方法:

lua_dostring(L, "sandbox_env = { print2 = print }"); // Without _ENV = sandbox_env
lua_getglobal(L, "sandbox_env");

关于环境的一般引用:2.2 Environments and the Global Environment .

参见 lua_setupvalue有关使用详情的文档。

请参阅 load_aux 中的另一个示例.请注意,这是 Lua 中可用的 load 函数源代码的一部分。此函数允许通过其参数之一设置加载 block 的环境(参见 load )。

本地

您可以修改上值 _ENV 或直接在 Lua 脚本中用本地覆盖它。让我们做后者:

local _ENV = { print2 = print }
(function ()
print2("Hello there")
end)()

这意味着您可以包装加载的脚本并像这样运行它:

std::string loaded_script /* = ... */;
std::string wrapped_script = "local _ENV = { print2 = print }; (" + loaded_script + ")()";
luaL_loadstring(L, wrapped_script.c_str());
lua_call(L, 0, 0);

这也适用于 return function () ... end 换行(而不是 (...)())。这是因为 local _ENV 将作为上值传递给返回的匿名函数。

引用Environments Tutorial有关 Lua 内部环境操作的更详细解释。

关于c++ - 从 C++ 为字符串函数设置 _ENV,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58604393/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com