aboutsummaryrefslogtreecommitdiff
path: root/ldebug.c
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 /ldebug.c
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.
Diffstat (limited to 'ldebug.c')
-rw-r--r--ldebug.c41
1 files changed, 25 insertions, 16 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)