在我的项目中,我正在执行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用户讨论之后,我创建了允许的功能列表,例如:
// Of course my list is bigger
std::string whitelist = "sandbox_env = {ipairs = ipairs} _ENV = sandbox_env"
问题是我不明白如何加载它以在我正在调用的函数中使用。
我试图像在lua用户网站上那样做:
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错误
试图执行字符串值
我也尝试这样做:
luaL_loadstring(L, whitelist.c_str());
lua_getglobal(L, "_ENV");
lua_setupvalue(L, -2, 1);
在执行加载的XML函数之前,这不会使程序崩溃,但也不会为调用的函数设置_ENV
。
我发现拥有我想要的唯一方法是用C ++搜索()
来解析函数字符串,在其后插入whitelist
,然后两次luaL_loadstring
和lua_pcall
来执行它。
喜欢这个:
.
.
size_t n = code.find("()") + 2;
code.insert(n, whitelist);
std::string wrapped_code = "return " + code;
.
.
这可以正常工作,并为该函数设置我的自定义_ENV,但在我看来,这是一种非常骇人听闻的方法。
如何为C语言加载的字符串函数设置_ENV变量,方法更好?
奖励指向是否有办法为整个lua_State
而不是每次调用函数都保存一次。
在深入探讨您的示例之前,让我们解释一下如何(以及在什么级别上)在Lua中或多或少地实现沙盒代码:
全局-删除或永远不要将不需要的模块/功能添加到全局环境中。
Chunk(ly)-修改块的_ENV
高值。
本地-创建和修改本地_ENV
。然后可以通过升值将其传播给子代。
查看问题中提供的示例,您尝试执行2或3。
我不确定您的需求是什么,但是我将尝试为您提供上述每种方法的示例。请注意,这些示例不是最终示例,也不是唯一可能的方法。
这很简单。如果您不打算使用提供的所有库...
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);
参考6 Standard Libraries,linit.c和luaL_requiref
。
除了选择性库加载之外,您还可以通过将选定的全局变量设置为luaL_requiref
来删除它们:
nil
这个不仅很大,而且非常接近下一个。
让我们局限于更改加载的主块的第一个上限值的非常基本的操作。请注意,我说的是[[main块。通常,您很少会加载期望lua_pushnil(L);
lua_setglobal(L, "print");
以外的东西作为其第一个升值的块,但是记住这一点总是很高兴的。
_ENV
您可以通过多种方式实现()。您可以使用Lua C API:A
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);
或者您可以通过lua_newtable(L); lua_pushliteral(L, "print2"); lua_getglobal(L, "print"); lua_settable(L, -3);
:
dostring
如果有疑问,请参考:lua_dostring(L, "return { print2 = print}");
,2.2 Environments and the Global Environment和lua_setupvalue
。本地
最后一点!
您可以修改升值
lua_setupvalue
或用local覆盖它。让我们稍后再做:
这意味着您可以包装已加载的脚本并像这样运行它:
load_aux
这也适用于_ENV
换行(而不是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);