3
votes

A C++ program I'm working on is using lua for configuration. It sets up several lua functions using a hardcoded script:

luaL_loadbuffer(pmLuaState, headerscript, strlen(headerscript), "header script");

It then loads the configuration lua file (that calls the previously mentioned functions):

luaL_loadfile(pmLuaState, filename)

Unfortunately, the Lua configuration file uses a global variable named type, so attempting to call the built-in type() function from my headerscript fails with a Lua error:

attempt to call global 'type' (a string value)

Due to the constraints of my scenario, I am unable to edit the offending config file. I'm wondering if there's a way to explicity state that I want to use the built-in type() function.

I'm new to Lua, so if there's a better way to load in these scripts that will avoid this global hiding issue, I'm open to it (but again, I cannot edit the config script).

Lua version: 5.1.4

2
Could you bind the type function to another name before loading the config, then restore the type global to point back to the function after loading the config? Or will this screw up stuff (e.g. functions in config that assume the global type will be the redefined global variable instead of the function). - Lily Ballard
This might work. How would I go about binding the type function? - KC3BZU

2 Answers

3
votes

Once the global variable has been modified, the value cannot be recovered- that is, you must prevent the script from setting _G.type in the first place, or store the value beforehand. The second is relatively trivial

local type = type -- problem solved

or you can use environments to solve this problem. This is a pretty common script to run.

local loadedmodules = {}
import = function(filename)
    if (loadedmodules[filename]) then
        return loadedmodules[filename]
    end
    local env = {}
    local func = loadfile(filename);
    local envmeta = {
        __index = _G -- changed thanks to comments
    }
    setmetatable(env, envmeta) -- Allow the imported script to read only our globals
    setfenv(func, env) -- set func's _G to env
    func() -- Load the script
    return loadedmodules[filename] = env -- Set the result and return it.
end

This would be the C++ equivalent of automatic header guards and macros being automatically scoped to the defining file.

Once you define this script from Lua, you can load it from C++ and call it relatively trivally. I don't recommend doing any extensive Lua logic from C++, the stack-based API is very hideous.

3
votes

You should sandbox config files so that they would not affect the global environment of the whole program.

Call lua_setfenv() with the empty table as an environment on the chunk, returned by luaL_load*().

Update: if config file needs stuff from global environment, expose it with metatables.

In Lua:

setfenv(chunk, setmetatable({__index = _G }))

(Read the manual to realize how to write it in C.)

Note that this method of sandboxing is too weak for untrusted code. For example, config would be able to do stuff like table.concat = myevilfunction. See Wiki page linked above for full-blown sandboxing approaches (which should be overkill in your case though).