Skip to content

Commit

Permalink
Fix stack manipulation in Premake's luaL_loadfilex override.
Browse files Browse the repository at this point in the history
An example of code that did not work:

```lua
local env = { ["foo"] = function(n) print(n) end }
assert(pcall(loadfile("foo.lua", nil, env))
```
  • Loading branch information
tritao committed Sep 16, 2024
1 parent db072b4 commit afcbbb2
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 6 deletions.
37 changes: 31 additions & 6 deletions src/host/lua_auxlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ LUALIB_API int luaL_loadfilex (lua_State* L, const char* filename, const char* m
const char* script_dir;
const char* test_name;

/* this function can be called with from 1 to 3 arguments on the stack,
* the filename, the mode and an environment table */

int env = (!lua_isnone(L, 3) ? 1 : 0); /* 1 if there is an env or 0 if no 'env' */
int bottom = lua_gettop(L);
int z = !OKAY;

Expand Down Expand Up @@ -60,11 +64,11 @@ LUALIB_API int luaL_loadfilex (lua_State* L, const char* filename, const char* m
z = premake_load_embedded_script(L, test_name + 2); /* Skip over leading "$/" */

/* remove test_name */
lua_remove(L, bottom + 1);
lua_remove(L, -3);
}

/* remove _SCRIPT_DIR */
lua_remove(L, bottom);
lua_remove(L, bottom + env);
}

/* Try to locate the script on the filesystem */
Expand Down Expand Up @@ -100,7 +104,15 @@ LUALIB_API int luaL_loadfilex (lua_State* L, const char* filename, const char* m
* script chunk on the stack. Turn these into a closure that will call my
* wrapper below when the loaded script needs to be executed. */
if (z == OKAY) {
lua_pushcclosure(L, chunk_wrapper, 2);
/* if we are called with an env, then our caller, luaB_loadfile, will
* call load_aux, which sets up our env as the first up value via
* lua_setupvalue, which would overwrite the one we are setting up here.
* workaround this by pushing a nil value as our first up value */
if (env) {
lua_pushnil(L);
lua_insert(L, -3);
}
lua_pushcclosure(L, chunk_wrapper, 2 + (env ? 1 : 0));
}
else if (z == LUA_ERRFILE) {
lua_pushfstring(L, "cannot open %s: No such file or directory", filename);
Expand All @@ -124,9 +136,14 @@ static int chunk_wrapper(lua_State* L)
const char* filename;
char* ptr;
int i, args;
int upvalue_offset;

args = lua_gettop(L);

/* if the first up value is a table, then we have an env upvalue
* and should take that into account by offsetting the rest of the up values */
upvalue_offset = (lua_type(L, lua_upvalueindex(1)) == LUA_TTABLE) ? 1 : 0;

/* Remember the current _SCRIPT and working directory so I can
* restore them after this new chunk has been run. */

Expand All @@ -136,12 +153,12 @@ static int chunk_wrapper(lua_State* L)

/* Set the new _SCRIPT variable */

lua_pushvalue(L, lua_upvalueindex(1));
lua_pushvalue(L, lua_upvalueindex(1 + upvalue_offset));
lua_setglobal(L, "_SCRIPT");

/* And the new _SCRIPT_DIR variable (const cheating) */

filename = lua_tostring(L, lua_upvalueindex(1));
filename = lua_tostring(L, lua_upvalueindex(1 + upvalue_offset));
ptr = strrchr(filename, '/');
if (ptr) *ptr = '\0';
lua_pushlstring(L, filename, strlen(filename));
Expand All @@ -157,7 +174,15 @@ static int chunk_wrapper(lua_State* L)
/* Move the function's arguments to the top of the stack and
* execute the function created by luaL_loadfile() */

lua_pushvalue(L, lua_upvalueindex(2));
lua_pushvalue(L, lua_upvalueindex(2 + upvalue_offset));

/* forward the env table to the closure as 1st upvalue */
if (upvalue_offset) {
lua_pushvalue(L, -1);
lua_pushvalue(L, lua_upvalueindex(1));
lua_setupvalue(L, -2, 1);
}

for (i = 1; i <= args; ++i) {
lua_pushvalue(L, i);
}
Expand Down
1 change: 1 addition & 0 deletions tests/_tests.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
return {
-- Base API tests
"test_lua.lua",
"test_string.lua",
"base/test_aliasing.lua",
"base/test_binmodules.lua",
Expand Down
37 changes: 37 additions & 0 deletions tests/test_lua.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
--
-- tests/test_lua.lua
-- Automated test suite for Lua base functions.
-- Copyright (c) 2008 Jason Perkins and the Premake project
--

local suite = test.declare("lua")

--
-- loadfile
--

function suite.loadfile()
local file = path.join(_SCRIPT_DIR, "test_lua_loaded_noenv.lua")
local fn = assert(loadfile(file, nil))
local ret, value = pcall(fn)
test.isequal(10, value)
end




--
-- loadfile with custom env
--

function suite.loadfile_with_env()
local file = path.join(_SCRIPT_DIR, "test_lua_loaded.lua")
local value = 0
local env = {
["foobar"] = function(n) value = n end
}
local fn = assert(loadfile(file, nil, env))
pcall(fn)
test.isequal(10, value)
end

1 change: 1 addition & 0 deletions tests/test_lua_loaded.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
foobar(10)
1 change: 1 addition & 0 deletions tests/test_lua_loaded_noenv.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
return 10

0 comments on commit afcbbb2

Please sign in to comment.