[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: lua calling exit()
- From: Edgar Toernig <froese@...>
- Date: Wed, 22 Aug 2001 00:48:17 +0200
Bruno Silva wrote:
>
> > > 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.
Hmm... it doesn't matter whether the function called by lua_call is a
C or Lua function. Results are 100% the same.
> So is the following any safer?
>
> lua_dostring (L, "return Function (arg1, arg2)");
This will avoid possible errors from lua_getglobal and lua_pushstring.
But somehow you have to pass arg1/arg2 to Lua and you have to fetch
the results. So practically it will not help you very much.
(Btw, lua_dostring _compiles_ the string so it's pretty slow.)
> This leaves whatever values "Function" returns on the stack and would
> allow leaking if Function is not properly written.
I don't understand what problems you have with "stack leaks". Where's
your problem of properly using the API? Your first example above
using lua_call is safe in this regard. It will always leave the stack
as it was before the lua_getglobal. The lua_dostring example alone
will leave things on the stack, but what would you expect?
Either omit the return:
lua_dostring(L, "Function(arg1, arg2)"); // ignore failure, no results
or force it to return 2 results (2nd is nil):
if (lua_dostring(L, "return Function(arg1, arg2), nil") == 0) {
... // process result at index -2 (at -1 is the nil)
lua_pop(L, 2);
} else error...
or clean up afterwards:
int x = lua_gettop(L);
if (lua_dostring(L, "return Function(arg1, arg2)" == 0) {
... // process var num of results between x and lua_gettop()
lua_settop(L, x); // remove all results
} else error...
All these three variants will leave a clean stack and will not call
exit() (unless Function calls Lua's exit of course ;-).
The possibly raised errors in your first example are:
- lua_getglobal may invoke tag methods and by that executes arbitrary
code and can generate all kind of errors.
- lua_pushstring may get an out of memory error.
- lua_getglobal and lua_pushstring may get a stack overflow.
- If you'd used lua_tostring instead of lua_tonumber you could get
an out of memory there too.
That're the only possible errors. You don't have to protect against
errors occurring during the lua_call execution (lua_call takes care
of them and returns an appropriate status) but errors while setting
up the arguments for and processing the results of lua_call.
Unfortunately it's pretty difficult to protect against these errors.
> 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).
_I_ would simply use the (undocumented) function luaD_runprotected (it
creates a setjmp context). It's already extern; only the prototype is
missing. I.e. to make your first example safe:
// -- Prototype for luaD_runprotected(L, f, ud) --
// It calls f with the ud ptr as second arg and catches any Lua errors
// that may happen while executing f.
// If an error occurs, the stack pointer is reset to the value it had
// before calling f and the error status is returned (LUA_ERRxxx).
// If everything goes well, the stack is left as f had set it and 0 is
// returned.
int luaD_runprotected (lua_State *L, void (*f)(lua_State *, void *), void *ud);
struct callenv { char *func, *arg1, *arg2; bool result; };
// this func pushes the arguments, calls the function and converts
// the results. It'll be called via luaD_runprotected.
static void callit(lua_State *L, void *x)
{
struct callenv *env = (struct callenv *)x;
lua_getglobal(L, env->func);
lua_pushstring(L, env->arg1);
lua_pushstring(L, env->arg2);
lua_rawcall(L, 2, 1); // no need for lua_call now
env->result = (bool)lua_tonumber(L, -1);
// pop results. luaD_runprotected only cleans up in case of errors.
lua_pop(L, 1);
}
...
// call func with arg1, arg2 - return bool (or garbage if func fails)
bool call(char *func, char *arg1, char *arg2)
{
struct callenv env;
env.func = func;
env.arg1 = arg1;
env.arg2 = arg2;
if (luaD_runprotected(L, callit, &env) == 0)
printf("results is %d\n", env.result);
else
printf("error calling %s\n", env.func);
return env.result;
}
If you are paranoid about functions like callit leaving elements on the
stack you could check it around the luaD_runprotected call. But another
note here: if the stack after calling luaD_runprotected is lower then
before you have a stack underflow and you can't recover that case. Some
elements on the stack (or even memory outside the stack) will be damaged.
Only faulty C code can produce a stack underflow and an abort/exit is the
only sane thing to do.
I hope that a luaD_runprotected-like function will be available in Lua 4.1.
In Sol I called it sol_catch:
sol_Error sol_catch(sol_State *, void (f)(sol_State *, va_list), ...)
and f gets the va_list of the varargs of sol_catch.
Ciao, ET.