lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


> > I have a newbie question then: how should I call this function in an
> > *error-proof* way?
> > 
> >         -- takes 2 strings, returns a boolean
> >         handled = Function (arg1, arg2)
> > 
> >         // calls Function
> >         lua_getglobal(s_lua, "Function");
> >         lua_pushstring (s_lua, arg1);
> >         lua_pushstring (s_lua, arg2);
> >         if (lua_call (s_lua, 2, 1) == 0)
> >         {
> >             handled = (bool) lua_tonumber (s_lua, -1);
> >             lua_pop (s_lua, 1);
> >         }
> 
> That is difficult.  lua_getglobal, lua_pushstring and lua_tonumber
> may raise an error.  Even lua_pushcfunction/cclosure may raise one
> so it's pretty difficult to call something in a secure way.  A
> lua_catch function (similar to luaD_runprotected) would help a lot.
>
This is unfortunate. As Luiz pointed out though, this is less of a
problem when the functions being called are written in Lua. So is the
following any safer?

	lua_dostring (L, "return Function (arg1, arg2)");

This leaves whatever values "Function" returns on the stack and would
allow leaking if Function is not properly written.

Luiz wrote:
> >This must work (i.e., leave a clean stack) regardless of what 
> >"Function" evaluates to and whether the execution of "Function" 
> >succeeded or failed for whatever reasons.
>
> If you need this, I think the simplest solution is use lua_rawcall
instead > of lua_call (same arguments, but lua_rawcall is void). In this
case, if
> "Function" fails, then you'll get an ordinary error through
_ERRORMESSAGE.
>
Well, it then immediately calls exit() on my app. Are you assuming that
the lua_rawcall is itself protected by the lua_call() trick? If so, then
I guess it would work.

Thanks for all the info. Bottom line for me so far is that we need to be
extra careful with the stack. Lua can kill the app mercilessly when a
stack overflow occurs, which is unacceptable for us, so we need to wrap
all Lua API usage with a lua_call. I am planning to write a convenience
class that provides this behavior, and change the Lua sources to make
sure no Lua API calls can happen outside this context (that is, one that
doesn't have a setjmp to jump back to).

Is it possible to handle breakrun's in a different manner, even if it
involves dumping the entire Lua context?

In case anybody cares, I threw the following class together to help
reduce the occurrence of these problems. It fairly simple and certainly
not fail proof but helps catching certain careless uses of the stack.

Bruno

//
// This class helps control lua stack leaks and underflows, use as:
//   { LUA_SAFE_STACK_BLOCK(L); /* any lua code */ }

#define LUA_MAKE_BLOCK_NAME(x)  _LUA_BLOCK_NAME_##x##_
// __COUNTER__ is MS specific, but you don't really need it
#define LUA_SAFE_STACK_BLOCK(LuaState) CLuaSafeStack
LUA_MAKE_BLOCK_NAME(__COUNTER__) (LuaState)

class CLuaSafeStack
{
  public:
    CLuaSafeStack (lua_State* L)
        : m_lua (L)
        {
            m_top = lua_gettop (m_lua);
            if (m_top < 0)
            {
                ASSERTMSG (false, _T("Lua stack underflow, fixed"));
                m_top = 0;
                lua_settop (m_lua, m_top);
            }
        }
    
    ~CLuaSafeStack (void)
        {
            int endtop = lua_gettop (m_lua);
            if (endtop != m_top)    // stack leak
            {
                if (endtop < 0)
                    ASSERTMSG (false, _T("Lua stack underflow, fixed"));
                else
                    ASSERTMSG (false, _T("Lua stack leak, cleaned up"));
                // fix the stack to avoid any leaks (should not be
needed)
                lua_settop (m_lua, m_top);
            }
        }
  protected:
    lua_State* m_lua;
    int m_top;
};