aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-12-27 14:32:29 -0200
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-12-27 14:32:29 -0200
commitba7da13ec5938f978c37d63aa40a3e340b301f79 (patch)
treec1f22403954f6e0c6d17c8495c11509103313c9a
parentda37ac9c7894186a0e2e0e6f1f5f00b825fd1555 (diff)
downloadlua-ba7da13ec5938f978c37d63aa40a3e340b301f79.tar.gz
lua-ba7da13ec5938f978c37d63aa40a3e340b301f79.tar.bz2
lua-ba7da13ec5938f978c37d63aa40a3e340b301f79.zip
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.)
-rw-r--r--lapi.c4
-rw-r--r--ldo.c40
-rw-r--r--llimits.h6
-rw-r--r--lparser.c8
-rw-r--r--lstate.c49
-rw-r--r--lstate.h52
-rw-r--r--ltests.h2
-rw-r--r--luaconf.h4
-rw-r--r--testes/all.lua1
-rw-r--r--testes/coroutine.lua4
-rw-r--r--testes/cstack.lua62
-rw-r--r--testes/pm.lua12
12 files changed, 170 insertions, 74 deletions
diff --git a/lapi.c b/lapi.c
index 9cabe7ca..2d10bb73 100644
--- a/lapi.c
+++ b/lapi.c
@@ -956,7 +956,7 @@ LUA_API void lua_callk (lua_State *L, int nargs, int nresults,
956 api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread"); 956 api_check(L, L->status == LUA_OK, "cannot do calls on non-normal thread");
957 checkresults(L, nargs, nresults); 957 checkresults(L, nargs, nresults);
958 func = L->top - (nargs+1); 958 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? */
960 L->ci->u.c.k = k; /* save continuation */ 960 L->ci->u.c.k = k; /* save continuation */
961 L->ci->u.c.ctx = ctx; /* save context */ 961 L->ci->u.c.ctx = ctx; /* save context */
962 luaD_call(L, func, nresults); /* do the call */ 962 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,
1004 func = savestack(L, o); 1004 func = savestack(L, o);
1005 } 1005 }
1006 c.func = L->top - (nargs+1); /* function to be called */ 1006 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? */
1008 c.nresults = nresults; /* do a 'conventional' protected call */ 1008 c.nresults = nresults; /* do a 'conventional' protected call */
1009 status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); 1009 status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
1010 } 1010 }
diff --git a/ldo.c b/ldo.c
index 056fef0c..f8d8f11c 100644
--- a/ldo.c
+++ b/ldo.c
@@ -138,7 +138,7 @@ l_noret luaD_throw (lua_State *L, int errcode) {
138 138
139 139
140int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { 140int 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;
142 struct lua_longjmp lj; 142 struct lua_longjmp lj;
143 lua_assert(L->nCcalls >= L->nci); 143 lua_assert(L->nCcalls >= L->nci);
144 lj.status = LUA_OK; 144 lj.status = LUA_OK;
@@ -513,12 +513,17 @@ void luaD_call (lua_State *L, StkId func, int nresults) {
513 513
514 514
515/* 515/*
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.
517*/ 520*/
518void luaD_callnoyield (lua_State *L, StkId func, int nResults) { 521void 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);
520 luaD_call(L, func, nResults); 525 luaD_call(L, func, nResults);
521 L->nny--; 526 decXCcalls(L);
522} 527}
523 528
524 529
@@ -530,7 +535,7 @@ static void finishCcall (lua_State *L, int status) {
530 CallInfo *ci = L->ci; 535 CallInfo *ci = L->ci;
531 int n; 536 int n;
532 /* must have a continuation and must be able to call it */ 537 /* 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));
534 /* error status can only happen in a protected call */ 539 /* error status can only happen in a protected call */
535 lua_assert((ci->callstatus & CIST_YPCALL) || status == LUA_YIELD); 540 lua_assert((ci->callstatus & CIST_YPCALL) || status == LUA_YIELD);
536 if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */ 541 if (ci->callstatus & CIST_YPCALL) { /* was inside a pcall? */
@@ -601,7 +606,6 @@ static int recover (lua_State *L, int status) {
601 luaD_seterrorobj(L, status, oldtop); 606 luaD_seterrorobj(L, status, oldtop);
602 L->ci = ci; 607 L->ci = ci;
603 L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */ 608 L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */
604 L->nny = 0; /* should be zero to be yieldable */
605 luaD_shrinkstack(L); 609 luaD_shrinkstack(L);
606 L->errfunc = ci->u.c.old_errfunc; 610 L->errfunc = ci->u.c.old_errfunc;
607 return 1; /* continue running the coroutine */ 611 return 1; /* continue running the coroutine */
@@ -623,13 +627,6 @@ static int resume_error (lua_State *L, const char *msg, int narg) {
623 627
624 628
625/* 629/*
626** "Cost" in the C stack for a coroutine invocation.
627*/
628#if !defined(LUAL_COROCSTK)
629#define LUAL_COROCSTK 3
630#endif
631
632/*
633** Do the work for 'lua_resume' in protected mode. Most of the work 630** Do the work for 'lua_resume' in protected mode. Most of the work
634** depends on the status of the coroutine: initial state, suspended 631** depends on the status of the coroutine: initial state, suspended
635** inside a hook, or regularly suspended (optionally with a continuation 632** inside a hook, or regularly suspended (optionally with a continuation
@@ -664,7 +661,6 @@ static void resume (lua_State *L, void *ud) {
664LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, 661LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
665 int *nresults) { 662 int *nresults) {
666 int status; 663 int status;
667 unsigned short oldnny = L->nny; /* save "number of non-yieldable" calls */
668 lua_lock(L); 664 lua_lock(L);
669 if (L->status == LUA_OK) { /* may be starting a coroutine */ 665 if (L->status == LUA_OK) { /* may be starting a coroutine */
670 if (L->ci != &L->base_ci) /* not in base level? */ 666 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,
675 if (from == NULL) 671 if (from == NULL)
676 L->nCcalls = 1; 672 L->nCcalls = 1;
677 else /* correct 'nCcalls' for this thread */ 673 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;
679 if (L->nCcalls >= LUAI_MAXCCALLS) 675 if (L->nCcalls >= LUAI_MAXCCALLS)
680 return resume_error(L, "C stack overflow", nargs); 676 return resume_error(L, "C stack overflow", nargs);
681 luai_userstateresume(L, nargs); 677 luai_userstateresume(L, nargs);
682 L->nny = 0; /* allow yields */
683 api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs); 678 api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
684 status = luaD_rawrunprotected(L, resume, &nargs); 679 status = luaD_rawrunprotected(L, resume, &nargs);
685 /* continue running after recoverable errors */ 680 /* continue running after recoverable errors */
@@ -698,14 +693,13 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs,
698 } 693 }
699 *nresults = (status == LUA_YIELD) ? L->ci->u2.nyield 694 *nresults = (status == LUA_YIELD) ? L->ci->u2.nyield
700 : cast_int(L->top - (L->ci->func + 1)); 695 : cast_int(L->top - (L->ci->func + 1));
701 L->nny = oldnny; /* restore 'nny' */
702 lua_unlock(L); 696 lua_unlock(L);
703 return status; 697 return status;
704} 698}
705 699
706 700
707LUA_API int lua_isyieldable (lua_State *L) { 701LUA_API int lua_isyieldable (lua_State *L) {
708 return (L->nny == 0); 702 return yieldable(L);
709} 703}
710 704
711 705
@@ -715,7 +709,7 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
715 luai_userstateyield(L, nresults); 709 luai_userstateyield(L, nresults);
716 lua_lock(L); 710 lua_lock(L);
717 api_checknelems(L, nresults); 711 api_checknelems(L, nresults);
718 if (unlikely(L->nny > 0)) { 712 if (unlikely(!yieldable(L))) {
719 if (L != G(L)->mainthread) 713 if (L != G(L)->mainthread)
720 luaG_runerror(L, "attempt to yield across a C-call boundary"); 714 luaG_runerror(L, "attempt to yield across a C-call boundary");
721 else 715 else
@@ -741,7 +735,7 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
741 735
742/* 736/*
743** Call the C function 'func' in protected mode, restoring basic 737** 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
745** its stack level in case of errors. 739** its stack level in case of errors.
746*/ 740*/
747int luaD_pcall (lua_State *L, Pfunc func, void *u, 741int luaD_pcall (lua_State *L, Pfunc func, void *u,
@@ -749,7 +743,6 @@ int luaD_pcall (lua_State *L, Pfunc func, void *u,
749 int status; 743 int status;
750 CallInfo *old_ci = L->ci; 744 CallInfo *old_ci = L->ci;
751 lu_byte old_allowhooks = L->allowhook; 745 lu_byte old_allowhooks = L->allowhook;
752 unsigned short old_nny = L->nny;
753 ptrdiff_t old_errfunc = L->errfunc; 746 ptrdiff_t old_errfunc = L->errfunc;
754 L->errfunc = ef; 747 L->errfunc = ef;
755 status = luaD_rawrunprotected(L, func, u); 748 status = luaD_rawrunprotected(L, func, u);
@@ -757,7 +750,6 @@ int luaD_pcall (lua_State *L, Pfunc func, void *u,
757 StkId oldtop = restorestack(L, old_top); 750 StkId oldtop = restorestack(L, old_top);
758 L->ci = old_ci; 751 L->ci = old_ci;
759 L->allowhook = old_allowhooks; 752 L->allowhook = old_allowhooks;
760 L->nny = old_nny;
761 status = luaF_close(L, oldtop, status); 753 status = luaF_close(L, oldtop, status);
762 oldtop = restorestack(L, old_top); /* previous call may change stack */ 754 oldtop = restorestack(L, old_top); /* previous call may change stack */
763 luaD_seterrorobj(L, status, oldtop); 755 luaD_seterrorobj(L, status, oldtop);
@@ -811,7 +803,7 @@ int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
811 const char *mode) { 803 const char *mode) {
812 struct SParser p; 804 struct SParser p;
813 int status; 805 int status;
814 L->nny++; /* cannot yield during parsing */ 806 incnny(L); /* cannot yield during parsing */
815 p.z = z; p.name = name; p.mode = mode; 807 p.z = z; p.name = name; p.mode = mode;
816 p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0; 808 p.dyd.actvar.arr = NULL; p.dyd.actvar.size = 0;
817 p.dyd.gt.arr = NULL; p.dyd.gt.size = 0; 809 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,
822 luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size); 814 luaM_freearray(L, p.dyd.actvar.arr, p.dyd.actvar.size);
823 luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size); 815 luaM_freearray(L, p.dyd.gt.arr, p.dyd.gt.size);
824 luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size); 816 luaM_freearray(L, p.dyd.label.arr, p.dyd.label.size);
825 L->nny--; 817 decnny(L);
826 return status; 818 return status;
827} 819}
828 820
diff --git a/llimits.h b/llimits.h
index 8ab58b5c..9d35d1c7 100644
--- a/llimits.h
+++ b/llimits.h
@@ -184,11 +184,13 @@ typedef LUAI_UACINT l_uacInt;
184** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) 184** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
185*/ 185*/
186#if LUAI_BITSINT >= 32 186#if LUAI_BITSINT >= 32
187typedef unsigned int Instruction; 187typedef unsigned int l_uint32;
188#else 188#else
189typedef unsigned long Instruction; 189typedef unsigned long l_uint32;
190#endif 190#endif
191 191
192typedef l_uint32 Instruction;
193
192 194
193 195
194/* 196/*
diff --git a/lparser.c b/lparser.c
index eed8bffd..3887958e 100644
--- a/lparser.c
+++ b/lparser.c
@@ -367,10 +367,12 @@ static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {
367} 367}
368 368
369 369
370#define enterlevel(ls) luaE_incCcalls((ls)->L) 370/*
371 371** Macros to limit the maximum recursion depth while parsing
372*/
373#define enterlevel(ls) luaE_enterCcall((ls)->L)
372 374
373#define leavelevel(ls) ((ls)->L->nCcalls--) 375#define leavelevel(ls) luaE_exitCcall((ls)->L)
374 376
375 377
376/* 378/*
diff --git a/lstate.c b/lstate.c
index 5ee024fc..04a9e064 100644
--- a/lstate.c
+++ b/lstate.c
@@ -99,24 +99,42 @@ void luaE_setdebt (global_State *g, l_mem debt) {
99/* 99/*
100** Increment count of "C calls" and check for overflows. In case of 100** Increment count of "C calls" and check for overflows. In case of
101** a stack overflow, check appropriate error ("regular" overflow or 101** a stack overflow, check appropriate error ("regular" overflow or
102** overflow while handling stack overflow). If 'nCalls' is larger than 102** overflow while handling stack overflow).
103** LUAI_MAXCCALLS (which means it is handling a "regular" overflow) but 103** If 'nCcalls' is larger than LUAI_MAXCCALLS but smaller than
104** smaller than 9/8 of LUAI_MAXCCALLS, does not report an error (to 104** LUAI_MAXCCALLS + CSTACKCF (plus 2 to avoid by-one errors), it means
105** allow overflow handling to work) 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).
106*/ 114*/
107void luaE_incCcalls (lua_State *L) { 115void luaE_enterCcall (lua_State *L) {
108 if (++L->nCcalls >= LUAI_MAXCCALLS) { 116 int ncalls = getCcalls(L);
109 if (L->nCcalls == LUAI_MAXCCALLS) 117 L->nCcalls++;
110 luaG_runerror(L, "C stack overflow"); 118 if (ncalls >= LUAI_MAXCCALLS) { /* possible overflow? */
111 else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) 119 luaE_freeCI(L); /* release unused CIs */
112 luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ 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 }
113 } 130 }
114} 131}
115 132
116 133
117CallInfo *luaE_extendCI (lua_State *L) { 134CallInfo *luaE_extendCI (lua_State *L) {
118 CallInfo *ci; 135 CallInfo *ci;
119 luaE_incCcalls(L); 136 lua_assert(L->ci->next == NULL);
137 luaE_enterCcall(L);
120 ci = luaM_new(L, CallInfo); 138 ci = luaM_new(L, CallInfo);
121 lua_assert(L->ci->next == NULL); 139 lua_assert(L->ci->next == NULL);
122 L->ci->next = ci; 140 L->ci->next = ci;
@@ -135,13 +153,13 @@ void luaE_freeCI (lua_State *L) {
135 CallInfo *ci = L->ci; 153 CallInfo *ci = L->ci;
136 CallInfo *next = ci->next; 154 CallInfo *next = ci->next;
137 ci->next = NULL; 155 ci->next = NULL;
138 L->nCcalls -= L->nci; /* to subtract removed elements from 'nCcalls' */ 156 L->nCcalls -= L->nci; /* subtract removed elements from 'nCcalls' */
139 while ((ci = next) != NULL) { 157 while ((ci = next) != NULL) {
140 next = ci->next; 158 next = ci->next;
141 luaM_free(L, ci); 159 luaM_free(L, ci);
142 L->nci--; 160 L->nci--;
143 } 161 }
144 L->nCcalls += L->nci; /* to subtract removed elements from 'nCcalls' */ 162 L->nCcalls += L->nci; /* adjust result */
145} 163}
146 164
147 165
@@ -151,7 +169,7 @@ void luaE_freeCI (lua_State *L) {
151void luaE_shrinkCI (lua_State *L) { 169void luaE_shrinkCI (lua_State *L) {
152 CallInfo *ci = L->ci; 170 CallInfo *ci = L->ci;
153 CallInfo *next2; /* next's next */ 171 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' */
155 /* while there are two nexts */ 173 /* while there are two nexts */
156 while (ci->next != NULL && (next2 = ci->next->next) != NULL) { 174 while (ci->next != NULL && (next2 = ci->next->next) != NULL) {
157 luaM_free(L, ci->next); /* free next */ 175 luaM_free(L, ci->next); /* free next */
@@ -160,7 +178,7 @@ void luaE_shrinkCI (lua_State *L) {
160 next2->previous = ci; 178 next2->previous = ci;
161 ci = next2; /* keep next's next */ 179 ci = next2; /* keep next's next */
162 } 180 }
163 L->nCcalls += L->nci; /* to subtract removed elements from 'nCcalls' */ 181 L->nCcalls += L->nci; /* adjust result */
164} 182}
165 183
166 184
@@ -250,7 +268,6 @@ static void preinit_thread (lua_State *L, global_State *g) {
250 L->allowhook = 1; 268 L->allowhook = 1;
251 resethookcount(L); 269 resethookcount(L);
252 L->openupval = NULL; 270 L->openupval = NULL;
253 L->nny = 1;
254 L->status = LUA_OK; 271 L->status = LUA_OK;
255 L->errfunc = 0; 272 L->errfunc = 0;
256} 273}
diff --git a/lstate.h b/lstate.h
index ce337707..b069b390 100644
--- a/lstate.h
+++ b/lstate.h
@@ -15,7 +15,6 @@
15 15
16 16
17/* 17/*
18
19** Some notes about garbage-collected objects: All objects in Lua must 18** Some notes about garbage-collected objects: All objects in Lua must
20** be kept somehow accessible until being freed, so all objects always 19** be kept somehow accessible until being freed, so all objects always
21** belong to one (and only one) of these lists, using field 'next' of 20** belong to one (and only one) of these lists, using field 'next' of
@@ -43,26 +42,58 @@
43** 'weak': tables with weak values to be cleared; 42** 'weak': tables with weak values to be cleared;
44** 'ephemeron': ephemeron tables with white->white entries; 43** 'ephemeron': ephemeron tables with white->white entries;
45** 'allweak': tables with weak keys and/or weak values to be cleared. 44** 'allweak': tables with weak keys and/or weak values to be cleared.
46
47*/ 45*/
48 46
49 47
50/*
51 48
49/*
52** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of 50** About 'nCcalls': each thread in Lua (a lua_State) keeps a count of
53** how many "C calls" it has in the C stack, to avoid C-stack overflow. 51** how many "C calls" it can do in the C stack, to avoid C-stack overflow.
54** This count is very rough approximation; it considers only recursive 52** This count is very rough approximation; it considers only recursive
55** functions inside the interpreter, as non-recursive calls can be 53** functions inside the interpreter, as non-recursive calls can be
56** considered using a fixed (although unknown) amount of stack space. 54** considered using a fixed (although unknown) amount of stack space.
57** 55**
56** The count itself has two parts: the lower part is the count itself;
57** the higher part counts the number of non-yieldable calls in the stack.
58**
59** Because calls to external C functions can use of unkown amount
60** of space (e.g., functions using an auxiliary buffer), calls
61** to these functions add more than one to the count.
62**
58** The proper count also includes the number of CallInfo structures 63** The proper count also includes the number of CallInfo structures
59** allocated by Lua, as a kind of "potential" calls. So, when Lua 64** allocated by Lua, as a kind of "potential" calls. So, when Lua
60** calls a function (and "consumes" one CallInfo), it needs neither to 65** calls a function (and "consumes" one CallInfo), it needs neither to
61** increment nor to check 'nCcalls', as its use of C stack is already 66** increment nor to check 'nCcalls', as its use of C stack is already
62** accounted for. 67** accounted for.
63
64*/ 68*/
65 69
70/* number of "C stack slots" used by an external C function */
71#define CSTACKCF 10
72
73/* true if this thread does not have non-yieldable calls in the stack */
74#define yieldable(L) (((L)->nCcalls & 0xffff0000) == 0)
75
76/* real number of C calls */
77#define getCcalls(L) ((L)->nCcalls & 0xffff)
78
79
80/* Increment the number of non-yieldable calls */
81#define incnny(L) ((L)->nCcalls += 0x10000)
82
83/* Decrement the number of non-yieldable calls */
84#define decnny(L) ((L)->nCcalls -= 0x10000)
85
86/* Increment the number of non-yieldable calls and nCcalls */
87#define incXCcalls(L) ((L)->nCcalls += 0x10000 + CSTACKCF)
88
89/* Decrement the number of non-yieldable calls and nCcalls */
90#define decXCcalls(L) ((L)->nCcalls -= 0x10000 + CSTACKCF)
91
92
93
94
95
96
66struct lua_longjmp; /* defined in ldo.c */ 97struct lua_longjmp; /* defined in ldo.c */
67 98
68 99
@@ -208,8 +239,9 @@ typedef struct global_State {
208*/ 239*/
209struct lua_State { 240struct lua_State {
210 CommonHeader; 241 CommonHeader;
211 unsigned short nci; /* number of items in 'ci' list */
212 lu_byte status; 242 lu_byte status;
243 lu_byte allowhook;
244 unsigned short nci; /* number of items in 'ci' list */
213 StkId top; /* first free slot in the stack */ 245 StkId top; /* first free slot in the stack */
214 global_State *l_G; 246 global_State *l_G;
215 CallInfo *ci; /* call info for current function */ 247 CallInfo *ci; /* call info for current function */
@@ -223,13 +255,11 @@ struct lua_State {
223 CallInfo base_ci; /* CallInfo for first level (C calling Lua) */ 255 CallInfo base_ci; /* CallInfo for first level (C calling Lua) */
224 volatile lua_Hook hook; 256 volatile lua_Hook hook;
225 ptrdiff_t errfunc; /* current error handling function (stack index) */ 257 ptrdiff_t errfunc; /* current error handling function (stack index) */
258 l_uint32 nCcalls; /* number of allowed nested C calls - 'nci' */
226 int stacksize; 259 int stacksize;
227 int basehookcount; 260 int basehookcount;
228 int hookcount; 261 int hookcount;
229 unsigned short nny; /* number of non-yieldable calls in stack */
230 unsigned short nCcalls; /* number of nested C calls + 'nny' */
231 l_signalT hookmask; 262 l_signalT hookmask;
232 lu_byte allowhook;
233}; 263};
234 264
235 265
@@ -283,8 +313,10 @@ LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
283LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); 313LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
284LUAI_FUNC void luaE_freeCI (lua_State *L); 314LUAI_FUNC void luaE_freeCI (lua_State *L);
285LUAI_FUNC void luaE_shrinkCI (lua_State *L); 315LUAI_FUNC void luaE_shrinkCI (lua_State *L);
286LUAI_FUNC void luaE_incCcalls (lua_State *L); 316LUAI_FUNC void luaE_enterCcall (lua_State *L);
317
287 318
319#define luaE_exitCcall(L) ((L)->nCcalls--)
288 320
289#endif 321#endif
290 322
diff --git a/ltests.h b/ltests.h
index 9d409c8d..997e1c4b 100644
--- a/ltests.h
+++ b/ltests.h
@@ -31,7 +31,7 @@
31 31
32/* compiled with -O0, Lua uses a lot of C stack space... */ 32/* compiled with -O0, Lua uses a lot of C stack space... */
33#undef LUAI_MAXCCALLS 33#undef LUAI_MAXCCALLS
34#define LUAI_MAXCCALLS 200 34#define LUAI_MAXCCALLS 400
35 35
36/* to avoid warnings, and to make sure value is really unused */ 36/* to avoid warnings, and to make sure value is really unused */
37#define UNUSED(x) (x=0, (void)(x)) 37#define UNUSED(x) (x=0, (void)(x))
diff --git a/luaconf.h b/luaconf.h
index ff708513..0fc161a4 100644
--- a/luaconf.h
+++ b/luaconf.h
@@ -695,14 +695,14 @@
695/* 695/*
696@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. 696@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system.
697** CHANGE it if it uses too much C-stack space. (For long double, 697** CHANGE it if it uses too much C-stack space. (For long double,
698** 'string.format("%.99f", -1e4932)' needs 5034 bytes, so a 698** 'string.format("%.99f", -1e4932)' needs 5052 bytes, so a
699** smaller buffer would force a memory allocation for each call to 699** smaller buffer would force a memory allocation for each call to
700** 'string.format'.) 700** 'string.format'.)
701*/ 701*/
702#if LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE 702#if LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE
703#define LUAL_BUFFERSIZE 8192 703#define LUAL_BUFFERSIZE 8192
704#else 704#else
705#define LUAL_BUFFERSIZE ((int)(0x80 * sizeof(void*) * sizeof(lua_Integer))) 705#define LUAL_BUFFERSIZE ((int)(16 * sizeof(void*) * sizeof(lua_Number)))
706#endif 706#endif
707 707
708/* 708/*
diff --git a/testes/all.lua b/testes/all.lua
index 26d24976..84ba80a6 100644
--- a/testes/all.lua
+++ b/testes/all.lua
@@ -172,6 +172,7 @@ if not _G._soft then
172 assert(f() == 'b') 172 assert(f() == 'b')
173 assert(f() == 'a') 173 assert(f() == 'a')
174end 174end
175dofile('cstack.lua')
175dofile('nextvar.lua') 176dofile('nextvar.lua')
176dofile('pm.lua') 177dofile('pm.lua')
177dofile('utf8.lua') 178dofile('utf8.lua')
diff --git a/testes/coroutine.lua b/testes/coroutine.lua
index 5674a4dd..ca30011f 100644
--- a/testes/coroutine.lua
+++ b/testes/coroutine.lua
@@ -107,7 +107,7 @@ function filter (p, g)
107 end) 107 end)
108end 108end
109 109
110local x = gen(100) 110local x = gen(80)
111local a = {} 111local a = {}
112while 1 do 112while 1 do
113 local n = x() 113 local n = x()
@@ -116,7 +116,7 @@ while 1 do
116 x = filter(n, x) 116 x = filter(n, x)
117end 117end
118 118
119assert(#a == 25 and a[#a] == 97) 119assert(#a == 22 and a[#a] == 79)
120x, a = nil 120x, a = nil
121 121
122 122
diff --git a/testes/cstack.lua b/testes/cstack.lua
new file mode 100644
index 00000000..9e5bbaef
--- /dev/null
+++ b/testes/cstack.lua
@@ -0,0 +1,62 @@
1-- $Id: testes/cstack.lua $
2-- See Copyright Notice in file all.lua
3
4print"testing C-stack overflow detection"
5
6-- Segmentation faults in these tests probably result from a C-stack
7-- overflow. To avoid these errors, recompile Lua with a smaller
8-- value for the constant 'LUAI_MAXCCALLS' or else ensure a larger
9-- stack for the program.
10
11local function checkerror (msg, f, ...)
12 local s, err = pcall(f, ...)
13 assert(not s and string.find(err, msg))
14end
15
16
17do -- simple recursion
18 local count = 0
19 local function foo ()
20 count = count + 1
21 foo()
22 end
23 checkerror("stack overflow", foo)
24 print(" maximum recursion: " .. count)
25end
26
27
28-- bug since 2.5 (C-stack overflow in recursion inside pattern matching)
29do
30 local function f (size)
31 local s = string.rep("a", size)
32 local p = string.rep(".?", size)
33 return string.match(s, p)
34 end
35 local m = f(80)
36 assert(#m == 80)
37 checkerror("too complex", f, 200000)
38end
39
40
41-- testing stack-overflow in recursive 'gsub'
42do
43 local count = 0
44 local function foo ()
45 count = count + 1
46 string.gsub("a", ".", foo)
47 end
48 checkerror("stack overflow", foo)
49 print(" maximum 'gsub' nest (calls): " .. count)
50
51 -- can be done with metamethods, too
52 count = 0
53 local t = setmetatable({}, {__index = foo})
54 foo = function ()
55 count = count + 1
56 string.gsub("a", ".", t)
57 end
58 checkerror("stack overflow", foo)
59 print(" maximum 'gsub' nest (metamethods): " .. count)
60end
61
62print'OK'
diff --git a/testes/pm.lua b/testes/pm.lua
index cdcf3bec..1afaccf6 100644
--- a/testes/pm.lua
+++ b/testes/pm.lua
@@ -237,18 +237,6 @@ checkerror("invalid capture index %%0", string.gsub, "alo", "(%0)", "a")
237checkerror("invalid capture index %%1", string.gsub, "alo", "(%1)", "a") 237checkerror("invalid capture index %%1", string.gsub, "alo", "(%1)", "a")
238checkerror("invalid use of '%%'", string.gsub, "alo", ".", "%x") 238checkerror("invalid use of '%%'", string.gsub, "alo", ".", "%x")
239 239
240-- bug since 2.5 (C-stack overflow)
241do
242 local function f (size)
243 local s = string.rep("a", size)
244 local p = string.rep(".?", size)
245 return pcall(string.match, s, p)
246 end
247 local r, m = f(80)
248 assert(r and #m == 80)
249 r, m = f(200000)
250 assert(not r and string.find(m, "too complex"))
251end
252 240
253if not _soft then 241if not _soft then
254 print("big strings") 242 print("big strings")