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") |
