diff options
| author | Roberto I <roberto@inf.puc-rio.br> | 2026-07-22 14:42:55 -0300 |
|---|---|---|
| committer | Roberto I <roberto@inf.puc-rio.br> | 2026-07-22 14:42:55 -0300 |
| commit | 8511e90b7df5daf139aba290b9c9c1595927e7b3 (patch) | |
| tree | c785e4595f212d9972fe9f40d2bb58a980a9257b | |
| parent | 0f781f836ab348716eb9232d3831a871b9821a3c (diff) | |
| download | lua-8511e90b7df5daf139aba290b9c9c1595927e7b3.tar.gz lua-8511e90b7df5daf139aba290b9c9c1595927e7b3.tar.bz2 lua-8511e90b7df5daf139aba290b9c9c1595927e7b3.zip | |
Bug: GC checks stack space before running finalizer
If some stack does not have a minimum available space, the GC defers
calling a finalizer until the next cycle. That avoids errors while
running a finalizer that the programmer cannot control.
| -rw-r--r-- | ldo.c | 21 | ||||
| -rw-r--r-- | ldo.h | 1 | ||||
| -rw-r--r-- | lgc.c | 7 | ||||
| -rw-r--r-- | lstate.c | 17 | ||||
| -rw-r--r-- | lstate.h | 2 |
5 files changed, 37 insertions, 11 deletions
| @@ -206,6 +206,25 @@ l_noret luaD_errerr (lua_State *L) { | |||
| 206 | 206 | ||
| 207 | 207 | ||
| 208 | /* | 208 | /* |
| 209 | ** Check whether stacks have enough space to run a simple function (such | ||
| 210 | ** as a finalizer): At least BASIC_STACK_SIZE in the Lua stack, two | ||
| 211 | ** available CallInfos, and two "slots" in the C stack. | ||
| 212 | */ | ||
| 213 | int luaD_checkminstack (lua_State *L) { | ||
| 214 | if (getCcalls(L) >= LUAI_MAXCCALLS - 2) | ||
| 215 | return 0; /* not enough C-stack slots */ | ||
| 216 | if (L->ci->next == NULL && luaE_extendCI(L, 0) == NULL) | ||
| 217 | return 0; /* unable to allocate first ci */ | ||
| 218 | if (L->ci->next->next == NULL && luaE_extendCI(L, 0) == NULL) | ||
| 219 | return 0; /* unable to allocate second ci */ | ||
| 220 | if (L->stack_last.p - L->top.p >= BASIC_STACK_SIZE) | ||
| 221 | return 1; /* enough (BASIC_STACK_SIZE) free slots in the Lua stack */ | ||
| 222 | else /* try to grow stack to a size with enough free slots */ | ||
| 223 | return luaD_growstack(L, BASIC_STACK_SIZE, 0); | ||
| 224 | } | ||
| 225 | |||
| 226 | |||
| 227 | /* | ||
| 209 | ** Reallocate the stack to a new size, correcting all pointers into it. | 228 | ** Reallocate the stack to a new size, correcting all pointers into it. |
| 210 | ** In ISO C, any pointer use after the pointer has been deallocated is | 229 | ** In ISO C, any pointer use after the pointer has been deallocated is |
| 211 | ** undefined behavior. So, before the reallocation, all pointers are | 230 | ** undefined behavior. So, before the reallocation, all pointers are |
| @@ -503,7 +522,7 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { | |||
| 503 | 522 | ||
| 504 | 523 | ||
| 505 | 524 | ||
| 506 | #define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L)) | 525 | #define next_ci(L) (L->ci->next ? L->ci->next : luaE_extendCI(L, 1)) |
| 507 | 526 | ||
| 508 | 527 | ||
| 509 | l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret, | 528 | l_sinline CallInfo *prepCallInfo (lua_State *L, StkId func, int nret, |
| @@ -80,6 +80,7 @@ LUAI_FUNC int luaD_reallocstack (lua_State *L, int newsize, int raiseerror); | |||
| 80 | LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror); | 80 | LUAI_FUNC int luaD_growstack (lua_State *L, int n, int raiseerror); |
| 81 | LUAI_FUNC void luaD_shrinkstack (lua_State *L); | 81 | LUAI_FUNC void luaD_shrinkstack (lua_State *L); |
| 82 | LUAI_FUNC void luaD_inctop (lua_State *L); | 82 | LUAI_FUNC void luaD_inctop (lua_State *L); |
| 83 | LUAI_FUNC int luaD_checkminstack (lua_State *L); | ||
| 83 | 84 | ||
| 84 | LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode); | 85 | LUAI_FUNC l_noret luaD_throw (lua_State *L, int errcode); |
| 85 | LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); | 86 | LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); |
| @@ -1242,7 +1242,7 @@ static void finishgencycle (lua_State *L, global_State *g) { | |||
| 1242 | correctgraylists(g); | 1242 | correctgraylists(g); |
| 1243 | checkSizes(L, g); | 1243 | checkSizes(L, g); |
| 1244 | g->gcstate = GCSpropagate; /* skip restart */ | 1244 | g->gcstate = GCSpropagate; /* skip restart */ |
| 1245 | if (!g->gcemergency) | 1245 | if (g->tobefnz != NULL && !g->gcemergency && luaD_checkminstack(L)) |
| 1246 | callallpendingfinalizers(L); | 1246 | callallpendingfinalizers(L); |
| 1247 | } | 1247 | } |
| 1248 | 1248 | ||
| @@ -1632,11 +1632,12 @@ static lu_mem singlestep (lua_State *L) { | |||
| 1632 | break; | 1632 | break; |
| 1633 | } | 1633 | } |
| 1634 | case GCScallfin: { /* call remaining finalizers */ | 1634 | case GCScallfin: { /* call remaining finalizers */ |
| 1635 | if (g->tobefnz && !g->gcemergency) { | 1635 | if (g->tobefnz && !g->gcemergency && luaD_checkminstack(L)) { |
| 1636 | g->gcstopem = 0; /* ok collections during finalizers */ | 1636 | g->gcstopem = 0; /* ok collections during finalizers */ |
| 1637 | work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST; | 1637 | work = runafewfinalizers(L, GCFINMAX) * GCFINALIZECOST; |
| 1638 | } | 1638 | } |
| 1639 | else { /* emergency mode or no more finalizers */ | 1639 | else { /* no more finalizers or emergency mode or no enough stack |
| 1640 | to run finalizers */ | ||
| 1640 | g->gcstate = GCSpause; /* finish collection */ | 1641 | g->gcstate = GCSpause; /* finish collection */ |
| 1641 | work = 0; | 1642 | work = 0; |
| 1642 | } | 1643 | } |
| @@ -102,14 +102,19 @@ LUA_API int lua_setcstacklimit (lua_State *L, unsigned int limit) { | |||
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | 104 | ||
| 105 | CallInfo *luaE_extendCI (lua_State *L) { | 105 | CallInfo *luaE_extendCI (lua_State *L, int err) { |
| 106 | CallInfo *ci; | 106 | CallInfo *ci; |
| 107 | lua_assert(L->ci->next == NULL); | 107 | ci = luaM_reallocvector(L, NULL, 0, 1, CallInfo); |
| 108 | ci = luaM_new(L, CallInfo); | 108 | if (l_unlikely(ci == NULL)) { /* allocation failed? */ |
| 109 | lua_assert(L->ci->next == NULL); | 109 | if (err) |
| 110 | L->ci->next = ci; | 110 | luaM_error(L); /* raise the error */ |
| 111 | return NULL; /* else only report it */ | ||
| 112 | } | ||
| 113 | ci->next = L->ci->next; | ||
| 111 | ci->previous = L->ci; | 114 | ci->previous = L->ci; |
| 112 | ci->next = NULL; | 115 | L->ci->next = ci; |
| 116 | if (ci->next) | ||
| 117 | ci->next->previous = ci; | ||
| 113 | ci->u.l.trap = 0; | 118 | ci->u.l.trap = 0; |
| 114 | L->nci++; | 119 | L->nci++; |
| 115 | return ci; | 120 | return ci; |
| @@ -395,7 +395,7 @@ union GCUnion { | |||
| 395 | 395 | ||
| 396 | LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); | 396 | LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt); |
| 397 | LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); | 397 | LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); |
| 398 | LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); | 398 | LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L, int err); |
| 399 | LUAI_FUNC void luaE_shrinkCI (lua_State *L); | 399 | LUAI_FUNC void luaE_shrinkCI (lua_State *L); |
| 400 | LUAI_FUNC void luaE_checkcstack (lua_State *L); | 400 | LUAI_FUNC void luaE_checkcstack (lua_State *L); |
| 401 | LUAI_FUNC void luaE_incCstack (lua_State *L); | 401 | LUAI_FUNC void luaE_incCstack (lua_State *L); |
