簡體   English   中英

從 C++ 為字符串 function 設置 _ENV

[英]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_loadstringlua_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 中或多或少地編寫沙箱代碼:

  1. 全局 - 刪除或從不將不需要的模塊/功能添加到全局環境中。

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

  3. 本地 - 通過 Lua 腳本中的本地或上值修改_ENV 然后它可以通過 upvalue 傳播給孩子。

查看您嘗試執行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 標准庫linit.c 另見: luaL_requiref

除了選擇性庫加載之外,您還可以通過將選定的全局變量設置為nil來刪除它們:

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

您為 lua_State 設置一次。

大塊(ly)

讓我們將自己限制在一個非常基本的操作,即更改加載的主塊的第一個上值。 此類塊的第一個上值預計為_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM