aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-07-17 11:01:05 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-07-17 11:01:05 -0300
commita2195644d89812e5b157ce7bac35543e06db05e3 (patch)
tree878e4e64731fc598e8868b37888b7da4aa447dbb
parent1ecfbfa1a1debd2258decdf7c1954ac6f9761699 (diff)
downloadlua-a2195644d89812e5b157ce7bac35543e06db05e3.tar.gz
lua-a2195644d89812e5b157ce7bac35543e06db05e3.tar.bz2
lua-a2195644d89812e5b157ce7bac35543e06db05e3.zip
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.
-rw-r--r--ldebug.c41
-rw-r--r--ldebug.h5
-rw-r--r--ldo.c6
-rw-r--r--lstate.c1
-rw-r--r--lstate.h2
-rw-r--r--lvm.c2
6 files changed, 36 insertions, 21 deletions
diff --git a/ldebug.c b/ldebug.c
index afdc2b74..0c4439c1 100644
--- a/ldebug.c
+++ b/ldebug.c
@@ -33,10 +33,8 @@
33 33
34#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL) 34#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
35 35
36 36/* inverse of 'pcRel' */
37/* Active Lua function (given call info) */ 37#define invpcRel(pc, p) ((p)->code + (pc) + 1)
38#define ci_func(ci) (clLvalue(s2v((ci)->func)))
39
40 38
41static const char *funcnamefromcode (lua_State *L, CallInfo *ci, 39static const char *funcnamefromcode (lua_State *L, CallInfo *ci,
42 const char **name); 40 const char **name);
@@ -127,20 +125,18 @@ static void settraps (CallInfo *ci) {
127/* 125/*
128** This function can be called during a signal, under "reasonable" 126** This function can be called during a signal, under "reasonable"
129** assumptions. 127** assumptions.
130** Fields 'oldpc', 'basehookcount', and 'hookcount' (set by 128** Fields 'basehookcount' and 'hookcount' (set by 'resethookcount')
131** 'resethookcount') are for debug only, and it is no problem if they 129** are for debug only, and it is no problem if they get arbitrary
132** get arbitrary values (causes at most one wrong hook call). 'hookmask' 130** values (causes at most one wrong hook call). 'hookmask' is an atomic
133** is an atomic value. We assume that pointers are atomic too (e.g., gcc 131** value. We assume that pointers are atomic too (e.g., gcc ensures that
134** ensures that for all platforms where it runs). Moreover, 'hook' is 132** for all platforms where it runs). Moreover, 'hook' is always checked
135** always checked before being called (see 'luaD_hook'). 133** before being called (see 'luaD_hook').
136*/ 134*/
137LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { 135LUA_API void lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
138 if (func == NULL || mask == 0) { /* turn off hooks? */ 136 if (func == NULL || mask == 0) { /* turn off hooks? */
139 mask = 0; 137 mask = 0;
140 func = NULL; 138 func = NULL;
141 } 139 }
142 if (isLua(L->ci))
143 L->oldpc = L->ci->u.l.savedpc;
144 L->hook = func; 140 L->hook = func;
145 L->basehookcount = count; 141 L->basehookcount = count;
146 resethookcount(L); 142 resethookcount(L);
@@ -795,10 +791,24 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
795} 791}
796 792
797 793
794/*
795** Traces the execution of a Lua function. Called before the execution
796** of each opcode, when debug is on. 'L->oldpc' stores the last
797** instruction traced, to detect line changes. When entering a new
798** function, 'npci' will be zero and will test as a new line without
799** the need for 'oldpc'; so, 'oldpc' does not need to be initialized
800** before. Some exceptional conditions may return to a function without
801** updating 'oldpc'. In that case, 'oldpc' may be invalid; if so, it is
802** reset to zero. (A wrong but valid 'oldpc' at most causes an extra
803** call to a line hook.)
804*/
798int luaG_traceexec (lua_State *L, const Instruction *pc) { 805int luaG_traceexec (lua_State *L, const Instruction *pc) {
799 CallInfo *ci = L->ci; 806 CallInfo *ci = L->ci;
800 lu_byte mask = L->hookmask; 807 lu_byte mask = L->hookmask;
808 const Proto *p = ci_func(ci)->p;
801 int counthook; 809 int counthook;
810 /* 'L->oldpc' may be invalid; reset it in this case */
811 int oldpc = (L->oldpc < p->sizecode) ? L->oldpc : 0;
802 if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */ 812 if (!(mask & (LUA_MASKLINE | LUA_MASKCOUNT))) { /* no hooks? */
803 ci->u.l.trap = 0; /* don't need to stop again */ 813 ci->u.l.trap = 0; /* don't need to stop again */
804 return 0; /* turn off 'trap' */ 814 return 0; /* turn off 'trap' */
@@ -819,15 +829,14 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
819 if (counthook) 829 if (counthook)
820 luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */ 830 luaD_hook(L, LUA_HOOKCOUNT, -1, 0, 0); /* call count hook */
821 if (mask & LUA_MASKLINE) { 831 if (mask & LUA_MASKLINE) {
822 const Proto *p = ci_func(ci)->p;
823 int npci = pcRel(pc, p); 832 int npci = pcRel(pc, p);
824 if (npci == 0 || /* call linehook when enter a new function, */ 833 if (npci == 0 || /* call linehook when enter a new function, */
825 pc <= L->oldpc || /* when jump back (loop), or when */ 834 pc <= invpcRel(oldpc, p) || /* when jump back (loop), or when */
826 changedline(p, pcRel(L->oldpc, p), npci)) { /* enter new line */ 835 changedline(p, oldpc, npci)) { /* enter new line */
827 int newline = luaG_getfuncline(p, npci); 836 int newline = luaG_getfuncline(p, npci);
828 luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */ 837 luaD_hook(L, LUA_HOOKLINE, newline, 0, 0); /* call line hook */
829 } 838 }
830 L->oldpc = pc; /* 'pc' of last call to line hook */ 839 L->oldpc = npci; /* 'pc' of last call to line hook */
831 } 840 }
832 if (L->status == LUA_YIELD) { /* did hook yield? */ 841 if (L->status == LUA_YIELD) { /* did hook yield? */
833 if (counthook) 842 if (counthook)
diff --git a/ldebug.h b/ldebug.h
index 1fe0efab..a0a58486 100644
--- a/ldebug.h
+++ b/ldebug.h
@@ -13,6 +13,11 @@
13 13
14#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1) 14#define pcRel(pc, p) (cast_int((pc) - (p)->code) - 1)
15 15
16
17/* Active Lua function (given call info) */
18#define ci_func(ci) (clLvalue(s2v((ci)->func)))
19
20
16#define resethookcount(L) (L->hookcount = L->basehookcount) 21#define resethookcount(L) (L->hookcount = L->basehookcount)
17 22
18/* 23/*
diff --git a/ldo.c b/ldo.c
index 4c976a14..98dd9fbb 100644
--- a/ldo.c
+++ b/ldo.c
@@ -327,7 +327,7 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
327 ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */ 327 ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */
328 int delta = 0; 328 int delta = 0;
329 if (isLuacode(ci)) { 329 if (isLuacode(ci)) {
330 Proto *p = clLvalue(s2v(ci->func))->p; 330 Proto *p = ci_func(ci)->p;
331 if (p->is_vararg) 331 if (p->is_vararg)
332 delta = ci->u.l.nextraargs + p->numparams + 1; 332 delta = ci->u.l.nextraargs + p->numparams + 1;
333 if (L->top < ci->top) 333 if (L->top < ci->top)
@@ -340,8 +340,8 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
340 luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */ 340 luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */
341 ci->func -= delta; 341 ci->func -= delta;
342 } 342 }
343 if (isLua(ci->previous)) 343 if (isLua(ci = ci->previous))
344 L->oldpc = ci->previous->u.l.savedpc; /* update 'oldpc' */ 344 L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */
345 return restorestack(L, oldtop); 345 return restorestack(L, oldtop);
346} 346}
347 347
diff --git a/lstate.c b/lstate.c
index 28853dc7..06fa13d7 100644
--- a/lstate.c
+++ b/lstate.c
@@ -301,6 +301,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
301 L->openupval = NULL; 301 L->openupval = NULL;
302 L->status = LUA_OK; 302 L->status = LUA_OK;
303 L->errfunc = 0; 303 L->errfunc = 0;
304 L->oldpc = 0;
304} 305}
305 306
306 307
diff --git a/lstate.h b/lstate.h
index 2e8bd6c4..0c545ec5 100644
--- a/lstate.h
+++ b/lstate.h
@@ -286,7 +286,6 @@ struct lua_State {
286 StkId top; /* first free slot in the stack */ 286 StkId top; /* first free slot in the stack */
287 global_State *l_G; 287 global_State *l_G;
288 CallInfo *ci; /* call info for current function */ 288 CallInfo *ci; /* call info for current function */
289 const Instruction *oldpc; /* last pc traced */
290 StkId stack_last; /* last free slot in the stack */ 289 StkId stack_last; /* last free slot in the stack */
291 StkId stack; /* stack base */ 290 StkId stack; /* stack base */
292 UpVal *openupval; /* list of open upvalues in this stack */ 291 UpVal *openupval; /* list of open upvalues in this stack */
@@ -297,6 +296,7 @@ struct lua_State {
297 volatile lua_Hook hook; 296 volatile lua_Hook hook;
298 ptrdiff_t errfunc; /* current error handling function (stack index) */ 297 ptrdiff_t errfunc; /* current error handling function (stack index) */
299 l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */ 298 l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */
299 int oldpc; /* last pc traced */
300 int stacksize; 300 int stacksize;
301 int basehookcount; 301 int basehookcount;
302 int hookcount; 302 int hookcount;
diff --git a/lvm.c b/lvm.c
index 66d451b0..08681af1 100644
--- a/lvm.c
+++ b/lvm.c
@@ -1794,7 +1794,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1794 ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p)); 1794 ProtectNT(luaT_adjustvarargs(L, GETARG_A(i), ci, cl->p));
1795 if (trap) { 1795 if (trap) {
1796 luaD_hookcall(L, ci); 1796 luaD_hookcall(L, ci);
1797 L->oldpc = pc + 1; /* next opcode will be seen as a "new" line */ 1797 L->oldpc = 1; /* next opcode will be seen as a "new" line */
1798 } 1798 }
1799 updatebase(ci); /* function has new base after adjustment */ 1799 updatebase(ci); /* function has new base after adjustment */
1800 vmbreak; 1800 vmbreak;