premake / premake-4.x

Premake 4
BSD 3-Clause "New" or "Revised" License
74 stars 20 forks source link

Rather subtle defect in `path_translate` #7

Open assarbad opened 8 years ago

assarbad commented 8 years ago

This took me a while to track down.

In path_translate from path_translate.c the handling is wrong in cases where a table is passed. The lua_next function requires that the keys in the table not be changed (or new values inserted, which also means a change of keys).

However, the luaL_checkstring will just do that in cases where the key is not a string and convert it into a string.

Reference: http://stackoverflow.com/a/6142700

The error would then be invalid key to 'next' which indicates that the key of a table was changed mid-iteration:

invalid key to 'next'
stack traceback:
        [C]: in function 'builtin_translate'
        src/base/path.lua:148: in function 'translate'
        D:/premake-wds/src/actions/vstudio/vs200x_vcproj.lua:366: in function '?'
        D:/premake-wds/src/actions/vstudio/vs200x_vcproj.lua:743: in function 'callback'
        D:/premake-wds/src/base/premake.lua:32: in function 'orig_generate'
        D:/premake-wds/premake4.lua:72: in function 'generate'
        D:/premake-wds/src/actions/vstudio/_vstudio.lua:322: in function 'onproject'
        D:/premake-wds/src/base/action.lua:59: in function 'call'
        src/_premake_main.lua:155: in function <src/_premake_main.lua:53>

(it's from my fork of premake4, but the same generally applies to other premake4 flavors and possibly even premake5).

The fix is trivial. Take a copy of the key, then use luaL_checkstring on the copy and pop 2 values from the stack instead of one.

Here's the changed condition:

    if (lua_istable(L, 1)) {
        int i = 0;
        lua_newtable(L);
        lua_pushnil(L);
        while (lua_next(L, 1)) {
            const char* key;
            lua_pushvalue(L, 4); // copy the key
            key = luaL_checkstring(L, 5);
            translate(buffer, key, sep[0]);
            lua_pop(L, 2);

            lua_pushstring(L, buffer);
            lua_rawseti(L, -3, ++i);
        }
        return 1;
    }