Search code examples
variablesluaredisglobal-variables

Script attempted to create global variable


I would like to load one script into redis that will export functions that future scripts executed will depend on, but attempt to define global function fails, same goes for global variables:

redis 127.0.0.1:6379> EVAL "function alex() return 3.1415 end" 0
(error) ERR Error running script (call to f_f24a5a054d91ccc74c2629e113f8f639bbedbfa2): user_script:1: Script attempted to create global variable 'alex'

How can I define global functions and variables ?


Solution

  • Looking at the source code in file scripting.c

    /* This function installs metamethods in the global table _G that prevent
     * the creation of globals accidentally.
     *
     * It should be the last to be called in the scripting engine initialization
     * sequence, because it may interact with creation of globals. */
    void scriptingEnableGlobalsProtection(lua_State *lua) {
        char *s[32];
        sds code = sdsempty();
        int j = 0;
    
        /* strict.lua from: http://metalua.luaforge.net/src/lib/strict.lua.html.
         * Modified to be adapted to Redis. */
        s[j++]="local mt = {}\n";
        s[j++]="setmetatable(_G, mt)\n";
        s[j++]="mt.__newindex = function (t, n, v)\n";
        s[j++]="  if debug.getinfo(2) then\n";
        s[j++]="    local w = debug.getinfo(2, \"S\").what\n";
        s[j++]="    if w ~= \"main\" and w ~= \"C\" then\n";
        s[j++]="      error(\"Script attempted to create global variable '\"..tostring(n)..\"'\", 2)\n";
        s[j++]="    end\n";
        s[j++]="  end\n";
        s[j++]="  rawset(t, n, v)\n";
        s[j++]="end\n";
        s[j++]="mt.__index = function (t, n)\n";
        s[j++]="  if debug.getinfo(2) and debug.getinfo(2, \"S\").what ~= \"C\" then\n";
        s[j++]="    error(\"Script attempted to access unexisting global variable '\"..tostring(n)..\"'\", 2)\n";
        s[j++]="  end\n";
        s[j++]="  return rawget(t, n)\n";
        s[j++]="end\n";
        s[j++]=NULL;
    
        for (j = 0; s[j] != NULL; j++) code = sdscatlen(code,s[j],strlen(s[j]));
        luaL_loadbuffer(lua,code,sdslen(code),"@enable_strict_lua");
        lua_pcall(lua,0,0,0);
        sdsfree(code);
    }
    

    The doc-string of scriptingEnableGlobalsProtection indicates that intent is to notify script authors of common mistake (not using local).

    It looks like this is not security feature, so we have two solutions:

    One can remove this protection:

    local mt = setmetatable(_G, nil)
    -- define global functions / variables
    function alex() return 3.1415 end
    -- return globals protection mechanizm
    setmetatable(_G, mt)
    

    Or use rawset:

    local function alex() return 3.1415 end
    rawset(_G, "alex", alex)