mirror of https://github.com/rusefi/lua.git
Fixed bug: invalid 'oldpc' when returning to a function
The field 'L->oldpc' is not always updated when control returns to a function; an invalid value can seg. fault when computing 'changedline'. (One example is an error in a finalizer; control can return to 'luaV_execute' without executing 'luaD_poscall'.) Instead of trying to fix all possible corner cases, it seems safer to be resilient to invalid values for 'oldpc'. Valid but wrong values at most cause an extra call to a line hook.
This commit is contained in:
parent
1ecfbfa1a1
commit
a2195644d8
41
ldebug.c
41
ldebug.c
|
@ -33,10 +33,8 @@
|
||||||
|
|
||||||
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
|
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
|
||||||
|
|
||||||
|
/* inverse of 'pcRel' */
|
||||||
/* Active Lua function (given call info) */
|
#define invpcRel(pc, p) ((p)->code + (pc) + 1)
|
||||||
#define ci_func(ci) (clLvalue(s2v((ci)->func)))
|
|
||||||
|
|
||||||
|
|
||||||
static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
|
||||||
const char **name);
|
const char **name);
|
||||||
|
@ -127,20 +125,18 @@ static void settraps (CallInfo *ci) {
|
||||||
/*
|
/*
|
||||||
** This function can be called during a signal, under "reasonable"
|
** This function can be called during a signal, under "reasonable"
|
||||||
** assumptions.
|
** assumptions.
|
||||||
** Fields 'oldpc', 'basehookcount', and 'hookcount' (set by
|
** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount')
|
||||||
** 'resethookcount') are for debug only, and it is no problem if they
|
** are for debug only, and it is no problem if they get arbitrary
|
||||||
** get arbitrary values (causes at most one wrong hook call). 'hookmask'
|
** values (causes at most one wrong hook call). 'hookmask' is an atomic
|
||||||
** is an atomic value. We assume that pointers are atomic too (e.g., gcc
|
** value. We assume that pointers are atomic too (e.g., gcc ensures that
|
||||||
** ensures that for all platforms where it runs). Moreover, 'hook' is
|
** for all platforms where it runs). Moreover, 'hook' is always checked
|
||||||
** always checked before being called (see 'luaD_hook').
|
** before being called (see 'luaD_hook').
|
||||||
*/
|
*/
|
||||||
LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
|
LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
|
||||||
if (func == NULL || mask == 0) { /* turn off hooks? */
|
if (func == NULL || mask == 0) { /* turn off hooks? */
|
||||||
mask = 0;
|
mask = 0;
|
||||||
func = NULL;
|
func = NULL;
|
||||||
}
|
}
|
||||||
if (isLua(L->ci))
|
|
||||||
L->oldpc = L->ci->u.l.savedpc;
|
|
||||||
L->hook = func;
|
L->hook = func;
|
||||||
L->basehookcount = count;
|
L->basehookcount = count;
|
||||||
resethookcount(L);
|
resethookcount(L);
|
||||||
|
@ -795,10 +791,24 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Traces the execution of a Lua function. Called before the execution
|
||||||
|
** of each opcode, when debug is on. 'L->oldpc' stores the last
|
||||||
|
** instruction traced, to detect line changes. When entering a new
|
||||||
|
** function, 'npci' will be zero and will test as a new line without
|
||||||
|
** the need for 'oldpc'; so, 'oldpc' does not need to be initialized
|
||||||
|
** before. Some exceptional conditions may return to a function without
|
||||||
|
** updating 'oldpc'. In that case, 'oldpc' may be invalid; if so, it is
|
||||||
|
** reset to zero. (A wrong but valid 'oldpc' at most causes an extra
|
||||||
|
** call to a line hook.)
|
||||||
|
*/
|
||||||
int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
||||||
CallInfo *ci = L->ci;
|
CallInfo *ci = L->ci;
|
||||||
lu_byte mask = L->hookmask;
|
lu_byte mask = L->hookmask;
|
||||||
|
const Proto *p = ci_func(ci)->p;
|
||||||
int counthook;
|
int counthook;
|
||||||
|
/* 'L->oldpc' may be invalid; reset it in this case */
|
||||||
|
int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
|
||||||
if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
|
if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
|
||||||
ci->u.l.trap = 0; /* don't need to stop again */
|
ci->u.l.trap = 0; /* don't need to stop again */
|
||||||
return 0; /* turn off 'trap' */
|
return 0; /* turn off 'trap' */
|
||||||
|
@ -819,15 +829,14 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
|
||||||
if (counthook)
|
if (counthook)
|
||||||
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
|
luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
|
||||||
if (mask & LUA_MASKLINE) {
|
if (mask & LUA_MASKLINE) {
|
||||||
const Proto *p = ci_func(ci)->p;
|
|
||||||
int npci = pcRel(pc, p);
|
int npci = pcRel(pc, p);
|
||||||
if (npci == 0 || /* call linehook when enter a new function, */
|
if (npci == 0 || /* call linehook when enter a new function, */
|
||||||
pc <= L->oldpc || /* when jump back (loop), or when */
|
pc <= invpcRel(oldpc, p) || /* when jump back (loop), or when */
|
||||||
changedline(p, pcRel(L->oldpc, p), npci)) { /* enter new line */
|
changedline(p, oldpc, npci)) { /* enter new line */
|
||||||
int newline = luaG_getfuncline(p, npci);
|
int newline = luaG_getfuncline(p, npci);
|
||||||
luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
|
luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
|
||||||
}
|
}
|
||||||
L->oldpc = pc; /* 'pc' of last call to line hook */
|
L->oldpc = npci; /* 'pc' of last call to line hook */
|
||||||
}
|
}
|
||||||
if (L->status == LUA_YIELD) { /* did hook yield? */
|
if (L->status == LUA_YIELD) { /* did hook yield? */
|
||||||
if (counthook)
|
if (counthook)
|
||||||
|
|
5
ldebug.h
5
ldebug.h
|
@ -13,6 +13,11 @@
|
||||||
|
|
||||||
#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1)
|
#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1)
|
||||||
|
|
||||||
|
|
||||||
|
/* Active Lua function (given call info) */
|
||||||
|
#define ci_func(ci) (clLvalue(s2v((ci)->func)))
|
||||||
|
|
||||||
|
|
||||||
#define resethookcount(L) (L->hookcount = L->basehookcount)
|
#define resethookcount(L) (L->hookcount = L->basehookcount)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
6
ldo.c
6
ldo.c
|
@ -327,7 +327,7 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
|
||||||
ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */
|
ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */
|
||||||
int delta = 0;
|
int delta = 0;
|
||||||
if (isLuacode(ci)) {
|
if (isLuacode(ci)) {
|
||||||
Proto *p = clLvalue(s2v(ci->func))->p;
|
Proto *p = ci_func(ci)->p;
|
||||||
if (p->is_vararg)
|
if (p->is_vararg)
|
||||||
delta = ci->u.l.nextraargs + p->numparams + 1;
|
delta = ci->u.l.nextraargs + p->numparams + 1;
|
||||||
if (L->top < ci->top)
|
if (L->top < ci->top)
|
||||||
|
@ -340,8 +340,8 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
|
||||||
luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
|
luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
|
||||||
ci->func -= delta;
|
ci->func -= delta;
|
||||||
}
|
}
|
||||||
if (isLua(ci->previous))
|
if (isLua(ci = ci->previous))
|
||||||
L->oldpc = ci->previous->u.l.savedpc; /* update 'oldpc' */
|
L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */
|
||||||
return restorestack(L, oldtop);
|
return restorestack(L, oldtop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
1
lstate.c
1
lstate.c
|
@ -301,6 +301,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
|
||||||
L->openupval = NULL;
|
L->openupval = NULL;
|
||||||
L->status = LUA_OK;
|
L->status = LUA_OK;
|
||||||
L->errfunc = 0;
|
L->errfunc = 0;
|
||||||
|
L->oldpc = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
2
lstate.h
2
lstate.h
|
@ -286,7 +286,6 @@ struct lua_State {
|
||||||
StkId top; /* first free slot in the stack */
|
StkId top; /* first free slot in the stack */
|
||||||
global_State *l_G;
|
global_State *l_G;
|
||||||
CallInfo *ci; /* call info for current function */
|
CallInfo *ci; /* call info for current function */
|
||||||
const Instruction *oldpc; /* last pc traced */
|
|
||||||
StkId stack_last; /* last free slot in the stack */
|
StkId stack_last; /* last free slot in the stack */
|
||||||
StkId stack; /* stack base */
|
StkId stack; /* stack base */
|
||||||
UpVal *openupval; /* list of open upvalues in this stack */
|
UpVal *openupval; /* list of open upvalues in this stack */
|
||||||
|
@ -297,6 +296,7 @@ struct lua_State {
|
||||||
volatile lua_Hook hook;
|
volatile lua_Hook hook;
|
||||||
ptrdiff_t errfunc; /* current error handling function (stack index) */
|
ptrdiff_t errfunc; /* current error handling function (stack index) */
|
||||||
l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */
|
l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */
|
||||||
|
int oldpc; /* last pc traced */
|
||||||
int stacksize;
|
int stacksize;
|
||||||
int basehookcount;
|
int basehookcount;
|
||||||
int hookcount;
|
int hookcount;
|
||||||
|
|
2
lvm.c
2
lvm.c
|
@ -1794,7 +1794,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
|
||||||
ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
|
ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
|
||||||
if (trap) {
|
if (trap) {
|
||||||
luaD_hookcall(L, ci);
|
luaD_hookcall(L, ci);
|
||||||
L->oldpc = pc + 1; /* next opcode will be seen as a "new" line */
|
L->oldpc = 1; /* next opcode will be seen as a "new" line */
|
||||||
}
|
}
|
||||||
updatebase(ci); /* function has new base after adjustment */
|
updatebase(ci); /* function has new base after adjustment */
|
||||||
vmbreak;
|
vmbreak;
|
||||||
|
|
Loading…
Reference in New Issue