[英]Set _ENV from C++ for string function
在我的項目中,我正在執行 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 代碼似乎不是很安全,所以我想設置代碼可以使用的函數的白名單。
在這個lua-users 討論之后,我創建了允許的函數列表,例如:
// 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 }
但這會導致 function 未正確加載和 Lua 錯誤
嘗試執行字符串值
我也嘗試過:
luaL_loadstring(L, whitelist.c_str());
lua_getglobal(L, "_ENV");
lua_setupvalue(L, -2, 1);
在執行加載的 XML function 之前,這不會使程序崩潰,但也不會為調用的 function 設置_ENV
。
我發現我想要的唯一方法是用 C++ 搜索()
解析 function 字符串,在它之后插入whitelist
,然后luaL_loadstring
和lua_pcall
兩次以執行它。
像這樣:
.
.
size_t n = code.find("()") + 2;
code.insert(n, whitelist);
std::string wrapped_code = "return " + code;
.
.
這有效並為 function 設置了我的自定義 _ENV,但在我看來,這對 go 來說是一種非常老套的方式。
如何以更好的方式為從 C 加載的字符串 function 設置 _ENV 變量?
如果有辦法為整個lua_State
保存一次,而不是每次調用 function 時,將獲得獎勵積分。
在深入研究示例之前,讓我們解釋一下如何(以及在什么級別)我們可以在 Lua 中或多或少地編寫沙箱代碼:
全局 - 刪除或從不將不需要的模塊/功能添加到全局環境中。
Chunk(ly) - 修改塊的_ENV
上值。
本地 - 通過 Lua 腳本中的本地或上值修改_ENV
。 然后它可以通過 upvalue 傳播給孩子。
查看您嘗試執行2或3的問題中提供的示例。
我不確定您的需求是什么,但我會嘗試為您提供上面列出的每種方法的示例。 請注意,這些示例不是最終示例,也不是唯一可能的方法,並且不要讓您的方法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 標准庫和linit.c 。 另見: luaL_requiref
。
除了選擇性庫加載之外,您還可以通過將選定的全局變量設置為nil
來刪除它們:
lua_pushnil(L);
lua_setglobal(L, "print");
您為 lua_State 設置一次。
讓我們將自己限制在一個非常基本的操作,即更改加載的主塊的第一個上值。 此類塊的第一個上值預計為_ENV
(參考下文)。
請注意,我說的是主要塊。 通常,您很少會加載期望除_ENV
之外的其他內容作為其第一個上值的塊,但記住這種情況總是很高興。
無論如何,請考慮以下示例:
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 環境和全球環境。
有關使用詳情,請參閱lua_setupvalue
文檔。
請參閱load_aux
中的另一個示例。 請注意,這是用於load
Lua 中的 function 的源代碼的一部分。 此 function 允許通過其 arguments 之一設置加載塊的環境(請參閱load
)。
您可以修改 upvalue _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
wrap(而不是(...)()
)。 這是因為local _ENV
將作為上值傳遞給返回的匿名 function。
有關 Lua 內部環境操作的更詳細說明,請參閱環境教程。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.