Skip to content

Commit ba7da13

Browse files
committed
Changes in the control of C-stack overflow
* unification of the 'nny' and 'nCcalls' counters; * external C functions ('lua_CFunction') count more "slots" in the C stack (to allow for their possible use of buffers) * added a new test script specific for C-stack overflows. (Most of those tests were already present, but concentrating them in a single script easies the task of checking whether 'LUAI_MAXCCALLS' is adequate in a system.)
1 parent da37ac9 commit ba7da13

12 files changed

Lines changed: 170 additions & 74 deletions

File tree

lapi.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -956,7 +956,7 @@ LUA_API void lua_callk (lua_State *L, int nargs, int nresults,
956956
api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread");
957957
checkresults(L, nargs, nresults);
958958
func = L->top - (nargs+1);
959-
if (k != NULL && L->nny == 0) { /* need to prepare continuation? */
959+
if (k != NULL && yieldable(L)) { /* need to prepare continuation? */
960960
L->ci->u.c.k = k; /* save continuation */
961961
L->ci->u.c.ctx = ctx; /* save context */
962962
luaD_call(L, func, nresults); /* do the call */
@@ -1004,7 +1004,7 @@ LUA_API int lua_pcallk (lua_State *L, int nargs, int nresults, int errfunc,
10041004
func = savestack(L, o);
10051005
}
10061006
c.func = L->top - (nargs+1); /* function to be called */
1007-
if (k == NULL || L->nny > 0) { /* no continuation or no yieldable? */
1007+
if (k == NULL || !yieldable(L)) { /* no continuation or no yieldable? */
10081008
c.nresults = nresults; /* do a 'conventional' protected call */
10091009
status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
10101010
}

ldo.c

Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ l_noret luaD_throw (lua_State *L, int errcode) {
138138

139139

140140
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
141-
unsigned short oldnCcalls = L->nCcalls - L->nci;
141+
l_uint32 oldnCcalls = L->nCcalls - L->nci;
142142
struct lua_longjmp lj;
143143
lua_assert(L->nCcalls >= L->nci);
144144
lj.status = LUA_OK;
@@ -513,12 +513,17 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
513513

514514

515515
/*
516-
** Similar to 'luaD_call', but does not allow yields during the call
516+
** Similar to 'luaD_call', but does not allow yields during the call.
517+
** If there is a stack overflow, freeing all CI structures will
518+
** force the subsequent call to invoke 'luaE_extendCI', which then
519+
** will raise any errors.
517520
*/
518521
void luaD_callnoyield (lua_State *L, StkId func, int nResults) {
519-
L->nny++;
522+
incXCcalls(L);
523+
if (getCcalls(L) >= LUAI_MAXCCALLS) /* possible stack overflow? */
524+
luaE_freeCI(L);
520525
luaD_call(L, func, nResults);
521-
L->nny--;
526+
decXCcalls(L);
522527
}
523528

524529

@@ -530,7 +535,7 @@ static void finishCcall (lua_State *L, int status) {
530535
CallInfo *ci = L->ci;
531536
int n;
532537
/* must have a continuation and must be able to call it */
533-
lua_assert(ci->u.c.k != NULL && L->nny == 0);
538+
lua_assert(ci->u.c.k != NULL && yieldable(L));
534539
/* error status can only happen in a protected call */
535540
lua_assert((ci->callstatus & CIST_YPCALL) || status == LUA_YIELD);
536541
if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */
@@ -601,7 +606,6 @@ static int recover (lua_State *L, int status) {
601606
luaD_seterrorobj(L, status, oldtop);
602607
L->ci = ci;
603608
L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */
604-
L->nny = 0; /* should be zero to be yieldable */
605609
luaD_shrinkstack(L);
606610
L->errfunc = ci->u.c.old_errfunc;
607611
return 1; /* continue running the coroutine */
@@ -622,13 +626,6 @@ static int resume_error (lua_State *L, const char *msg, int narg) {
622626
}
623627

624628

625-
/*
626-
** "Cost" in the C stack for a coroutine invocation.
627-
*/
628-
#if !defined(LUAL_COROCSTK)
629-
#define LUAL_COROCSTK 3
630-
#endif
631-
632629
/*
633630
** Do the work for 'lua_resume' in protected mode. Most of the work
634631
** depends on the status of the coroutine: initial state, suspended
@@ -664,7 +661,6 @@ static void resume (lua_State *L, void *ud) {
664661
LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
665662
int *nresults) {
666663
int status;
667-
unsigned short oldnny = L->nny; /* save "number of non-yieldable" calls */
668664
lua_lock(L);
669665
if (L->status == LUA_OK) { /* may be starting a coroutine */
670666
if (L->ci != &L->base_ci) /* not in base level? */
@@ -675,11 +671,10 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
675671
if (from == NULL)
676672
L->nCcalls = 1;
677673
else /* correct 'nCcalls' for this thread */
678-
L->nCcalls = from->nCcalls - from->nci + L->nci + LUAL_COROCSTK;
674+
L->nCcalls = getCcalls(from) - from->nci + L->nci + CSTACKCF;
679675
if (L->nCcalls >= LUAI_MAXCCALLS)
680676
return resume_error(L, "C stack overflow", nargs);
681677
luai_userstateresume(L, nargs);
682-
L->nny = 0; /* allow yields */
683678
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
684679
status = luaD_rawrunprotected(L, resume, &nargs);
685680
/* continue running after recoverable errors */
@@ -698,14 +693,13 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
698693
}
699694
*nresults = (status == LUA_YIELD) ? L->ci->u2.nyield
700695
: cast_int(L->top - (L->ci->func + 1));
701-
L->nny = oldnny; /* restore 'nny' */
702696
lua_unlock(L);
703697
return status;
704698
}
705699

706700

707701
LUA_API int lua_isyieldable (lua_State *L) {
708-
return (L->nny == 0);
702+
return yieldable(L);
709703
}
710704

711705

@@ -715,7 +709,7 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
715709
luai_userstateyield(L, nresults);
716710
lua_lock(L);
717711
api_checknelems(L, nresults);
718-
if (unlikely(L->nny > 0)) {
712+
if (unlikely(!yieldable(L))) {
719713
if (L != G(L)->mainthread)
720714
luaG_runerror(L, "attempt to yield across a C-call boundary");
721715
else
@@ -741,23 +735,21 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
741735

742736
/*
743737
** Call the C function 'func' in protected mode, restoring basic
744-
** thread information ('allowhook', 'nny', etc.) and in particular
738+
** thread information ('allowhook', etc.) and in particular
745739
** its stack level in case of errors.
746740
*/
747741
int luaD_pcall (lua_State *L, Pfunc func, void *u,
748742
ptrdiff_t old_top, ptrdiff_t ef) {
749743
int status;
750744
CallInfo *old_ci = L->ci;
751745
lu_byte old_allowhooks = L->allowhook;
752-
unsigned short old_nny = L->nny;
753746
ptrdiff_t old_errfunc = L->errfunc;
754747
L->errfunc = ef;
755748
status = luaD_rawrunprotected(L, func, u);
756749
if (unlikely(status != LUA_OK)) { /* an error occurred? */
757750
StkId oldtop = restorestack(L, old_top);
758751
L->ci = old_ci;
759752
L->allowhook = old_allowhooks;
760-
L->nny = old_nny;
761753
status = luaF_close(L, oldtop, status);
762754
oldtop = restorestack(L, old_top); /* previous call may change stack */
763755
luaD_seterrorobj(L, status, oldtop);
@@ -811,7 +803,7 @@ int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
811803
const char *mode) {
812804
struct SParser p;
813805
int status;
814-
L->nny++; /* cannot yield during parsing */
806+
incnny(L); /* cannot yield during parsing */
815807
p.z = z; p.name = name; p.mode = mode;
816808
p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0;
817809
p.dyd.gt.arr = NULL; p.dyd.gt.size = 0;
@@ -822,7 +814,7 @@ int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
822814
luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size);
823815
luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size);
824816
luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size);
825-
L->nny--;
817+
decnny(L);
826818
return status;
827819
}
828820

llimits.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,11 +184,13 @@ typedef LUAI_UACINT l_uacInt;
184184
** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
185185
*/
186186
#if LUAI_BITSINT >= 32
187-
typedef unsigned int Instruction;
187+
typedef unsigned int l_uint32;
188188
#else
189-
typedef unsigned long Instruction;
189+
typedef unsigned long l_uint32;
190190
#endif
191191

192+
typedef l_uint32 Instruction;
193+
192194

193195

194196
/*

lparser.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -367,10 +367,12 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
367367
}
368368

369369

370-
#define enterlevel(ls) luaE_incCcalls((ls)->L)
371-
370+
/*
371+
** Macros to limit the maximum recursion depth while parsing
372+
*/
373+
#define enterlevel(ls) luaE_enterCcall((ls)->L)
372374

373-
#define leavelevel(ls) ((ls)->L->nCcalls--)
375+
#define leavelevel(ls) luaE_exitCcall((ls)->L)
374376

375377

376378
/*

lstate.c

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -99,24 +99,42 @@ void luaE_setdebt (global_State *g, l_mem debt) {
9999
/*
100100
** Increment count of "C calls" and check for overflows. In case of
101101
** a stack overflow, check appropriate error ("regular" overflow or
102-
** overflow while handling stack overflow). If 'nCalls' is larger than
103-
** LUAI_MAXCCALLS (which means it is handling a "regular" overflow) but
104-
** smaller than 9/8 of LUAI_MAXCCALLS, does not report an error (to
105-
** allow overflow handling to work)
102+
** overflow while handling stack overflow).
103+
** If 'nCcalls' is larger than LUAI_MAXCCALLS but smaller than
104+
** LUAI_MAXCCALLS + CSTACKCF (plus 2 to avoid by-one errors), it means
105+
** it has just entered the "overflow zone", so the function raises an
106+
** overflow error.
107+
** If 'nCcalls' is larger than LUAI_MAXCCALLS + CSTACKCF + 2
108+
** (which means it is already handling an overflow) but smaller than
109+
** 9/8 of LUAI_MAXCCALLS, does not report an error (to allow message
110+
** handling to work).
111+
** Otherwise, report a stack overflow while handling a stack overflow
112+
** (probably caused by a repeating error in the message handling
113+
** function).
106114
*/
107-
void luaE_incCcalls (lua_State *L) {
108-
if (++L->nCcalls >= LUAI_MAXCCALLS) {
109-
if (L->nCcalls == LUAI_MAXCCALLS)
110-
luaG_runerror(L, "C stack overflow");
111-
else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3)))
112-
luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
115+
void luaE_enterCcall (lua_State *L) {
116+
int ncalls = getCcalls(L);
117+
L->nCcalls++;
118+
if (ncalls >= LUAI_MAXCCALLS) { /* possible overflow? */
119+
luaE_freeCI(L); /* release unused CIs */
120+
ncalls = getCcalls(L); /* update call count */
121+
if (ncalls >= LUAI_MAXCCALLS) { /* still overflow? */
122+
if (ncalls <= LUAI_MAXCCALLS + CSTACKCF + 2) {
123+
/* no error before increments; raise the error now */
124+
L->nCcalls += (CSTACKCF + 4); /* avoid raising it again */
125+
luaG_runerror(L, "C stack overflow");
126+
}
127+
else if (ncalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS >> 3)))
128+
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
129+
}
113130
}
114131
}
115132

116133

117134
CallInfo *luaE_extendCI (lua_State *L) {
118135
CallInfo *ci;
119-
luaE_incCcalls(L);
136+
lua_assert(L->ci->next == NULL);
137+
luaE_enterCcall(L);
120138
ci = luaM_new(L, CallInfo);
121139
lua_assert(L->ci->next == NULL);
122140
L->ci->next = ci;
@@ -135,13 +153,13 @@ void luaE_freeCI (lua_State *L) {
135153
CallInfo *ci = L->ci;
136154
CallInfo *next = ci->next;
137155
ci->next = NULL;
138-
L->nCcalls -= L->nci; /* to subtract removed elements from 'nCcalls' */
156+
L->nCcalls -= L->nci; /* subtract removed elements from 'nCcalls' */
139157
while ((ci = next) != NULL) {
140158
next = ci->next;
141159
luaM_free(L, ci);
142160
L->nci--;
143161
}
144-
L->nCcalls += L->nci; /* to subtract removed elements from 'nCcalls' */
162+
L->nCcalls += L->nci; /* adjust result */
145163
}
146164

147165

@@ -151,7 +169,7 @@ void luaE_freeCI (lua_State *L) {
151169
void luaE_shrinkCI (lua_State *L) {
152170
CallInfo *ci = L->ci;
153171
CallInfo *next2; /* next's next */
154-
L->nCcalls -= L->nci; /* to subtract removed elements from 'nCcalls' */
172+
L->nCcalls -= L->nci; /* subtract removed elements from 'nCcalls' */
155173
/* while there are two nexts */
156174
while (ci->next != NULL && (next2 = ci->next->next) != NULL) {
157175
luaM_free(L, ci->next); /* free next */
@@ -160,7 +178,7 @@ void luaE_shrinkCI (lua_State *L) {
160178
next2->previous = ci;
161179
ci = next2; /* keep next's next */
162180
}
163-
L->nCcalls += L->nci; /* to subtract removed elements from 'nCcalls' */
181+
L->nCcalls += L->nci; /* adjust result */
164182
}
165183

166184

@@ -250,7 +268,6 @@ static void preinit_thread (lua_State *L, global_State *g) {
250268
L->allowhook = 1;
251269
resethookcount(L);
252270
L->openupval = NULL;
253-
L->nny = 1;
254271
L->status = LUA_OK;
255272
L->errfunc = 0;
256273
}

0 commit comments

Comments
 (0)