diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-12-27 14:32:29 -0200 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-12-27 14:32:29 -0200 |
commit | ba7da13ec5938f978c37d63aa40a3e340b301f79 (patch) | |
tree | c1f22403954f6e0c6d17c8495c11509103313c9a | |
parent | da37ac9c7894186a0e2e0e6f1f5f00b825fd1555 (diff) | |
download | lua-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.c | 4 | ||||
-rw-r--r-- | ldo.c | 40 | ||||
-rw-r--r-- | llimits.h | 6 | ||||
-rw-r--r-- | lparser.c | 8 | ||||
-rw-r--r-- | lstate.c | 49 | ||||
-rw-r--r-- | lstate.h | 52 | ||||
-rw-r--r-- | ltests.h | 2 | ||||
-rw-r--r-- | luaconf.h | 4 | ||||
-rw-r--r-- | testes/all.lua | 1 | ||||
-rw-r--r-- | testes/coroutine.lua | 4 | ||||
-rw-r--r-- | testes/cstack.lua | 62 | ||||
-rw-r--r-- | testes/pm.lua | 12 |
12 files changed, 170 insertions, 74 deletions
@@ -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 | } |
@@ -138,7 +138,7 @@ l_noret luaD_throw (lua_State *L, int errcode) { | |||
138 | 138 | ||
139 | 139 | ||
140 | int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { | 140 | 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; |
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 | */ |
518 | void luaD_callnoyield (lua_State *L, StkId func, int nResults) { | 521 | 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); | ||
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) { | |||
664 | LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs, | 661 | LUA_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 | ||
707 | LUA_API int lua_isyieldable (lua_State *L) { | 701 | LUA_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 | */ |
747 | int luaD_pcall (lua_State *L, Pfunc func, void *u, | 741 | int 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 | ||
@@ -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 |
187 | typedef unsigned int Instruction; | 187 | typedef unsigned int l_uint32; |
188 | #else | 188 | #else |
189 | typedef unsigned long Instruction; | 189 | typedef unsigned long l_uint32; |
190 | #endif | 190 | #endif |
191 | 191 | ||
192 | typedef l_uint32 Instruction; | ||
193 | |||
192 | 194 | ||
193 | 195 | ||
194 | /* | 196 | /* |
@@ -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 | /* |
@@ -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 | */ |
107 | void luaE_incCcalls (lua_State *L) { | 115 | void 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 | ||
117 | CallInfo *luaE_extendCI (lua_State *L) { | 134 | CallInfo *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) { | |||
151 | void luaE_shrinkCI (lua_State *L) { | 169 | void 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 | } |
@@ -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 | |||
66 | struct lua_longjmp; /* defined in ldo.c */ | 97 | struct lua_longjmp; /* defined in ldo.c */ |
67 | 98 | ||
68 | 99 | ||
@@ -208,8 +239,9 @@ typedef struct global_State { | |||
208 | */ | 239 | */ |
209 | struct lua_State { | 240 | struct 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); | |||
283 | LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); | 313 | LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L); |
284 | LUAI_FUNC void luaE_freeCI (lua_State *L); | 314 | LUAI_FUNC void luaE_freeCI (lua_State *L); |
285 | LUAI_FUNC void luaE_shrinkCI (lua_State *L); | 315 | LUAI_FUNC void luaE_shrinkCI (lua_State *L); |
286 | LUAI_FUNC void luaE_incCcalls (lua_State *L); | 316 | LUAI_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 | ||
@@ -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)) |
@@ -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') |
174 | end | 174 | end |
175 | dofile('cstack.lua') | ||
175 | dofile('nextvar.lua') | 176 | dofile('nextvar.lua') |
176 | dofile('pm.lua') | 177 | dofile('pm.lua') |
177 | dofile('utf8.lua') | 178 | dofile('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) |
108 | end | 108 | end |
109 | 109 | ||
110 | local x = gen(100) | 110 | local x = gen(80) |
111 | local a = {} | 111 | local a = {} |
112 | while 1 do | 112 | while 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) |
117 | end | 117 | end |
118 | 118 | ||
119 | assert(#a == 25 and a[#a] == 97) | 119 | assert(#a == 22 and a[#a] == 79) |
120 | x, a = nil | 120 | x, 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 | |||
4 | print"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 | |||
11 | local function checkerror (msg, f, ...) | ||
12 | local s, err = pcall(f, ...) | ||
13 | assert(not s and string.find(err, msg)) | ||
14 | end | ||
15 | |||
16 | |||
17 | do -- 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) | ||
25 | end | ||
26 | |||
27 | |||
28 | -- bug since 2.5 (C-stack overflow in recursion inside pattern matching) | ||
29 | do | ||
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) | ||
38 | end | ||
39 | |||
40 | |||
41 | -- testing stack-overflow in recursive 'gsub' | ||
42 | do | ||
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) | ||
60 | end | ||
61 | |||
62 | print'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") | |||
237 | checkerror("invalid capture index %%1", string.gsub, "alo", "(%1)", "a") | 237 | checkerror("invalid capture index %%1", string.gsub, "alo", "(%1)", "a") |
238 | checkerror("invalid use of '%%'", string.gsub, "alo", ".", "%x") | 238 | checkerror("invalid use of '%%'", string.gsub, "alo", ".", "%x") |
239 | 239 | ||
240 | -- bug since 2.5 (C-stack overflow) | ||
241 | do | ||
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")) | ||
251 | end | ||
252 | 240 | ||
253 | if not _soft then | 241 | if not _soft then |
254 | print("big strings") | 242 | print("big strings") |